Friday 3 May 2013

Adding Multiple Users to 'Accept Messages Only From' - Exchange 2007

Exchange 2007 has a feature that allows restrictions to be placed on a distribution group (I call them "distribution lists", or "DL's") that control who is allowed/permitted to send messages to them.

The Exchange Management Console enables you to search and add individual users to this list, but I found the process can be very slow and tedious, especially if you want to add a large number of users to this list, or want to add users to multiple DL's.

For this reason, I created the below script. Others who may have tried this using a simple "Set-DistributionGroup" command would have found that every time they tried to add a user it replaced any users who were already in the list. This script queries the already existing users and appends/adds the new user onto the already existing list. You will need to provide a list of users either within the script, or by importing a simple .txt file with a list of users you wish to add. For this example, I will use a .txt file.

The .txt file contains a simple list of users with a single name on each line;

firstname.lastname@domain.com
test.user1@domain.com
example.user2@domain.com

And here is the script:

#Name of Distribution Group/List to add user(s) to
$TargetDL = "DL_Test"

#Domain Controller Name - must be used to ensure each user is appended and not replaced. 
$DC = "domaincontrollername"

#Import List of Users to add from .txt file
$UserListFile = "C:\UserList.txt"
$UserList = Get-Content $UserListFile

#Cycle through list of users and add them to 'Accept Messages Only From' list for DL
ForEach ($User in $UserList)
     {
     Set-DistributionGroup "$TargetDL" -AcceptMessagesOnlyFrom ((Get-DistributionGroup -DomainController "$DC" -identity "$TargetDL").AcceptMessagesOnlyFrom + "$User") -DomainController "$DC"
     Write-Host "$User granted permission to send to $TargetDL"
     }


Replace the $TargetDL, $DC, and $UserListFile variables with the relevant information for your Exchange environment

Friday 8 February 2013

Querying Exchange Mailboxes with Powershell Pt 2

In my previous blog post, I outlined some methods to query Exchange mailboxes using the get-mailbox cmdlet. Whilst the information obtained using this cmdlet is very useful, it does not contain all the information that is available for an Exchange mailbox.

There is another cmdlet available called Get-MailboxStatistics. It is used/applied in the same way as the Get-Mailbox cmdlet, but contains different fields or properties, and therefore, different information relating to the mailbox.

If you run the command Get-MailboxStatistics -identity "MailboxName" | fl it will display the mailbox statistics for that particular mailbox.

This information includes;

  • Associated item count (ie. number of items within the mailbox)
  • Deleted item count
  • Last logon/logoff time
  • Total size of items
  • Total size of deleted items
So, for a practical example that you could potentially use to query this information. Let's say you want to query all your mailboxes to identify those that have more than 20,000 items within them. You could run the following command;

Get-Mailboxstatistics | Where {$_.ItemCount -gt "20000"} | Select DisplayName

You could replace $_.ItemCount with any of the other properties that are contained within the Get-MailboxStatistics cmdlet.

If you want the query to return more than just the DisplayName field for objects that match your query, you can add them after DisplayName and separate them with a comma (,), or if you want to return all properties then remove | Select DisplayName altogether.

You can also add | Export-CSV-path "C:\Export.csv" -notype to the end to export your query results to a .csv file.

Tuesday 5 February 2013

Querying Exchange Mailboxes with Powershell

Exchange and Powershell go hand in hand - as it is essentially what Exchange is built on top of, and is what the Exchange Management Console (EMC) uses to execute commands you choose through the GUI. (Ever noticed you get the Powershell command line equivalent for commands you execute in EMC?)

One of the most common things an administrator will need to do is query all the mailboxes within the system for certain criteria, which I will explain and breakdown in this blog entry.

The command Get-Mailbox is one of the most useful and powerful commands you can use (for information gethering). It can be used to retrieve all the properties for all the mailboxes in your system, or can be tweaked to only return some of the properties for some of the mailboxes within your system.

If you execute the command Get-Mailbox on its own, you will probably be flooded with 1000 responses, followed by the following warning message;


WARNING: By default only the first 1000 items are returned. To change the number of items returned, specify the parameter "-ResultSize". To return all items specify "-ResultSize Unlimited" (Note: Returning all items may take a 
very long time and consume a large amount of memory depending on the actual number of items). It is not recommended to store the results in a variable; instead pipe the results to another task or script to perform batch changes

As per the message, the command on its own will only return the first 1000 results. You can change this by adding -ResultSize Unlimited to it, so it would look like this;

Get-Mailbox -ResultSize Unlimited

If you run this command it will run through and display the first few properties for every mailbox within your system. While this can be useful, the command can be further tweaked to run faster and provide more accurate/informative results.

To get all the properties for a single users mailbox, you can use the following command

Get-Mailbox -Identity "Morrissey, Peter"

You can replace what is between the " " with any identifier for the mailbox - such as Primary SMTP Address, Display Name or Alias.

When you run this command it will only display the first few properties as it is presenting the information in a table format. If you change the command to display in list format (by adding | fl), you get MUCH more information:

Get-Mailbox -Identity "Morrissey, Peter" | fl

Now you can view ALL the properties for an individual users mailbox. These properties apply to every mailbox within the system, so you can once again, modify the Get-Mailbox command to search on any of the properties you see in this list.

For example, if I wanted to query all the mailboxes within my system, and only return those whose 'Name' property contain the text "Peter", I would run the following command

Get-Mailbox -ResultSize Unlimited | Where {$_.Name -like "*Peter*"}

You still need to include -Resultsize Unlimited because although you aren't expecting more than 1000 results to be returned, you do want the Get-Mailbox command to query more than the first 1000 mailboxes, which it will not do if this omitted.

You can replace the $_.Name with any of the other properties for a mailbox - it just needs to have the $_. before it.

The -like operator can also be changed to use -eq (equal to) -ne (not equal to) -gt (greater than) -lt (less than) -le (less than or equal to) -ge (greater than or equal to)

Next you can fine tune the properties that you wish to display for each object whos name contains "Peter". Say you want to only display the DisplayName, Alias, Database and PrimarySMTPAddress properties for each of the results. You could use the following line

Get-Mailbox -ResultSize Unlimited | Where {$_.Name -like "*Peter*"} | Select DisplayName, Alias, Database, PrimarySMTPAddress

The benefit of doing this is that by retrieving fewer properties for each object, it speeds up the time it takes for the query to run. It also makes looking through the results much faster as you aren't reading over information you don't need. Once again, you can replace the properties I used with any of the properties available for each mailbox, as we discovered earlier.

Finally, you  can export the results of your query to a CSV file

Get-Mailbox -ResultSize Unlimited | Where {$_.Name -like "*Peter*"} | Select DisplayName, Alias, Database, PrimarySMTPAddress | Export-CSV -path "C:\Export.csv" -notype

Once you have the information in a CSV file you can attach it to an email and send it to yourself (or someone else) as per my previous blog post

Creating Custom Tables in Powershell

When working with scripts, it is often necessary to collect, store and present information in a table format. An example of this could be to export a number of properties from a users mailbox in Exchange into a table format.

For my example, I will run the following command to export all the properties of a mailbox (mine) to a variable called $UserList

$UserList = get-mailbox -ResultSize Unlimited | Where-Object {$_.DisplayName -eq "Morrissey, Peter"}

The next thing to do is to declare/create the table. It can be called anything you like, but I tend to use $report. The following line of code creates the table;

$report = @()

Next, you need to populate each row of your table with whatever fields you require. This would usually be done with a ForEach statement where you cycle through a list of objects, with numerous properties for each object.

In my example, the object list is stored in $UserList. I can run the command $UserList | fl to display the list of all the properties available for each object in my variable.

This ForEach command will cycle through each entry in $UserList (in my example it is only a single entry for my mailbox) and add whatever properties of that object I declare into the table (I have used DisplayName and Database) but you can use as many as you like - they just need to be separated by a semi colon (;)

ForEach ($User in $UserList)
             {
             $report += New-Object psobject -Property @{DisplayName=$($User.DisplayName);Database=$($User.Database)}
             }

This is a simple example and will create a table with two values for each row; DisplayName and Database.

Once you have populated all the rows you require in your table, you can then export it to a CSV file using the following command;

$report | select DisplayName, Database | Export-CSV -path "C:\ReportOutput.csv"

Once again, you will need to replace $report with whatever variable you used to declare your table. You also need to use the Select component to select all the table fields you wish to export into the CSV file - in my case it was the DisplayName and Database fields.

Finally, once the CSV file is exported, you can then attach it to an email and send it to yourself (or someone else) as outlined in my previous blog post.

Tuesday 29 January 2013

Sending SMTP Emails with Powershell using Authentication

The following script can be used to send an email message using SMTP. This method is slightly different to my previous post about sending email messages in Powershell as this method uses the smtpClient method rather than mailmessage in order to allow for authentication to occur.

$From = "fromaddress@domain.com"
$To = "toaddress@domain.com"
$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "username@gmail.com"
$Password = "gmailpassword"
$subject = "Email Subject"
$body = "Insert body text here"

$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);

$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($From, $To, $subject, $body);

You will need to update the values of the first 8 variables to match what is required for the email account/domain you are sending from.

The SMTP Server and Port number will vary for different email providers - I can confirm the server and port specified above for Gmail are correct though.

UPDATE - 29/11/13

You can also use the System.Net.Mail.MailMessage class to give you the additional option to add CC, BCC, attachments etc, as per the below script. Simply update the first 9 variables below to configure your email message.

$SMTPServer = "smtp.gmail.com"
$SMTPPort = "587"
$Username = "username@gmail.com"
$Password = ""

$to = "user1@domain.com"
$cc = "user2@domain.com"
$subject = "Email Subject"
$body = "Insert body text here"
$attachment = "C:\test.txt"

$message = New-Object System.Net.Mail.MailMessage
$message.subject = $subject
$message.body = $body
$message.to.add($to)
$message.cc.add($cc)
$message.from = $username
$message.attachments.add($attachment)

$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSSL = $true
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.send($message)
write-host "Mail Sent"

File Locked After Sending SMTP Email with Powershell

In my previous blog post about sending emails with Powershell, I mentioned a problem where files that you attach to an email become locked until the instance of Powershell you are running has exited completely. So if you run a script through Powershell ISE that attaches a file to an email, that file will remain locked until you exit Powershell ISE.

If the file is locked by Powershell, you will get an error/warning message similar to the following if you try to modify it in any way;

The process cannot access the file 'c:\filename.txt' because it is being used by another process

By using the following command, you can ensure that Powershell 'disposes' of the email message once it has been sent and does not continue to 'lock' any files you attach and send via email;

$mailmessage.dispose()

Note: this is assuming that $MailMessage = New-Object system.net.mail.mailmessage 

Friday 25 January 2013

Sending Email Messages with Powershell

A lot of the scripts I use generate reports or .csv files that I need to reference or send to other users who have requested them. One of the easiest ways to accomplish this is to configure your script to automatically email yourself (or anyone else) with the results and/or any files that were created in your script. This saves you time by not having to manually copy the files to another location or attach them to a new email message. You can simply receive the email message and forward it on, or include all recipients in the original email sent by the script.

The following lines of code can be used to create and send an email message with Windows Powershell:


$SmtpClient = new-object system.net.mail.smtpClient 
$MailMessage = New-Object system.net.mail.mailmessage 
$SmtpClient.Host = "your.smtp.server" 
$mailmessage.from = ("fromaddress@yourdomain.com") 

$mailmessage.Subject = “Subject”
$mailmessage.Body = "body text"
$mailmessage.IsBodyHtml = $true
$mailmessage.ReplyTo = "replytoaddress@yourdomain.com"
$mailmessage.Attachments.Add("C:\Path\yourfile.txt")
$smtpclient.Send($mailmessage)
$mailmessage.dispose()

Notes:

You will need to replace the values in some of the variables above to allow it to work in your environment (ie. SmtpClient.Host, FromAddress, Subject, Body text, Reply to address, attachment path).

You will also need to ensure that whatever machine you are running this script on is "authorised" to send with the SMTP server you have configured.

The final line of code $mailmessage.dispose() is very important when sending emails with attachments. If this line is not included, Powershell 'locks' the file(s) you attached after the message has been sent and will not 'release' them until the Powershell instance you are running is closed.

Thursday 24 January 2013

Exporting Exchange Distribution Group Members with Powershell

Before making any major changes to distribution groups, I like to take a backup of the current membership lists prior to making any changes. Particularly in cases where membership lists are quite large, it's always better to be safe than sorry for when a user or distribution group owner wants to know who was a member prior to making the change.

Getting the list of members and exporting it to a simple .csv file can be achieved using the Powershell script below.

Note that you should change the first two (2) variables ($DLName and $ExportFilePath) accordingly.

#Script to get/export list of all distribution group members
#Date Created: 24th January, 2013
#Author: Peter Morrissey


#Enable Exchange cmdlets
add-pssnapin *exchange* -erroraction SilentlyContinue

$DLName = "DL_Example_List"
$ExportFilePath = "C:\Export\Export.csv"


$MemberList = get-distributiongroupmember -identity $DLName

ForEach ($Member in $MemberList){write-output "$($member.name)" | out-file -filepath "$exportfilepath" -append -noClobber}

Wednesday 23 January 2013

Creating a Graphical User Interface (GUI) with Powershell

One of the first things I wanted to do when I started learning Powershell was to create a simple graphical user interface (GUI) that I could use to automate some of my day to day tasks. One main task in particular that took up a lot of my time was creating new user accounts. The process involved creating an Active Directory account, attaching a new mailbox to the Active directory account, adding the new account to security and distribution groups, and also sending a notification email to the new account requester with all the information in it. Doing this process manually was incredibly time consuming, and I knew there had to be a better way.

By using Powershell, I created a script that prompts with several input boxes and list boxes where I can enter all the required details to create a user account. All these inputs are saved as variables, and can then be used to run a single command to perform all the necessary steps.

Here is a screenshot of one of the input boxes that is presented during the script:


By repeating this prompt for first name, last name, office, department, title, phone number, password etc, I was able to quickly and easily collect all the information I needed to create an Active Directory account using the dsadd command - which also runs as part of the script once all the information has been collected.

Once I had an Active Directory account created, I was then able to create a mailbox and attach it to the Active Directory account using the Enable-Mailbox cmdlet.

And finally, once the Active Directory account and mailbox were created,  I could add the user to any required security/distribution groups, and send all the new account information via email to the requester. (I will cover each these topics in more detail in future blog posts.)

One of the most valuable things you can teach yourself with Powershell is how to create input boxes and list boxes for the collecting of information you require to run a command. Some examples of such commands are creating user accounts (as I just discussed), enabling or disabling ActiveSync on a mailbox, or enabling/disabling email forwarding. The possibilities are endless, though most of mine focus around the Active Directory/Microsoft Exchange technologies.

This Technet Article is what I used to get started on input boxes and highly recommend it as a starting point as the article breaks down each line of code and explains what it does.

Once you learn how to create, modify and use input boxes, you can apply the same lines of code over and over again within all your scripts - invest the time to learn it and it will save you time in the end, and open up enormous possibilities/options for your Powershell scripting.

Monday 21 January 2013

Working with Forms Outside of Powershell ISE

Another issue I encountered when I was first starting out with Powershell scripting was a strange error that occurred when I loaded any of my scripts that used forms. (Forms are used to provide a graphical user interface (GUI) for the script - eg. input boxes, list boxes, pop up messages etc). The issue only occurred when I ran the script outside of the Powershell editor - Powershell ISE.

The following error would occur when the script started;

New-Object : Cannot find type [System.Windows.Forms.Form]: make sure the assembly containing this type is loaded

This error would occur hundreds of times, once for every line of code that referred to System forms that were being used.

After a little investigating, I discovered that you must load the .NET Framework classes being used at the beginning of the script. This is easily accomplished with the following lines of code;


[void]  [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void]  [System.Reflection.Assembly]::LoadWithPartialName("system.net.mail")

This will load all the system form assembly's required to use custom windows forms in your script.

Note: The [void] text simply prevents any informational messages from being displayed on the screen when the classes are loaded.

Friday 18 January 2013

Powershell Execution Policies - Execution of Scripts is Disabled on this System

If you are attempting to run a powershell script file (.ps1) on a system for the first time, you will most likely encounter an error message similar to this;

File filename.ps1 cannot be loaded because the execution of scripts is disabled on this system.

This is related to what is known as the "execution policy" that is set for the particular user/computer you are attempting to run the scripts on.

You can view what the current execution policy is on your machine by opening a Windows Powershell prompt and running the command get-executionpolicy. There are four (4) different execution policies available:

  • Restricted - No scripts can be run. Windows PowerShell can be used only in interactive mode.
  • AllSigned - Only scripts signed by a trusted publisher can be run.
  • RemoteSigned - Downloaded scripts must be signed by a trusted publisher before they can be run.
  • Unrestricted - No restrictions; all Windows PowerShell scripts can be run.

You can modify what the current execution policy is by running the command set-executionpolicy setting where setting is one of the four (4) values specified above.

The following technet article contains further information on signing of scripts with Windows Powershell

http://technet.microsoft.com/en-us/library/ee176949.aspx

Mozilla Firefox - The Proxy Server is Refusing Connections

Over the past few days I have encountered a number of users reporting issues with Firefox displaying the below error page:

The proxy server is refusing connections. Firefox is configured to use a proxy server that is refusing connections.



As at the time of this blog entry being written, the issue appears to be specifically related to version 18.0 of Mozilla Firefox. By installing a previous version of Mozilla Firefox (17.0.1), we were able to resolve the problem. Version 17.0.1 can be downloaded from here (Windows version)

In the specific environment that I discovered the issue, the proxy server involved was a Client Side Proxy (CSP) Squid server. The error page above was displayed intermittently on random pages/sites, most of which were using https.

Note: If you install a previous version of Mozilla Firefox, be sure to go into the Options and disable automatic updates for Firefox, otherwise it will simply update itself to 18.0 again automatically.

Powershell Scripts Slow to Load

One of the more unusual issues I have experienced is one where Powershell scripts take a significantly long time to load. After right clicking a .ps1 and selecting 'Run with Powershell', it can take several minutes of waiting on the black shell screen before anything actually happens.

The issue is caused by a setting within Internet Explorer that forces a Certificate Revocation check for the certificate used to sign the code in the Powershell script.

You can turn off this checking by following the steps below.
  1. Open Internet Explorer
  2. Go to Tools > Internet Options
  3. Select the Advanced tab
  4. Scroll down to the Security section. Uncheck the option to Check for publisher's certificate revocation
For security reasons it is not recommended to leave this setting unchecked, especially if the server has Internet access.

I have come across this problem on Windows Server 2003 and Windows Server 2008 R2 platforms.

Enabling Exchange Cmdlets in Powershell

In order to run Exchange Server commands within a Powershell script, the relevant cmdlets need to be 'called' within Powershell.

This is accomplished with the following line, which is found at the start of nearly all of my Powershell scripts.

#Enable Exchange cmdlets
add-pssnapin *exchange* -erroraction SilentlyContinue

If you attempt to run Exchange commands in Powershell (eg. Get-Mailbox) without loading the Exchange cmdlets first, you will get an error similar to the following;


The term 'get-mailbox' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:12
+ get-mailbox <<<< 
    + CategoryInfo          : ObjectNotFound: (get-mailbox:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException


Exchange Global Address List Not Updating - Force Global Address List (GAL) Update

I have created this Powershell script to be able to force an update of the Global Address List within Exchange 2007. After making changes to recipients/distribution lists within Exchange, I sometimes required a way to make these changes quickly accessible to users, so when they next download the GAL through Outlook, they obtain the latest changes. Most environments do this automatically once or twice a day, which is fine, but this script can be used to force the process to run as required.

Before running this script, I manually force an update of the Default Offline Address Book (OAB) through the Exchange Management Console. I then run this script to force the latest version of the OAB to the Client Access Server's so it is available for download by end users.


#Enable Exchange cmdlets
add-pssnapin *exchange* -erroraction SilentlyContinue

#Force Update of OAB

Get-OfflineAddressBook | Update-OfflineAddressBook

#Get List of Current CAS Servers

$CASList = Get-ExchangeServer | where {$_.isClientAccessServer -eq $true}

#Force Deployment of GAL to CAS's

ForEach ($CAS in $CASList)
    {
    Update-FileDistributionService $($CAS.Name)
    }

Welcome

Welcome to my Blog!

I'm currently employed as an IT professional, and have decided to create this blog to share some of my ideas, thoughts, creations and findings with anyone who cares to look, as well as have a repository of sorts for some of my work/solutions.

I'm a big believer in automation - working smarter, not harder. I am a self taught Powershell "scripter" and have created a large number of scripts to help make my job much easier, and my time spent performing tasks much more efficient - in particular with Microsoft Exchange 2007 and Active Directory.

I plan to share alot of these creations on this very blog. Everything I've come to know and learn about Powershell scripting has been from other various blogs and sites around the Internet - it's always nice to give something back.

Feel free to comment - feedback/suggestions/questions are always welcome :-)

Cheers,
Peter.