Home > Exchange Server > Script: New-WelcomeEmail.ps1 – Automatically Sending a ‘Welcome’ Email to All New User Accounts

Script: New-WelcomeEmail.ps1 – Automatically Sending a ‘Welcome’ Email to All New User Accounts

PowerShell-logo-128x84Note: I’ve updated this script to address a couple of issues. The first is that if a scheduled task was configured for a time frame other than what was configured in the script itself, this would yield sporadic results. I’ve addressed this by writing a time stamp to the registry when the script runs. This removed the requirement of configuring the time in the script itself, and provides resiliency if the script runs at different times. Run the script once manually to set the configuration. I’ve also added some code that verifies the Exchange PowerShell snapin is loaded before attempting to run. If you’d like a feature added, please let me know in the comments below.

Note #2: If you’re using a server that’s not configured for the normal U.S. style time-date format, such as in the U.K., see Neil Hobson’s post at http://neilhobson.blogspot.com/2010/11/powershell-bug.html for information.

Anything that we can do to cut down on repetitive calls to the Help Desk staff is a good thing. When a new employee starts, there are always questions about ‘what is my email address?’, and ‘how do I get to email from the web?”. For years, admins have come up with sometimes complicated methods to send a new user a canned email that tries to answer these questions. With Exchange 2007 and Exchange Management Shell (PowerShell), we can do this quite easily. In fact, the hardest part is deciding what to include in the email message. Let’s get started..

Let’s read some info from the registry to see when was the last time the script ran. If it hasn’t run before, let’s set some initial info:

$strScriptName =  $MyInvocation.MyCommand.Name
if (!(Get-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name LastRun -EA SilentlyContinue)){
# this is the first time the script has run - let's create the registry key and value for future runs
New-Item -path HKLM:\Software\Innervation -EA SilentlyContinue | Out-Null
New-Item -path HKLM:\Software\Innervation\$strScriptName | Out-Null
New-ItemProperty -path HKLM:\Software\Innervation\$strScriptName -Name "LastRun" -Value (Get-Date) -propertyType String | Out-Null
write-host "Initial configuration completed." -ForegroundColor green
}
# get time stamp from registry so we know when it last ran
$LastRun = Get-Date ((Get-ItemProperty -path HKLM:\Software\Innervation\$strScriptName -Name LastRun).LastRun)
$ElapsedTime = ((Get-Date) - $lastrun).TotalSeconds

Let’s define some variables that we’ll use throughout the process.

$strMsgFrom = "Contoso HelpDesk "
$strMsgTitle = "Welcome to Contoso!"

These set the From and Title for the email that we’ll send, as well as get today’s date, and the name of the script. Next, we create a new object to allow sending SMTP email:

$SMTPClient = New-Object Net.Mail.SmtpClient("localhost")

We can replace “localhost” with the IP address of a remote hub transport server if the script is not running on a hub transport server.

Next, we get a list of mailboxes that we need to send the email to. We’ll use a scheduled task to actually run the task. I run mine every 4 hours, but the code doesn’t care how often it runs. It will use the time stamp established above to email all mailbox created since then. We also want to avoid any mailboxes that are disabled. So our query looks like this:

$MBXArray = @(Get-Mailbox -ResultSize Unlimited | ? {($_.WhenCreated -gt (Get-Date).AddSeconds(-$ElapsedTime)) -and ($_.ExchangeUserAccountControl -ne "AccountDisabled")})

We now have an array, $MBXArray, that contains all of the mailboxes that we’ll email. We now cycle through the array via ForEach, and begin to assemble a personalized email message to each user. $mailbox holds the current account in the loop, so we can pull specific info for each user. Note that the text in $strBody is completely arbitrary – you can include whatever you want. Here’s a sample of one I did for a recent client:

ForEach ($mailbox in $MBXArray ) {
$strMsgTo = $mailbox.PrimarySMTPAddress
$strMsgBody = "Hello, "+$mailbox.DisplayName+", and welcome to the Contoso family! Please keep this email for future use. It contains vital information.
--------------------------------------
Username and password
--------------------------------------
Your network username is '"+$mailbox.SamAccountName+"'. Use your username and password to login to the network. Your password should NEVER be shared with anyone except the I.T. department, and only then when requested. Please do not write it down on anything that can be seen by your coworkers. You will be prompted to change it regularly.
--------------------------------------
Email
--------------------------------------
Your email address is '"+$mailbox.PrimarySMTPAddress+"'.

To access your email, calendar, contacts, and tasks from outside of the building, such as from home, you can do so from any Internet connected computer. Simply open Internet Explorer and go to the Outlook Web Access (OWA) page at https://mail.contoso.com/ and log in using your username and password. Please note the 's' in https.

If you’d like to have access to your email and contacts from your cell phone, you will need a cell phone that has Windows Mobile 5 or later, or an Apple iPhone. Blackberry phones are not supported. Instructions for configuring your device can be found in the Frequently Asked Questions (FAQ) section of the Contoso Intranet at https://intranet.contoso.com/helpdesk/Lists/SupportFaq/AllItems.aspx
--------------------------------------
Contact information
--------------------------------------
Once you’re situated, please go to http://directory/DirectoryUpdate and update your information. Log in using your username and password. It’s important that you update your information anytime something changes, such as title, department, phone number, etc. This information is used in various systems and applications, and is your responsibility to keep up to date.
--------------------------------------
Computer, Email, and Internet policies
--------------------------------------
Contoso, Inc. provides a computer for your work tasks. The use of personally owned computers and related equipment is not permitted on our network. Additional information about use of Contoso computers, email, Internet, etc. can be found in the Employee Handbook located in the HR section of the intranet at https://intranet.contoso.com/hr/
--------------------------------------
Technical assistance
--------------------------------------
Should you need technical assistance, please check the Frequently Asked Questions (FAQ) section of the Contoso Intranet at https://intranet.contoso.com/helpdesk/Lists/SupportFaq/AllItems.aspx. If you cannot find an answer there, submit a Service Request on the Contoso intranet at https://intranet.contoso.com/helpdesk. If you are unable to access the intranet site, only then should you email HelpDesk@contoso.com. It is monitored by the whole IT department, and will ensure your issue is resolved in a timely manner.

Thank you, and, again, welcome to Contoso!
The Information Technology Department"

As you can see, we insert the user’s actual account name, email address, etc since that info is available in the ForEach loop. The message is just plain text, so spacing is preserved. URLs will be clickable links as well. Note: You’ll want to pay close attention to quotes and variables, as having an extra or missing quote can cause an error.

Now we actually send the message:

$SMTPClient.Send($strMsgFrom,$strMsgTo,$strMsgTitle,$strMsgBody)
}
# update registry here with a fresh time stamp
Set-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name "LastRun" -Value (Get-Date) | Out-Null

We’ll run this script on a hub transport server. So take the script, available in the DOWNLOAD section below, and save it in your \scripts folder. You’ll also need an Exchange receive connector that will accept email sent from PowerShell scripts. For that, see Creating a receive connector to use for sending email from PowerShell. Now, schedule a task to run every 4 hours using the info in Running PowerShell scripts via Scheduled Tasks.

Point of interest: In the text I send to the users, you’ll see a link to the Directory Update (http://directory/DirectoryUpdate in the example above). This is for Directory-Update, a VERY lightweight ASP app developed by fellow MVP and author Jim McBee and another developer. It’s completely customizable, and allows users to update selected fields of their AD account to help keep the Global Address List (GAL) current. It is worth the small cost, and really helps you keep the GAL full of correct info. I have another PowerShell script that checks AD account fields, and when it finds empty fields (phone number, title, etc), it sends them an email with a link to the Directory-Update web page. Combine that with Automatically updating the Global Address List with mobile numbers from Exchange ActiveSync and it’s like a self-cleaning oven!

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.3 – 02-24-2013 – New-WelcomeEmail.v1.3.zip

Send-NewUserWelcome.zip

ScriptImages.zip – image files used in emails

Changelog

See the changelog for this script for information on versions and what’s included/addressed in each.

  1. Lars Bornich
    November 22nd, 2011 at 15:16 | #1

    First of all – excellent work; I have implemented a deviation of this script in our current messaging environment. The major changes I made was to log to the event log instead of the console, as well rewriting the mailbox retrieval logic:

    [DateTime]$LastRun = Get-Date ((Get-ItemProperty -path HKLM:\Software\Sauer-Danfoss\$strScriptName -Name LastRun).LastRun) -Format s
    if ($Verbose) { write-eventlog -logname application -source $strScriptName -eventID 5 -entrytype Information -message “Last run time: $LastRun”}

    Set-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name “LastRun” -Value (Get-Date -Format s) | Out-Null

    Get-Mailbox -ResultSize Unlimited -Filter {(IsResource -eq $false) -and (IsShared -eq $false)} | ? {($_.WhenMailboxCreated -gt $LastRun) -and ($_.ExchangeUserAccountControl -ne “AccountDisabled”)}

    You will notice that I’ve changed WhenCreated to WhenMailboxCreated as mailboxes are not necessarily created at the same time as the mailbox. Also, I removed the ElapsedTime logic, as it was redundant (you can compare WhenMailboxCreated directly to a timestamp), and finally, moved adding the new timestamp to the registry to before the mailboxes are enumerated, as there would otherwise be a gap during the time it takes to enumerate all mailboxes.

    I’d be happy to send you a copy of my script if you would like to take a look at the changes.

    Again, thank you for your inspiration!

    • Pat Richard
      November 22nd, 2011 at 15:24 | #2

      Sure, I’d love a copy. My email should be in the comment block in the beginning of the script.

      As for moving the lastrun timestamp, the only reason I didn’t do it that was was in case of an error that causes the script to stop. I wait till the end to make sure everything was successful. But I can see your point.

  2. Wesley
    March 12th, 2012 at 08:10 | #3

    Larry can you send me a copy of the script that Pat send you?

  3. Paul Gordon
    April 16th, 2012 at 05:40 | #4

    Couple of points…

    When entering the message body into $strmsgbody, I found it only works with linebreaks rather than carriage returns… (i.e. Shift-Return instead of Return) at the end of each line. If I entered the text with “proper” carriage returns at the end of each line, powershell does not like it!

    Also, rather than entering all of the body text in the script, I wanted to give the ability to simply maintain a single text file somewhere on the network, and pull it in with get-content… This works fine to a point, but it does not preserve the formatting in the text file at all… neither linebreaks or carriage returns are honoured, and the content is rendered as a single line.. – any ideas how to improve that?

    And lastly, rather than running a scheduled script, I plan to use the extension agent and put this into the scriptingagentconfig.xml file for the new-mailbox and enable-mailbox cmdlets so that the mail is generated automatically for all new mailboxes AS they are created…

  4. Manuel Arce
    July 31st, 2012 at 12:33 | #5

    just a quick question, does this work for new users that were copied from an existing one?
    I mean, I implemented this and it works fine if I create a new user from EMC, but if a copy an user in AD and then create it on EMC doesn’t work, any idea?

    • Pat Richard
      July 31st, 2012 at 21:33 | #6

      Yes, it should work for any newly created user.

  5. Norman
    October 29th, 2012 at 08:30 | #7

    Hi, thanks för at great script. In the changelog v1.3 i mentioned, but i cannot find it to download. The link points to v1.2 or am i missing something?

  6. diego
    February 24th, 2013 at 19:47 | #8

    yes… no version 1.3 for download…

    • Pat Richard
      February 24th, 2013 at 20:30 | #9

      Okay, just posted the file that should be 1.3. Try it out.

  7. Clinton
    March 12th, 2013 at 08:25 | #10

    This does not work for exchange 2010.

    Is there away to make it work for exchange 2010?

    • Pat Richard
      March 12th, 2013 at 08:39 | #11

      It does work with Exchange 2010. And 2013.

      • Clinton
        March 12th, 2013 at 08:53 | #12

        Can you help me out then?

        • Pat Richard
          March 12th, 2013 at 08:54 | #13

          This script was written using 2010. If you have a properly configured receive connector, it will work fine.

          • Clinton
            March 12th, 2013 at 09:48 | #14

            I found the problem

            $MBXArray = @(Get-Mailbox -ResultSize Unlimited | ? {($_.WhenCreated -gt (Get-Date).AddSeconds(-$ElapsedTime)) -and ($_.ExchangeUserAccountControl -ne “AccountDisabled”)})

            This part does not get any results.

          • Pat Richard
            March 12th, 2013 at 17:22 | #15

            Run just by itself, it won’t return anything. That’s because there is a variable in that line that’s empty when run by itself. If it’s run via the script, then it’s only going to show users created since the last time the script ran (it uses a time stamp from the registry).

            If you open Exchange Management Shell and run a wide check, using something like
            $LastRun = (Get-Date).AddDays(-1000)
            $ElapsedTime = ((Get-Date) – $lastrun).TotalSeconds
            $MBXArray = @(Get-Mailbox -ResultSize Unlimited | ? {($_.WhenCreated -gt (Get-Date).AddSeconds(-$ElapsedTime)) -and ($_.ExchangeUserAccountControl -ne “AccountDisabled”)})
            $MBXArray

            Does it list anything? It should list all users created in the last 1000 days.

          • Clinton
            March 13th, 2013 at 02:34 | #16

            Yes it shows results.

            Hmm.. is the problem then in the connector. Do i really need a Internal connector. Because when i use send-mailmessage it works. Is the connector still a problem?

  8. Clinton
    March 12th, 2013 at 09:01 | #17

    I use Send-NewUserWelcome.zip and i modifed it. No errors. I created a new mailbox and new user but still no mail. I checked the registry key and it was created.

    if (-not((Get-PSSnapin) -match “Microsoft.Exchange.Management.Powershell.E2010”)){ Add-PSSnapin Microsoft.Exchange.Management.Powershell.E2010 }

    $strScriptName = $MyInvocation.MyCommand.Name
    if (!(Get-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name LastRun -EA SilentlyContinue)){
    # this is the first time the script has run – let’s create the registry key and value for future runs
    New-Item -path HKLM:\Software\Innervation -EA SilentlyContinue | Out-Null
    New-Item -path HKLM:\Software\Innervation\$strScriptName | Out-Null
    New-ItemProperty -path HKLM:\Software\Innervation\$strScriptName -Name “LastRun” -Value (Get-Date -Format G) -propertyType String | Out-Null
    write-host “Initial configuration completed.” -ForegroundColor green
    }
    # get time stamp from registry so we know when it last ran
    $LastRun = Get-Date ((Get-ItemProperty -path HKLM:\Software\Innervation\$strScriptName -Name LastRun).LastRun)
    $ElapsedTime = ((Get-Date) – $lastrun).TotalSeconds

    $strMsgFrom = “Administrator ”
    $strMsgTitle = “Welkom bij Company!”

    $SMTPClient = New-Object Net.Mail.SmtpClient(“*.*.*.*”)

    $MBXArray = @(Get-Mailbox -ResultSize Unlimited | ? {($_.WhenCreated -gt (Get-Date).AddSeconds(-$ElapsedTime)) -and ($_.ExchangeUserAccountControl -ne “AccountDisabled”)})

    ForEach ($mailbox in $MBXArray ) {
    $strMsgTo = $mailbox.PrimarySMTPAddress

    $strMsgBody = “Beste, “+$mailbox.DisplayName+”, welkom bij Company! Deze email is persoonlijk. Het bevat gevoelige informatie.
    ————————————–
    Gebruikersnaam en wachtwoord
    ————————————–
    Jouw netwerk gebruikersnaam is ‘”+$mailbox.SamAccountName+”‘.
    De Support Afdeling”

    $SMTPClient.Send($strMsgFrom,$strMsgTo,$strMsgTitle,$strMsgBody)
    }

    # update registry here with a fresh time stamp
    Set-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name “LastRun” -Value (Get-Date -Format G) | Out-Null

    • Clinton
      March 12th, 2013 at 09:18 | #18

      Do i need to add or change the SMTPclient line?

      $SMTPServer = “*.*.*”
      $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer)

  9. Clinton
    March 14th, 2013 at 08:11 | #19

    Is there anyway i can put html in the body?

  10. Clinton
    March 19th, 2013 at 03:45 | #20

    It is working. I changed your script a little bit. I made the mail more html. But thnx for your nice script.

  11. Vik
    July 21st, 2013 at 06:02 | #21

    Your script is running great but it is sending emails to existing users also. Everytime this script runs, it sends email to new user as well as existing user. Would you please help?

  12. Philip
    August 7th, 2013 at 02:54 | #22

    Thank You for this great script. However, I need some help in modifying this script.

    1. Can we send a PDF attachement along with this Welcome email, if so how?

    2. We need to send this PDF attachment to only new mailbox users who have “EX” in the “Description” field in the Active Directory user properties. Is this possible?

    Thank You in advance. Kindly help.

  13. Ayanes
    February 26th, 2014 at 20:00 | #24

    would anybody be able to assist in setting this up I am a little confused here. I downloaded the script and saved it to my script folder then ran .\New-WelcomeEmail.ps1 -Install however the only thing it did was ask me for my password and created the scheduled task. It did not however go through the parameters and ask me for things like IP address of email server, or OWA URL etc. Am I missing something, I think this script would be great if I could get it to run and test it. Any guidance is greatly appreciated.

    • Pat Richard
      February 26th, 2014 at 20:01 | #25

      It’s not supposed to ask for those things. You have to manually enter that info in the script.

  14. Ayanes
    February 26th, 2014 at 20:06 | #26

    ok thanks for the quick response. SO the only way to test is by creating a new account and seeing if I get the email right?

  15. Pat Richard
    February 26th, 2014 at 20:08 | #27

    Or the run it manually with the -PreviewUser option

  16. Ayanes
    February 27th, 2014 at 12:08 | #28

    Ok so I got it to run and send the email great sript by the way, but instead of sending the email to the newly created mailboxit is sending it to my account which is who the scheduled task is to run under. The email contains all the correct information for the newly created account i.e. testuser domain name and email address but it just keep sending it to me and I dont know where its getting my email address from. How can I get this to send it to the new mailbox instead.

  17. Ayanes
    February 27th, 2014 at 13:14 | #29

    please disregard previous comment I figured out where I had put my email address. Sorry

  18. Ayanes
    February 27th, 2014 at 16:04 | #30

    One last question and I think I am all set. For testing purposes I set the scheduled task to run every 5 minutes, the email is created and sent to the correct email address fine but for whatever reason it sends it over and over to the same user every time that tasks run. Shouldn’t it identify it sent it already to this test user?

  19. Pat Richard
    February 27th, 2014 at 16:07 | #31

    On the machine you’re running it on, look in the registry at HKLM:\Software\Innervation\New-WelcomeEmail.ps1 and see if there is a LastRun timestamp.

  20. Ayanes
    February 27th, 2014 at 16:22 | #32

    yes there is but the timestamp is from early this morning and it has ran multiple times since then but seems like the timestamp is not updating. However I also see LastRun under HKLM:\Software\Innervation and the timestamp there is updating, I tested it by running the task again manually and it changed it once it completed.

  21. Ayanes
    February 27th, 2014 at 16:59 | #33

    I fixed it the command at the end Set-ItemProperty HKLM:\Software\Innervation\$strScriptName -Name “LastRun” -Value (Get-Date) | Out-Null was updating HKLM:\Software\Innervation instead. So I changed that line to Set-ItemProperty HKLM:\Software\Innervation\New-WelcomeEmail.ps1 -Name “LastRun” -Value (Get-Date) | Out-Null and I received only one email at it has ran at least twice. I’m not sure why it didnt identify the variable correctly but thanks. Again thanks for all the help and awsome script been looking for something like this for a while.

  22. Pat Richard
    February 27th, 2014 at 17:03 | #34

    Ah – so that’s an issue on my end. I stopped using something called Hungarian notation, which required me to change the names of some variables. So
    Set-ItemProperty HKLM:\Software\Innervation\$strScriptName
    at the end of the script should be
    Set-ItemProperty HKLM:\Software\Innervation\$ScriptName
    Sorry about that!

  23. Ayanes
    February 27th, 2014 at 19:19 | #35

    No worries I didnt even catch that myself. Thanks again

  24. Ayanes
    February 28th, 2014 at 18:28 | #36

    I am seeing an odd issue with the script and I wanted to see if you or anyone else have experienced it as well. Right now the script works perfectly BUT only if I create the user in AD then add the mailbox through the EMC i.e New Mailbox –> Existing user, look for the newly created user and complete the wizard. Once the script runs it finds the newly created mailbox and sends the email fine. However if I create the new user using the EMC from the beginning the email is never sent, the script never sees this newly created account from the EMC. When I run the following in EMS (Get-Mailbox -ResultSize Unlimited | ? {($_.WhenCreated -gt (Get-Date).AddDays(-1)) -and ($_.ExchangeUserAccountControl -ne “AccountDisabled”)}) I do see the all three accounts that I created including the one from within the EMC. Anyone else seen this?

  25. Nathan
    August 27th, 2014 at 13:02 | #37

    Pat – I just ran across this and it looks like it will be a great solution for us. I have one question, however, before I implement it. We are a construction company, and over the lifespan of a computer it will serve many different users. I am concerned that writing the timestamp in HKLM will only notify the first, new user of a machine. Would the best remedy be to just use HKCU instead of HKLM or I am I misunderstanding how the script runs? Thanks!

    • Pat Richard
      August 27th, 2014 at 13:05 | #38

      This script does not write to the local user’s computer registry. It time stamps the registry that the script runs on (server). This is a method to ensure that you capture everyone that needs to be emailed since the last time it ran.

  26. Nathan
    August 27th, 2014 at 15:00 | #39

    Pat – thanks for the quick response. One more question. Will the script work with Office 365? We have a hybrid setup so I can relay mail off of our on-premises Exchange box, but all of our mailboxes are in the cloud.

    • Pat Richard
      August 27th, 2014 at 16:22 | #40

      It should. As long as you can hit a receive connector that will accept the messages.

  1. March 23rd, 2012 at 08:05 | #1
  2. August 21st, 2012 at 08:38 | #2
  3. April 17th, 2014 at 17:10 | #3