Archive | PowerShell

PowerShell code to split a space delimited string – with double spaces

I am working on a PowerShell wrapper script to run multiple commands on multiple machines. The first thing I wanted to do was to chop up a space or comma delimited argument. (If you make an argument mandatory, and the user does not provide it, PowerShell will prompt you for it – and when prompted you can use space delimited values).

I was going to use the split() function, but I did not know how to handle “multiple spaces”. For example:

("123  4 5678").Split() = 
123
 
 
4

5678"

I would end up with “empty” array members. I found this post, and they guy used “| where-object {$_ -ne ”“}” to filter out the empty values. But then I stumbled upon a better way to do it, using the built in “StringSplitOptions” of  RemoveEmptyEntries.

("123  4 5678").split(" ",[StringSplitOptions]'RemoveEmptyEntries')

would result in:

123
4
5678

If you don’t know if you are going to have space or comma delimited then I used:

$Argument.split(" ",[StringSplitOptions]'RemoveEmptyEntries') | foreach {
$_.split(",",[StringSplitOptions]'RemoveEmptyEntries')}

PowerShell wrapper scripts to find locked accounts and prompt to unlock

I wanted a quick way to find if an account is locked out (you get the call “I can’t log in”) and unlock it. I had a wrapper script that just called “Search-ADAccount –LockedOut” but I took it a bit further. The first of these two wrapper scripts/functions gets all the accounts that are  locked out, then it asks if you want to unlock the account – if yes, it calls the second unlock function. If no, then it loops to the next locked account.

function JBMURPHY-AD-GetLockedOut {
	Search-ADAccount –LockedOut | foreach-object {
	$UserName=$_.Name
	$SamAccountName=$_.SamAccountName
	write-host "`n$UserName is locked out`n"
	$message = "Do you want to unlock $UserName"
	$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes nnlock $UserName"
	$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","No, don't unlock $UserName"
	$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
	$result = $host.ui.PromptForChoice($title, $message, $options, 0)
	if ($result -eq 0){
		JBMURPHY-AD-UnlockAccount -UserName $SamAccountName
	}
	}
}

The second function is a simple script that wraps the unlock-adaccount function:

function JBMURPHY-AD-UnlockAccount {
	Param([parameter(Mandatory = $true)]$UserName)
	Unlock-ADAccount -Identity $UserName
}

Start Visual Studio form PowerShell

I am moving on to a new project – our migration from SharePoint 2007 to 2010. First thing I wanted to do was to upgrade my Solutions/Features from 2007 to 2010. I installed Visual Studio and started looking at how to recreate my Delegate JQuery Control (more on that later). The first thing I found annoying was that every time I added an “Empty SharePoint Project”, I received a warning “This task requires the application to have elevated permissions”. That is annoying. Since I usually leave a PowerShell prompt running as administrator (It has a red background to distinguish it), I wanted to quickly open VisualStudio from that PowerShell prompt. Here is my wrapper script to open VisualStudio 2010 from PowerShell:

function JBMURPHY-Start-VisualStudio {
Start-Process "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe" -workingdirectory "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\"
}

PowerShell wrapper script to send email

I wanted a standard function that I can call from other scripts to send email. I needed multiple recipients, and default sender and smtp arguments. Here is the script that I came up with.

function JBMURPHY-Send-Email {
Param(	[parameter(Mandatory = $true)]$ToAddress,
	[parameter(Mandatory = $true)]$Subject,
	[parameter(Mandatory = $true)]$Body,
	[parameter(Mandatory = $false)]$FromAddress="[email protected]",
	[parameter(Mandatory = $false)]$SMTPAddress="192.168.1.1")
$msg = New-Object Net.Mail.MailMessage
$msg.From = $FromAddress
$msg.Body = $Body
$msg.Subject = $Subject
if($ToAddress -isnot [Object[]]) {$ToAddresses = ([string]$ToAddress).Split(";")}
foreach($Address in $ToAddresses) { $msg.To.Add($Address)}
$smtp = new-object Net.Mail.SmtpClient($SMTPAddress)
$smtp.Send($msg)
}

PowerShell script to add users to a group

In this previous post : PowerShell wrapper for creating a new distribution group, I created a script for creating a new distribution group. I wanted to take that a step further and prompt the SysAdmin to add users. I created a new recursive function called AddToDistributionGroup. In this code, I prompt for a group name, and a user to add. The SysAdmin types in the first few parts of the name (I could have used samaccountname) and then I then loop through ADusers with that name asking the SysAdmin if that is the user they want to add.

function JBMURPHY-EXCHANGE-AddToDistributionGroup {
Param(	[parameter(Mandatory = $true)]$GroupName,
	[parameter(Mandatory = $true)]$UserToAdd)
JBM-EXCHANGE-StartPSSESSION
if (!($GroupName)) {write-host "you need to specify a group name"
break}
if (($UserToAdd)) {
 $UserToAdd=$UserToAdd+"*"
 Get-aduser -filter {(name -like $UserToAdd) -and (Enabled -eq $true)} | foreach-object {
  $UserName=$_.Name
  $message = "Add $UserName to the group: $GroupName"
  $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes add $UserName to the group $GroupName"
  $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","No, don't add $UserName to the group $GroupName?"
  $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
  $result = $host.ui.PromptForChoice($title, $message, $options, 0) 
  if ($result -eq 0){
   write-host "Adding $UserName"
   Add-DistributionGroupMember -Identity $GroupName -Member $UserName
  }
 }
JBM-EXCHANGE-AddToDistributionGroup $GroupName
}
}

* Note, there is not any error checking to see if the group exists. I am mainly using this code to be called from a NewDistributionGroup script, where I know the name of the group. I may add a lookup to see if the group exists at some point.

** Now that I think about it, this is for any type group, not just distribution groups.

PowerShell wrapper for creating a new distribution group

Unknown to me, in Exchange 2010 when you create a new distribution group in EMC, by default, the group will not receive email from external recipients – the setting “Require that senders are authenticated” is checked. We use distribution groups to communicate with clients, so unauthenticated senders need to email these groups.  This setting is on the Mail Flow Setting – Message Delivery restrictions page.  I wrote a simple wrapper script to create a new distribution group and turn off the “Require that senders are authenticated” setting:

function JBMURPHY-EXCHANGE-NewDistributionGroup {
Param([parameter(Mandatory = $true)]$GroupName)
Write-host "Creating group named $GroupName"
new-DistributionGroup -Name $GroupName -OrganizationalUnit 'site.name/OUName' -SamAccountName $GroupName -Alias $GroupName
Set-DistributionGroup $GroupName -RequireSenderAuthenticationEnabled $false
write-host "The $GroupName distribution group has been created."
}

Simple one. I know.

PowerShell wrapper scripts for Exchange 2010 – first step: make a connection

As I talked about in this previous post, I like to write wrapper scripts that collect input and pass it along to the actual provided functions. I call these wrapper scripts because they are not really doing anything ground breaking, they are just a series of conditionals and commands that I put together, with a common naming convention. Then, all we have to do is tab completion through the scripts that I have written.

I wanted to do the same for creating new distribution groups in Exchange 2010, but first ,I needed to make  the Exchange 2010 PowerShell functions available on our local machines. I wrote the following function that starts a PSSession on the exchange server. This function will be called at the beginning of every Exchange wrapper script, guaranteeing that we have a connection to the Exchange PowerShell functions.

Here is that function:

Function JBMURPHY-EXCHANGE-StartPSSESSION {
if(! (Get-PSSession | Where-Object { $_.ComputerName -like "servername.company.com" })){
Write-Host "Createing PSSession to SVNYEXCH01.SARDVERB.LOCAL" -ForegroundColor Green
Import-PSSession (New-PSSession -Configurationname Microsoft.Exchange –ConnectionUri http://servername.company.com/powershell) | out-null
}
}

PowerShell script to email users if password expires soon, and send a summary to IT

I wanted to expand on my previous script: powershell-to-list-all-users-and-when-their-password-expires, so that it would send the user an email if their password was going to expire soon. Additionally I wanted to send a summary to our IT staff of accounts that were going to expire soon.

Here is that script:

$maxdays=(Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.TotalDays
$summarybody="Name `t ExpireDate `t DaysToExpire `n"

(Get-ADUser -filter {(Description -notlike "IfYouWantToExclude*") -and (Enabled -eq "True") -and (PasswordNeverExpires -eq "False")} -properties *) | Sort-Object pwdLastSet |
foreach-object {

$lastset=Get-Date([System.DateTime]::FromFileTimeUtc($_.pwdLastSet))
$expires=$lastset.AddDays($maxdays).ToShortDateString()
$daystoexpire=[math]::round((New-TimeSpan -Start $(Get-Date) -End $expires).TotalDays)
$samname=$_.samaccountname
$firstname=$_.GivenName
if ($daystoexpire -le 3){
	$ThereAreExpiring=$true

	$emailFrom = "[email protected]"
	$emailTo = "[email protected]"
	$subject = "$firstname, your password expires in $daystoexpire day(s)"
	$body = "$firstname,
	Your password expires in $daystoexpire day(s).

	Please press Ctrl + Alt + Del -> Change password"

	$smtpServer = "smtp.yourdomain.com"
	$smtp = new-object Net.Mail.SmtpClient($smtpServer)
	$smtp.Send($emailFrom, $emailTo, $subject, $body)

	$summarybody += "$samname `t $expires `t $daystoexpire `n"
}
}
if ($ThereAreExpiring) {
$emailFrom = "[email protected]"
$emailTo = "[email protected]"
$subject = "Expiring passwords"
$body = $summarybody
$smtpServer = "smtp.yourdomain.com"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($emailFrom, $emailTo, $subject, $body)
}

PowerShell Active Directory wrapper scripts.

In my PowerShell profile I have a routine the sources all the *.ps1 files in a folder. One of these .ps1 files contains Active Directory “wrapper” functions. I call them “wrapper” functions because they simplify the parameters needed to return data in a format we want. The idea is that I can write a series of “wrapper” scripts, alias them, and then everyone on our team can use the same script. All a user has to do is type “jbmurphy” (if that is the script starts with) and hit TAB  and the command completion will cycle through all the wrapper scripts. Some of the scripts are not much different than the actual “wrapped” script, but there usually is simplified formatting to just dump the needed info. Here are two wrapper scripts to interact with ActiveDirectory (AD).

First one finds users in a group:

function jbmurphy-GetGroupMembers {
    Param($GroupName)
    return Get-ADGroupMember $GroupName | ft -hidetableheaders name
}

Second one finds the groups a users is a member of:

function jbmurphy-AD-GetGroupMembership {
Param($UserName)
   $user=Get-ADUser -properties memberof $UserName
   return $user.MemberOf -replace '^cn=([^,]+).+$','$1'
}

PowerShell script to silently install RSAT 2008 R2 SP1

I often re-image my machine – that way I know my SCCM OSD is working properly and has the most recent “secondary apps” (Reader,Flash,FireFox . . .). Because I am always re-imaging, I like to automate installing the software I always need like Remote Server Administration Tools – RSAT. I wanted a function to install the software and automatically add all the features. Below is that script:

function Install-RSAT {
$ScriptPath = "\\server\sahre\path\to\RAST\installers\"
Set-Location $ScriptPath

$os=(Get-WMIObject win32_operatingsystem).OSArchitecture
$sp=(Get-WmiObject Win32_OperatingSystem).CSDVersion
if (($os -eq "64-bit") -and ($sp -eq "")) {$files = get-childitem -recurse -filter amd64*.msu}
if (($os -eq "32-bit") -and ($sp -eq "")) {$files = get-childitem -recurse -filter x86*.msu}
if (($os -eq "64-bit") -and ($sp -eq "Service Pack 1")) {$files = get-childitem -recurse -filter *KB958830*amd64*.msu}
if (($os -eq "32-bit") -and ($sp -eq "Service Pack 1")) {$files = get-childitem -recurse -filter *KB958830*x86*.msu}

foreach ($file in $files)
{
Start-Process -wait -FilePath "C:\WINDOWS\SYSTEM32\wusa.exe" -ArgumentList "$file.FullName /quiet /norestart"
}

DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-SnapIns /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-AdministrativeCenter /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-DS-NIS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-LDS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-AD-Powershell /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-ServerManager /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices-CA /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-CertificateServices-OnlineResponder /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-DHCP /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-DNS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-Dfs /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-Fsrm /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-FileServices-StorageMgmt /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-HyperV /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Roles-RDS /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-BitLocker /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-Clustering /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-GP /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-LoadBalancing /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-SmtpServer /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-StorageExplorer /Quiet /NoRestart
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-StorageManager
DISM /online /Enable-Feature /FeatureName:RemoteServerAdministrationTools-Features-Wsrm
}