Sometimes it’s easiest to learn something new simply by using it, and to my mind PowerShell is no exception. Often we discover new capabilities and features in looking at what tasks other people are accomplishing using PowerShell, and specifically, looking at how they’re using the scripting language.
In this scriptshow, I take five common tasks and show how to accomplish them using PowerShell. The tasks are:
- Adding a user
- Deleting a specific attachment (like one that contained in a virus or malware payload) from a set of Exchange mailboxes
- Handling the mailing-list deletion of employees who are leaving the company for any reason
- Working with CSV files within PowerShell
- Connecting to certain Microsoft cloud services from your on-premises servers
I provide the cmdlets or a script, and then walk you through how I put the cmdlets or scripts together so that you can see the logic of why the scripts work the way they do. You can use these as a launchpad of sorts for further customization or for creating your own daily administrative task scripts, whatever you would find useful. I hope this gives you a real taste of the practical applicability that the PowerShell scripting language can bring to your IT life.
With that said, let us get on with it!
1. Adding users
Have you ever had a batch of users you needed to create accounts for, but you did not want to page through the wizards in Active Directory Users and Computers? This kind of rote, repetitive task is exactly what Windows PowerShell is designed to handle.
Import-Module ActiveDirectory
Import-Csv "C:\powershell\users.csv" | ForEach-Object {
$userPrincipal = $_."samAccountName" + "@yourdomain.local"
New-ADUser -Name $_.Name
-Path $_."ParentOU"
-SamAccountName $_."samAccountName"
-UserPrincipalName $userPrincipal
-AccountPassword (ConvertTo-SecureString "cheeseburgers4all"
-AsPlainText -Force)
-ChangePasswordAtLogon $true
-Enabled $true
Add-ADGroupMember "Office Users"
$_."samAccountName";
}
In this script, we use the Import-CSV cmdlet, which knows how to read .CSV-formatted files. We tell the Import-CSV cmdlet that each row of the CSV data located in C:\powershell called users.csv contains information in three columns: The Name of the user; the samAccountName of the user, which is basically the login ID for the user; and the organizational unit (OU) of Active Directory that the user needs to live in.
We're also telling the cmdlet that we are using the column samAccount Name to create the login ID for the user by marrying the value that lives in that column with the string “@yourdomain.local” to complete the user principal name (UPN).
From there, we loop through the file using ForEach-Object and send that assembled string (which is stored in the PowerShell variable called $userPrincipal). We assign the default password to each user as “cheeseburgers4all” and then set the Active Directory flag to require the user to change the password at first logon. At the end of the script, we then add all of these accounts to the Active Directory security group called Office Users.
2. Deleting dangerous or objectionable content from Exchange mailboxes
I was inspired by PowerShell MVP Mike Robbins’ post on removing phishing messages from Exchange mailboxes. In this day and age I think Cryptolocker and CryptoWall ransomware infections are much more nefarious than phishing. The most recent infections go after network drives and are not well picked up and covered by client anti-malware solutions, so if you are not careful you could well pick up an infection.
For this reason, when you see a suspect message, you might want to just get it out of any mailbox that it is located in -- a kind of mass deletion, if you will. If you are running Exchange 2010 or later, you can take care of that from within a PowerShell window.
Add-PSSnapin -Name
Microsoft.Exchange.Management.PowerShell.E2010
Get-Mailbox -ResultSize Unlimited |
Search-Mailbox -SearchQuery 'Subject:"*Please review the attached invoice*"' -DeleteContent |
Where-Object {$_.ResultItemsCount}
In this script, we add the Exchange tools to our PowerShell window and then put two cmdlets together. The first one is a generic Get-Mailbox cmdlet and we also let PowerShell know that we are targeting all of the mailboxes on the system, so we tell it to give us an unlimited result size.
The second cmdlet searches the content within the mailbox and searches the subject field of every message inside each mailbox for the string we provide in the cmdlet parameter. In this case, “Please review the attached invoice” is actually the subject line of a Cryptolocker infection message I just received as I was writing this. The –DeleteContent eliminates the message, and the Where-Object controls the display of the results within the console window.
Before you do this, you might consider adding the –whatif flag to this transaction so that you can see the impact of the cmdlet’s intended deletion across your entire deployment. Also consider the performance implications: PowerShell searching this way is not, as we would say in the South, too terribly efficient, so for a large organization with tens of thousands of mailboxes, you can expect this operation to consume a fair amount of resources for a while.
3. Elegantly handling departed employees and their distribution list memberships
It happens in every organization: Employees leave. They are terminated, they leave voluntarily, they get another job, they retire. Whatever the reason, you need to deal with their accounts. If your organization is like many others, users wind up embedded in tons of distribution lists per department, per project, per location and so on.
We often find departed employee accounts still around, just without any rights or security group memberships. Most identity-lifecycle best practices suggest you should not simply delete accounts when employees leave; often, their mailboxes live on as shared resources for the remaining employees who might need to unlock some data stored within them.
However, these mailboxes can quickly fill up with distribution list messages that are completely unnecessary. So how do you keep a mailbox active but find all of its various distribution list memberships and unsubscribe from them? That’s where this set of cmdlets comes in.
New-DistributionGroup –Name “Sayonara” –OrganizationalUnit “yourdomain.local” –SamAccountName “Sayonara” –Type “Security” Import-CSV separatedemployees.csv | ForEach {Add-DistributionGroupMember -Identity "Sayonara" -Member $_.Name}
$groupstounsubscribe=get-distributiongroup -filter {DisplayName -ne "Sayonara"}
Get-DistributionGroupMember Sayonara | remove-distributiongroupmember $groupstounsubscribe
First, we create a new distribution group called Sayonara, the members of which will be the accounts of departed employees. We will then procure a CSV file from human resources that lists their user principal names. We will feed that file into PowerShell, again using the Import-CSV cmdlet, and then say that for every entry (row) in that CSV file, we should add that login ID to the distribution group called Sayonara.
After this, we initialize a variable called groupstounsubscribe. To populate this variable we ask PowerShell to get a list of all Exchange distribution groups, and then filter it down to only those in which the name is not equal to Sayonara. In other words, the lists stored in this variable will be all lists except our new Sayonara list.
In the final step of this set of cmdlets, we ask PowerShell to grab all of the names within the distribution group Sayonara -- these are the ones we want to remove from the other groups -- and then pipe that list into the remove-distributiongroupmember cmdlet using the list of groups (except Sayonara) to compare against.
What have we accomplished? All the accounts that are a member of Sayonara will get removed from any distribution group that is NOT Sayonara. So the only new mail a departed employee account’s mailbox will receive is mail addressed directly to that mailbox. A neat and tidy solution.
(Hat tip to this post by David Shackelford for the inspiration.)
4. Create a new comma separated values (.CSV) file and populate it with data
This script is fairly simple but it has a number of interesting implications and is very easy to modify for your specific scenarios. We have used the Import-CSV cmdlet a couple of times in this scriptshow already, but I want to show that PowerShell can also write to CSV files as well, which is really useful to get data out of a system, play around with it in Excel and then re-import it into another cmdlet later.
Get-Mailbox | Select-Object
Name,OrganizationalUnit,WindowsEmailAddress | Export-CSV
C:\powershell\export.csv
In this case, what we are doing is using the Exchange Get-Mailbox cmdlet to get a list of all mailboxes on a deployment. We will pipe that output to the Select-Object cmdlet, which grabs specific parts of whatever it is sent; in this case we are getting the name, organizational unit and default email address properties of each mailbox. And then we are piping just those properties over to the Export-CSV cmdlet, which will write them conveniently to the CSV file at the directory path I included above.
If you are wondering how you can easily grab all of the properties you can use within a CSV, just use a get cmdlet and format the output as a list. For example, “get-mailbox jhassell | fl” will show you all of the different properties you can use with the Select-Object cmdlet in the example above to populate the columns in your CSV file.
5. Easily connect to Exchange Online or Office 365 from your hybrid deployment
If you are running a hybrid Exchange deployment, chances are you are connecting up to the Office 365 portal a lot. If you’ve tried to do a lot of administrative work with PowerShell in this scenario, you know it is a bit of a rigmarole to set up the remoting necessary to run PowerShell cmdlets against the Office 365 servers. Below, I’ve created a script that takes care of the setup for you, so that when you are ready to go, you just run the script and enter your Office 365 administrative credentials.
$URL = "https://ps.outlook.com/powershell"
$Credentials = Get-Credential -Message "Enter your Exchange Online or Office 365 administrator credentials"
$CloudSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $URL -Credential $Credentials -Authentication Basic -AllowRedirection -Name "Office 365/Exchange Online"
Import-PSSession $CloudSession –Prefix “365”
First off, we declare a variable to store the location on the Internet where we’re sending all of these cmdlets -- think of it like a Web service. Then, we set up a variable to securely hold our username and password. The Get-Credential cmdlet pops up a window where you can enter credentials, and the variable will hold those credentials as secure strings. The third variable starts a new PowerShell remoting session using the specific remoting language necessary to connect up to Office 365 or Exchange Online (this works for both offerings). Finally, the Import-PSSession merges that session with your current console, letting you work directly within it.
This particular script is specific to hybrid deployments because sometimes namespaces for cmdlets collide. PowerShell does not always know immediately how to sort out -- say, if you ran New-Mailbox -- whether you wanted to create that new mailbox on your local deployment or up in the cloud.
To fix this, this script loads the Office 365 namespace of cmdlets with the prefix 365. So all Exchange cmdlets that should run in the cloud should use the 365 prefix, a la New-365Mailbox or Get-365DistributionGroup. All Exchange cmdlets that should run on your local deployment should be left as they are by default. This makes it very easy to distinguish one from the other.
If you want to run this script in a purely cloud environment, however, you can just remove the prefix “365” from the last line of the script and everything will return to its default.
Remember, to save this as a script, just put the cmdlets above into a text file and then save the file with an extension of .PS1. Then, from the PowerShell console window, type in .\script.ps1 (that’s period, backslash, name of file) to run the script.