Archive

Posts Tagged ‘script’

Script: New-DirectoryUpdateReminder.ps1 – Prompt Users to Update Their Active Directory Information

October 3rd, 2011 5 comments

This script will look at all users in AD, and determine if they are missing key information such as office, address, title, and manager. If they are, it will send them an email requesting they update their information. It should be noted that this script is designed for environments that have a self-service solution in place for users to update their information. This can include Exchange 2010, where ECP allows the user to change many fields:

ECP options to change user info

ECP options to change user info

In some of the environments that I build, where Exchange 2010 isn’t an option, or other fields need to be changed, I install Directory Update, a small footprint solution for IIS. Directory Update is a PHENOMENAL solution that’s inexpensive, yet feature packed. It’s fully configurable and features drop downs, check boxes, and logic to ensure that users are inputting the correct information in the correct format. It also allows you to specify what fields the user can update. I highly recommend it. Other environments might use some home-grown solution, or even SharePoint. Either way, a self-service solution takes the burden off the Help Desk. A perfect example is when a manager leaves the organization. When their AD account is deleted, the users who had that person listed as their manager will automatically start getting reminders from this script since the field is now empty.

Many orgs don’t worry as much about some of these fields. However, when the information is current and correct, the data can be pulled for other purposes, such as workflow applications, org charts, phone lists, etc. Some orgs use transport rules to create disclaimer or signature phrases such as how to contact a user’s manager. All of these are perfect reasons for using this script.

Features

Runs as a scheduled task, and will remind users daily until their information is complete.

Can be run in DEMO mode to see which users would receive an email.

Can be run in PREVIEW mode to receive the formatted message to see what it looks like before rolling it out in production.

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.

This script requires a receive connector that will accept mail. See Creating a receive connector to use for sending email from PowerShell.

Once the receive connector is created, copy the script from the .zip file below to your server.  Open the script in any true text editor, and set the various parameters. See the highlighted lines in the script below. Each should be configured for your environment.

[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a company name.")]
[ValidateNotNullOrEmpty()]
[string]$Company = "Contoso Ltd",
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify an OWA URL")]
[ValidateNotNullOrEmpty()]
[string]$UpdateUrl = "https://directory.contoso.com/",
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify the IP address of your email server")]
[ValidateNotNullOrEmpty()]
[string]$EmailServer = "10.9.0.11",
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a name and email address for the email 'from' field")]
[ValidateNotNullOrEmpty()]
[string]$EmailFrom = "Help Desk ",
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
[string]$HelpDeskPhone = "(586) 555-1010",
[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
[ValidatePattern("^http")]
[string]$HelpDeskURL = "https://intranet.contoso.com/",
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
[string] $TranscriptFilename = $MyInvocation.MyCommand.Name + " " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date),
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="This must be zero")]
[ValidateNotNullOrEmpty()]
[int]$UsersNotified = 0,
[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
[string] $ImagePath = "http://www.domain.com/images",

Save the script on your server.

Copy the images in the .zip file to the path you specified on line 102 above.

Run the script in demo mode to see a list of users that would receive the email messages:

.\New-DirectoryUpdateReminder.ps1 -demo
New-DirectoryUpdateReminder -demo

New-DirectoryUpdateReminder scheduled task

Test the script’s email functionality next by using preview mode. In preview mode, a single user will receive the email message. This will allow you to see what the users will see, as well as ensure that the formatting and wording is sufficient.

.\New-DirectoryUpdateReminder.ps1 -preview -previewuser [username]

After receiving and reviewing the message, adjust the HTML code as needed.

To configure the script to run as a scheduled task, run the script in install mode using

.\New-DirectoryUpdateReminder -install

This will create a Windows scheduled task that will run the script every day at 6:30am. Once the scheduled task is created, feel free to edit it to change the time.

Editing the scheduled task

That’s all it takes. Feel free to leave comments below, including any feature requests you’d like.

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.7 – 01-27-2014 – New-DirectoryUpdateReminder.v1.7.zip

v1.5 – 09-02-2011 – New-DirectoryUpdateReminder.v1.5.zip

ScriptImages.zip – image files used in emails

Changelog

See the changelog for this script which details all versions and their features.

[Redirect] New-PasswordReminder.ps1 v2.2 – Target Specific OUs, Better Password Policy Info, Code Tweaks

September 30th, 2011 35 comments

Here’s the latest version of the script. For information on previous builds, including installation instructions, please see New-PasswordReminder.ps1 v2.1 – updated to include better formatting, preview, and installer!

Issues resolved:

  1. added some missing ‘alt’ tags for some images in email HTML code
  2. added $HelpDeskURL variable in param block. That resolves the problem of some links that weren’t clickable (whoops!)
  3. updated links to point to new blog. This includes the one in the event log message.

#2 is the only one that you need to worry about. Line 166 defines a URL for your Help Desk:

[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory=$false)]
[ValidatePattern("^http")]
[string]$HelpDeskURL = https://intranet.contoso.com/,

New features/additions

  1. added code to determine global minimum password length & format message accordingly
  2. added code to determine global password complexity requirements & format message accordingly
  3. added some parameter validation
  4. added ability to target a single OU, and its children
  5. updated the Send-MailMessage line based on user feedback

#1 and #2 are quite similar. The script now looks at the default domain password policy and retrieves the minimum password length and complexity requirements. It then uses this info to formulate the text for the email. In previous versions of the script, the HTML code had to be set manually.

#3 is just to make sure valid info is passed to some of the parameters.

#4. This was a reader request. To target a specific OU, set the OU in the param block. Look at line 185

[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, Mandatory = $false, HelpMessage = "Please specify an Organizational Unit")]
[ValidateNotNullOrEmpty()]
[string] $ou

And set $ou to the full name of the OU, such as

[parameter(ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, Mandatory = $false, HelpMessage = "Please specify an Organizational Unit")]
[ValidateNotNullOrEmpty()]
[string] $ou = "ou=myusers,dc=contoso,dc=com"

The script will only check user accounts in that OU, and ALL CHILD OUs.

#5 was done to improve performance, but also get around an issue that was reported by a reader. Two issues resolved at once!

Download the new version at New-PasswordReminder.zip and keep those comments coming!

[Redirect] New-PasswordReminder.ps1 v2.1 – updated to include better formatting, preview, and installer!

September 18th, 2011 10 comments

Update 09-30-2011: An updated version is available at New-PasswordReminder.ps1 v2.2 – target specific OUs, better password policy info, code tweaks.

I wrote in New-PasswordReminder.ps1 – email users when their password will soon expire about how to set up a scheduled task to send users a polite reminder email when their password will soon expire. It’s been a fairly popular post, but there has been some areas where it could be improved. Well, consider it done.

One of the hardest parts was getting a decently formatted email that looked good. This could take some trial and error, and the original script didn’t really have a way built in to preview what the end user would see. As a result, some hapless users would be flooded with your “test” messages. I fixed that by creating a preview mode. Manually run the script with the preview switch, and a user to send the email to. For example

.\New-PasswordReminder.ps1 -Preview -PreviewUser bgates

This will send an email to the user, bgates. The email is formatted for a password that expires in one day, so the user gets the additional banner near the top as well.

Next up was creating a scheduled task. Not really terribly difficult to do manually, but I could see where it might take some trial and error. So, I added the install switch, which will create a scheduled task for the script, configuring it to run at 6am each day. Of course, that time can be manually adjusted by opening the scheduled task once it’s created. The install mode will ask for credentials to run the scheduled task under. Install it as so:

.\New-PasswordReminder.ps1 -Install

Note: The scheduled task is configured to point to where the script is when you run the install switch. So don’t move it later!

Next up was a little tweaking to the HTML code. In the original version, I tossed in some very basic formatting, but the person installing it had to tweak some HTML code to point to the location of some images. The new version has more images, but I defined a variable in the param block for the root folder where the images are stored. Just edit that line, and all other HTML code for the images will be fine. Look for these lines in the param block:

[parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Mandatory=$false)] [string] $ImagePath = "http://www.mydomain.com/scripts"

Update accordingly with a URL to the directory holding the images, but don’t include a trailing slash. Of course, you’re free to rip out all of the formatting and substitute your own. I merely included something so that it would work “out of the box”. The new zip file includes all image files required for the new formatting.

Next up, I added some simple logging to the application event log. The script will write a single entry when it starts, and a single entry when it finishes, noting how many users were processed (sent an email). I would love to hear how this script works in large environments. If you’re willing, please let me know (via comments below) how long it’s taking to run in your environment, and the number of users in AD.

I fixed a couple of minor bugs, and included some code to install the RSAT-AD-PowerShell feature if it’s not installed. The comment based help was enhanced, and some of the code was cleaned up so it’s easier to read.

The rest of the setup requirements remain. See the original post for additional info. Download the latest version New-PasswordReminder2.1

I’m very interested in hearing how you customize this, and any suggestions you may have. I’m always looking for ideas.

Script: New-ADPasswordReminder.ps1 – Email Users When Their Password Will Soon Expire

August 27th, 2011 345 comments

Note: Development on this script has moved to Github. Please see the repo at https://github.com/patrichard/New-AdPasswordReminder.

Description

In today’s increasingly mobile workforce, many users don’t login to Active Directory on a domain joined computer. Some use only Outlook Web Access/App, some use non-domain joined machines external to the company network, some just use mobile devices such as smartphones and tablets. And others may use Macs.

Users who login via domain joined machines on the company network get the reminder several days ahead. The default is 14 days, but can be configured in the Default Domain Group Policy under Interactive logon: Prompt user to change password before expiration.

OWA users see a notification when they login as well. In OWA 2007 running on IIS6, this can be adjusted via PasswordExpirePrenotifyDays. In fact, with OWA 2007 and 2010, you can even change your password after it expires, using the Password Reset Feature in Exchange 2007 and 2010. However, there are times when that’s just not a remedy. The password reset feature requires the Exchange server to be running on Windows 2008 or later, as it relies on IIS 7. Many Exchange 2007 shops are not on that platform yet.

Anyone who’s ever worked on a Help Desk knows that a LOT of users call to say they can’t login, only to determine it’s because their password expired. Many, if not most, are those types of users mentioned above. Others don’t notice, or simply ignore the notice when logging in. So let’s really make sure we notify them of the pending expiration. There are some third-party tools, including some that run on SharePoint, that enable a user to reset their password. But this is after the fact. Sure, we could use some third-party application to send a reminder, but… well… why? PowerShell to the rescue!

In the pre-Windows 2008 domain functional level days, we could just peek at the Default Domain GPO, and grab the Maximum Password Age parameter, since it was a global setting. Then we could go through Active Directory, find users who are not set to “never expire”, use some math, and come up with a list of users whose password expired soon.

But with the changes implemented with Windows Server 2008, we can now have Fine Grained Password policies, which allows us to have more than just one password policy in our organization. So, Executives get one password, IT people with elevated rights get another, etc. Cool in theory, but frustrating in our endeavor to notify users when they’ll expire.

I blatantly admit that I used part of a script by M. Ali, who wrote a blog post Find out when your Password Expires. The script looks checks Get-AdDomain, and looks at the DomainMode parameter in the results. From here, we know whether we can just peek at the Default Domain policy, or if we need to look deeper. Regardless of which way, we look through the users using Get-AdUser, and grab the PasswordExpired, PasswordNeverExpires, and PasswordLastSet fields. Obviously, if the account is expired, no need to keep reminding the user. And if the password never expires, then we also don’t need to notify the user. With PasswordLastSet, our math comes into play to determine when the password will expire. Not terribly short and sweet, but effective.

Once we know when the password will expire, we can then set a window for when we should notify the users. It makes sense to match what’s in the GPO so that notifications are consistent regardless of platform. This script is set to 14 days by default.

Next, we need to craft some information that we want to convey to the user. In this case, we’ll use some HTML formatting so that we can properly convey the importance of the info, as well as include some additional formatting. I’ve mocked up something based on some third-party tools, and on the comments and recommendations of IT Professionals and users. It’s simple enough to change, but be warned that many clients, including Outlook, don’t strictly adhere to HTML standards. So it can take quite a bit of trial and error to find out what does actually appear the way you want it to.

Installation and Setup

First, you need a receive connector that will accept mail from PowerShell. I cover that in Creating a receive connector to use for sending email from PowerShell. Next, the script must run on a machine with PowerShell 2.0 installed. This is a prerequisite for Exchange 2010 (and installed by default on Windows 2008 R2), but not for Exchange 2007. If you’re reluctant to upgrade PowerShell on your 2007 box, it can be run from any other box that has PowerShell 2.0 and the Exchange Management tools installed. Note: Exchange Management tools should always be updated and patched to the same level that your Exchange servers are.

Second, you’ll need the ActiveDirectory module available on the machine that will run the script. The ActiveDirectory module is installed when you add the Remote-Server Administration Tools feature in Windows Server 2008 R2. If the module is not detected, the script will attempt to install it automatically the first time it runs.

Next, grab the latest zip file from the DOWNLOAD section below. It includes the script and ScriptImages.zip contains a couple of images that are used in the warning for users who’s password expires in < 24 hours (seen in the Outlook screenshot above). The images need to be accessible to all users who will receive the reminder emails. This is likely to be a public web site.

Crack open the script in your favorite editor and update the lines in the param() block to match your environment. This includes $Company, $OwaUrl, $PSEmailServer, $EmailFrom, $HelpDeskPhone, $HelpDeskURL and $DaysToWarn. If you want to target a specific OU, set $OU. Also, set $ImagePath to a path holding the included image files (or those you add/edit). This path should be available to all users who may receive the reminder message. This is probably a public server.

param(
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Demo,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Preview,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Install,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$PreviewUser,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[switch]$Transcript,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a company name.")]
	[ValidateNotNullOrEmpty()]
	[string]$Company = "Contoso Ltd",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify an OWA URL")]
	[ValidateNotNullOrEmpty()][ValidatePattern("^https://")]
	[string]$OwaUrl = "https://mail.contoso.com",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify the IP address of your email server")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b")]
	[string]$PSEmailServer = "10.9.0.11",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify a name and email address for the email 'from' field")]
	[ValidateNotNullOrEmpty()][ValidatePattern("\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b")]
	[string]$EmailFrom = "Help Desk ",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string]$HelpDeskPhone = "(586) 555-1010",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[ValidatePattern("^http")]
	[string]$HelpDeskURL = "https://intranet.contoso.com/",
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $TranscriptFilename = $MyInvocation.MyCommand.Name + " " + (hostname)+ " {0:yyyy-MM-dd hh-mmtt}.log" -f (Get-Date),
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="This must be zero")]
	[ValidateNotNullOrEmpty()]
	[int]$global:UsersNotified = 0,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false, HelpMessage="Please specify how many days before expiration that users should begin to be notified.")]
	[ValidateNotNullOrEmpty()]
	[int]$DaysToWarn = 14,
	[parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Mandatory=$false)]
	[string] $ImagePath = "http://www.contoso.com/images/new-passwordreminder.ps1",
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptName = $MyInvocation.MyCommand.Name,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false)]
	[ValidateNotNullOrEmpty()]
	[string] $ScriptPathAndName = $MyInvocation.MyCommand.Definition,
	[parameter(ValueFromPipeline=$false, ValueFromPipelineByPropertyName=$false, Mandatory=$false, HelpMessage="Please specify an Organizational Unit")]
	[ValidateNotNullOrEmpty()]
	[string] $ou
)

Open an Exchange Management Shell session and run the script in demo mode to see a list of users that are expiring soon.The script won’t email the users in demo mode. It merely shows you who it WOULD, and how long till their password expires.

.\New-PasswordReminder.ps1 -demo

As we see in the example screenshot, Claudia’s password expires in 5 days, and the password policy that applies to her requires the password to be changed every 42 days. If we run the script normally, Claudia will receive the email reminder since it’s within the 14 day window defined in the script.

To run the script normally (non-demo mode), manually, just omit the -demo. There is no output to the screen when run normally, as the script is designed to be run as a scheduled task.

Once you’re satisfied that the script is running correctly, we can set it to run as a scheduled task. I have a blog post Running PowerShell scripts via Scheduled Tasks that details everything. In my production environment, it runs at 6am each day.

One of the hardest parts was getting a decently formatted email that looked good. This could take some trial and error, and the original script didn’t really have a way built in to preview what the end user would see. As a result, some hapless users would be flooded with your “test” messages. I fixed that by creating a preview mode. Manually run the script with the preview switch, and a user to send the email to. For example

.\New-PasswordReminder.ps1 -Preview -PreviewUser bgates

This will send an email to the user, bgates. The email is formatted for a password that expires in one day, so the user gets the additional banner near the top as well.

Next up was creating a scheduled task. Not really terribly difficult to do manually, but I could see where it might take some trial and error. So, I added the install switch, which will create a scheduled task for the script, configuring it to run at 6am each day. Of course, that time can be manually adjusted by opening the scheduled task once it’s created. The install mode will ask for credentials to run the scheduled task under. Install it as so:

.\New-PasswordReminder.ps1 -Install

Note: The scheduled task is configured to point to where the script is when you run the install switch. So don’t move it later!

To send an email that does not contain the images or their related formatting, specify $NoImages when running the script. This will send essentially an HTML formatted text email.

Next up, I added some simple logging to the application event log. The script will write a single entry when it starts, and a single entry when it finishes, noting how many users were processed (sent an email). I would love to hear how this script works in large environments. If you’re willing, please let me know (via comments below) how long it’s taking to run in your environment, and the number of users in AD.

Please send me your suggestions!

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.

Syntax

New-PasswordReminder.ps1 [-Demo] [-Install] [[-PreviewUser] ] [-NoImages] [-WhatIf] [-Confirm] []

Demo Runs the script in demo mode. Demo mode displays users who are expiring soon, but does not send them the reminder email.

Install Creates a scheduled task to run the script automatically every day at 6:00am

PreviewUser
Defines the user to send the preview email to.

NoImages
Specifies that a HTML text only message should be sent instead of one that contains the fancy formatting.

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.

In addition to the info listed above:

If you leave the following parameters blank, the related text will be removed from the email sent to users: $HelpDeskURL. This will get expanded in the future.

You can change the format of the date displayed in the email by changing the value of $DateFormat. The default is “d”, which yields a date such as 09/07/2012 (MM/dd/yyyy). If you’d like the European style, use “MM/dd/yyyy” instead.

Frequently Asked Questions

Question: Does this work with Exchange Server 2013

Answer: Yes

Download

v2.9 – 09-13-2013 New-ADPasswordReminder.v2.9.zip

v2.8 – 05-03-2013 New-ADPasswordReminder.v2.8.zip

v2.7 New-PasswordReminder.v2.7.zip

v2.6 New-PasswordReminder.v2.6.zip

v2.4 New-PasswordReminder.v2.4.zip

New-PasswordReminder.zip

ScriptImages.zip – image files used in emails

Changelog

See the changelog for this script which details all versions and their features.

Script: Get-CsConnections.ps1 – See User Connections, Client Versions, Load Balancing in Lync & Skype for Business Server

August 11th, 2011 190 comments

Lync 2013 logo 128x128Tracy A. Cerise and Mahmoud Badran came up with a script to show Lync connections, and the users connected. This was quite informative as it could be used to show load balance distribution, client versions being used, and more.

I took the script and updated it a little, including:

  1. Removed the help function and the header block and inserted comment based help. So a user can run get-help Get-CsConnections.ps1 and get the help, just like any other script and cmdlet.
  2. Added a parameter to display the user list. My needs didn’t require the user list – just the statistics at the beginning. So I added the feature to show the user list by running Get-CsConnections.ps1 -IncludeUsers.
  3. Added a couple of functions, including one that cleans up some variables when exiting.
  4. Adjusted some of the formatting. I noticed things didn’t always line up when the server FQDNs were really long, like those in child domains.
  5. Did a prereq check to verify the Lync module is loaded. If not, it gets loaded. That way, the script will still run fine if it’s run from an ordinary PowerShell console.
  6. Accounted for the pool parameter being just a NetBIOS name by adding the $env:userdnsdomain to the NetBIOS name to create the FQDN. This appears to work fine if the Lync servers and user running the script are both in the same domain. If not, then an FQDN would be required.
  7. Renamed the script to Get-CsConnections.ps1 and some of the functions to the normal verb-noun format.
  8. Added a feature to show just a specific client version number, and the users connected with that client version. This can help you determine who is connecting with what versions, which is helpful when looking into licensing, upgrades, etc.
  9. Added support for Lync Server 2013, which uses a different query than Lync Server 2010.
  10. Tons more info in updates and releases following that. See the changelog for more info.

Syntax

Get-CsConnections.ps1 [[-Pool] ] [[-SIPAddress] ] [[-FilePath] ] [-IncludeUsers] [-IncludeHighUsers] [-IncludeSystem] [[-UserHighConnectionFlag] ] [[-ClientVersion] ] [-ShowFullClient] [ShowTotal] [[-Server] ] [-WhatIf] [-Confirm] []

Run the script specifying the front end pool or server to target:

Get-CsConnections.ps1 -Pool [pool FQDN]

or

Get-CsConnections.ps1 -Server [server FQDN]

The script automatically determines the version (2010 or 2013) of the pool, and uses the correct query.

If I can find an auto-detect method for server versions, I’ll include that in a later build.

Will show you unique client versions, their user agent, and the number of connections for each:

Current connected users listed by client name/version

Current connected users listed by client name/version

Distribution of connections across frontend servers (load balancing):

Connections by server (load balancing)

Connections by server (load balancing)

The number of unique users and clients connected:

Total unique users and clients

Total unique users and clients

And, adding the -IncludeUsers switch, such as:

Get-CsConnections.ps1 -Pool [pool FQDN] -IncludeUsers

will also show the users who are connected, and the number of connections they each have:

Connections per user

Connections per user

Using -IncludeHighUsers instead of -IncludeUsers will only list those users who meet the UserHighConnectionFlag (shown in white) or exceed the UserHighConnectionFlag (shown in red).

Get-CsConnections.ps1 -SipAddress [sip address] -Pool [pool FQDN]

Will show you the information for a single user:

Connection info for a specific user

Connection info for a specific user

Get-CsConnections.ps1 -Pool [pool FQDN] -ClientVersion [version number]

Will show the connection data for just that version number, including listing the users connected with that client version. This is helpful if the first method lists some version numbers you’d like to track down. Here, I used a client version of 13.1. MC/13.1.x is the OCS client on the Mac.

Connections by client version

Connections by client version

Using the -ShowFullClient option will show extended info for client name/version. However, the previous ‘Client Version’ column is not shown due to formatting restrictions. Here we can see more info, especially about mobile devices, Lync Phone Edition, and Mac clients.

Show Full Client extended info

Show Full Client extended info

Using -ShowTotal will also add additional info to the bottom section, including total number of users who are Lync enabled, total who are voice enabled, and percentage of total Lync enabled users who are connected.

ShowTotal option with totals and percentage

ShowTotal option with totals and percentage

You can export the info to a .csv file for viewing/manipulation in Excel using:

Get-CsConnections.ps1 -Pool [pool FQDN] -FilePath [path to csv file]

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.

NOTE: In order to gain remote access to each Front End server’s RTCLOCAL database where connection information is found, you need to open two local firewall ports; one static UDP port (1434), and one dynamic TCP port. We can use netsh to open the two required ports. First, open an elevated command prompt, and paste the following line. You should get “Ok.” in return:

netsh advfirewall firewall add rule name="SQL Browser (UDP 1434)" dir=in action=allow protocol=UDP localport=1434 profile=domain description="Created for Get-CsConnections.ps1. For more information, see https://www.ucunleashed.com/269"

Next, find the dynamically assigned port used by the Named Instance (RTCLOCAL):

  • On the Front End server, open SQL Server Configuration Manager.
  • Expand SQL Server Network Configuration.
  • Click on Protocols for RTCLOCAL.
  • On the right side, right click on TCP/IP, and choose Properties.
  • Click on the IP Addresses tab.
  • Scroll to the last section, called IPAll.
  • Note the TCP Dynamic Ports value

IPAll

Replace [dynamic port] in the code below with the dynamic port number, and run the entire following command:

netsh advfirewall firewall add rule name="SQL RTCLOCAL Dynamic Port (tcp-in)" dir=in action=allow protocol=TCP localport=[dynamic port] profile=domain description="Created for Get-CsConnections.ps1. For more information, see https://www.ucunleashed.com/269"

If you look at the inbound rules for the firewall, you’ll now see the two new rules:

fwrules

Repeat the process for both ports on each Front End server.

Note: The dynamically assigned port is unique to each Front End server, not the pool. So you’ll need find the value on each server. Once the two ports are open on each Front End server in the pool, the script should work fine.

Thanks to James Cussen for explaining what config is needed for SQL access through the firewall.

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

v3.5 – 08-14-2019 – Get-CsConnections.v3.5.zip

v3.4 – 03-27-2018 – Get-CsConnections.v3.4.zip

v3.3 – 01-0-2017 – Get-CsConnections.v3.3.zip

v3.2 – 11-17-2016 – Get-CsConnections.v3.2.zip

v3.1 – 04-07-2016 – Get-CsConnections.v3.1.zip

v3.0 – 09-09-2015 – Get-CsConnections.v3.0.zip

v2.9 – 10-28-2014 – Get-CsConnections.v2.9.zip

v2.8 – 06-10-2014 – Get-CsConnections.v2.8.zip

v2.7 – 05-24-2014 – Get-CsConnections.v2.7.zip

v2.6 – 02-08-2014 – Get-CsConnections.v2.6.zip

v2.5 – 11-26-2013 – Get-CsConnections.v2.5.zip

v2.4 – 09-13-2013 – Get-CsConnections.v2.4.zip

v2.3 – 08-01-2013 – Get-CsConnections.v2.3.zip

v2.2 – 05-10-2013 – Get-CsConnections.v2.2.zip

v2.1 – 12-13-2012 – Get-CsConnections.v2.1.zip

v2.0 – 10-16-2012 – Get-CsConnections.v2.0.zip

v1.9 – 09-21-2012 – Get-CsConnections.v1.9.zip

v1.8 – 09-14-2012 – Get-CsConnections.v1.8.zip

v1.7 – Get-CsConnections.v1.7.zip

v1.6 – Get-CsConnections.v1.6.zip

v1.4 – Get-CsConnections.v1.4.zip

v1.3 – Get-CsConnections.zip

v1.0 – Get-CsConnections.zip

Changelog

See the changelog for a complete list of features added in each release

Script: New-LoopbackAdapter.ps1 – Using PowerShell to Create and Configure a Loopback Adapter for Use with Direct Server Return Load Balancing in Exchange 2010 and Lync 2010

July 29th, 2011 1 comment

Using a hardware load balancer is very common to spread workload across multiple servers, as well as direct traffic to surviving servers when one goes down or is being patched. When using an HLB, there are several methods that can be used. The more conventional SNAT is quite popular, but another method, Direct Server Return, or DSR, provides for substantially increased performance. This can be key in environments where a lot of traffic from Exchange and/or Lync is going through the HLBs, or where the same HLBs are being used for many apps. Update: Tom Pacyk and John Cook pointed out that I neglected to mention that DSR based HLB is not supported on Lync Edge servers (and it won’t even work based on my testing). See Microsoft’s support statement at the bottom of http://technet.microsoft.com/en-us/library/gg425779.aspx. Sorry about the omission, guys!

The difference from a server side perspective, is that to use the DSR method, extra configuration is required when building the server. To configure a server for DSR based load balancing a loopback adapter is added and configured with the IP address of the load balancer VIP. This allows the server to accept packets targeted for the VIP address. Without it, the server would ignore them.

  • Add the Microsoft Loopback adapter
  • Unbind all services and protocols except IPv4
  • Rename the loopback adapter to “loopback” and rename the regular NIC to “net”
  • Assign an IP address and subnet mask (but no gateway)
  • Configure the adapter to not register its address in DNS
  • Set the adapter’s metric to 254
  • Adjust the adapter bindings so that it is lower than the normal NIC in the server
  • Set WeakHostSend and WeakHostReceive settings on both adapters

While adding and configuring the loopback adapter is simple enough, it’s a list of steps that is ideally suited for PowerShell – especially if you build a fair number of servers. Unfortunately, there is no straightforward methods to accomplish all of the required tasks. In order to configure the adapter bindings, a separate file must be downloaded. In order to even add the loopback adapter, yet another file needs to be downloaded. It’s almost more grief to try it through PowerShell than to just manually do it, right? Not so fast!

PowerShell is quite powerful. We can download the required files, unzip them if needed, and then use them as required to fulfill our needs. Of course, this assumes that the server has Internet connectivity.

Run the script one of two ways. Run just the script itself by typing

.\New-LoopbackAdapter.ps1

and the script will prompt you for the VIP IP and subnet mask.

Or, type

.\New-LoopbackAdapter.ps1 -vipip [ip address] -vipsm [subnet mask]

for example

.\New-LoopbackAdapter.ps1 -vipip 10.1.0.100 -vipsm 255.255.255.0

As with most of my scripts, tab completion for the parameters works, and there is a full help section by typing:

Get-Help .\New-LoopbackAdapter.ps1

Once the script completes, Explorer will open to the network adapters section so that you can verify the various steps have completed:

No services or protocols other than IPv4 bound to the loopback adapter:

IP address and subnet mask configured:

Metric set to 254:

DNS registration unchecked:

Loopback adapter at the bottom of the adapter bindings:

The script also creates a full transcript in the same folder as the script.

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

New-LoopbackAdapter.zip

Script: Set-Lync2010Features.ps1 – Automated Prereq PowerShell Script for Lync Server 2010

July 25th, 2011 14 comments

If you’ve followed this blog for a while, you may remember one of the more popular posts, Automated prerequisite installation via PowerShell for Exchange Server 2010 on Windows Server 2008 R2. In that script, I used some simple techniques to help take some of the boring tasks out of installing Exchange 2010 servers. It was updated several times, and a new update is coming soon.

Well, Lync Server MVP Stale Hansen took that script and adapted it to installing the prerequisites for Lync Server 2010 and posted the results online. He did some neat things like installing the Best Practice Analyzer and the Resource Kit.

I took the script and added a bunch of functionality, including prerequisites for all Lync related roles, some added third-party tools such as the cool “Find Lync Versions” tool from Stephen Olsen, aka “Stumper”, was added as an option. Note: This automatically installs the .Net 4 framework.

I also added the ability to download some files that are needed – essentially automating some routine tasks. See the changelog file linked below for features added in each version.

For those of you doing Lync voice work, I haven’t done any deployments yet, so if there are tasks you’d like me to automate, please let me know and I can get them added when I have time.

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

v5.3: Set-Lync2010Features.v5.3.zip

v5.2: Set-Lync2010Features.v5.2.zip

v5.1: Set-Lync2010Features.v5.1.zip

v5.0: Set-Lync2010Features.zip.

Changelog

See the changelog for this script for all version information

Script: Set-WindowsEmailAddress.ps1 – Set WindowsEmailAddress Field for Users in a Non-Exchange Environment

June 12th, 2011 No comments

Windows-logo-128x128A friend of mine had a dilemma. Her environment doesn’t have Exchange, but does has some LDAP apps that query AD. They needed to set the WindowsEmailAddress field on all AD user accounts to first.last@domain.com. In Exchange, this is automatically set when a user is mail or mailbox enabled. No such luck here (and not for a lack of me trying!). And, since there are hundreds and hundreds of users, manually setting each user was a task best left for those with no life. So – PowerShell to the rescue.

In the ActiveDirectory module, available on Domain Controllers, we have both Get-AdUser and Set-AdUser. These are really the only two cmdlets we need to fill the requirement. We essentially use Get-AdUser to get all of the users in a specific OU:

[object] $objUsers = Get-AdUser -filter * -ResultSetSize $null | Where-Object {$_.DistinguishedName -match $ou -and $_.GivenName -ne $null -and $_.Surname -ne $null}

$ou is defined as the target OU containing all users we want to process. The script will include all children OUs as well. Not in this OU are service accounts and non email enabled accounts. So we have an array with users. We’re 1/2 way there. We then cycle through the array using a ForEach and set the email address by combining the first name, a “.”, the second name, an “@”, and the SMTP domain. But I wanted to do some simple cleanup of the addresses. First concern was people with multiple names in either the first name or last name fields. Think “Billy Ray”.  This was simple enough using a simple replace:

$strEmail = ($objUser.GivenName).replace(" ","") + "." + ($objUser.Surname).replace(" ","") + "@" + $SMTPDomainName

The second concern was consistent formatting of case. I’m a big believer that Capitalizing each word in an email or URL address (aka “CamelCase”) makes them easier to read quickly. Doing some digging around, I found Get-Culture.TextInfo.ToTitleCase. This will capitalize each word, so “billy ray cyrus” becomes “Billy Ray Cyrus”. Unfortunately, “BILLY RAY CYRUS” stays the same. A little ToLower() magic and poof – perfection:

$strEmail = ((Get-Culture).TextInfo.ToTitleCase($objUser.GivenName.ToLower()).replace(" ","")) + "." + ((Get-Culture).TextInfo.ToTitleCase($objUser.Surname.ToLower()).replace(" ","")) + "@" + $strSMTPDomainName

So we’ve got the users, and we’ve figured out how to create the email address. Lastly, we actually make the change. Within our loop, we simply run

Set-AdUser $objUser.SamAccountName -emailaddress $strEmail

Results in just a few lines!

Now, my friend hasn’t embraced the PowerShell way of life yet, so I figured I’d spruce things up a little. Rather than have to manually type the OU path, I found a post that showed how to create a GUI based OU picker. It got me about 3/4 the way there, and added ~100 lines to the script. I added the “OK” button and dumped the results back into the script. I added a prompt for the SMTP domain name as well. The script now adds a transcript for a record of what was done and some code to ensure the ActiveDirectory module is available and loaded (just in case the script is run from a normal PowerShell session and not the Active Directory Module for Windows PowerShell session).

I didn’t really add any error checking, as this is a one-off script meant to just save a little time.

if this is the first script you’re running, then

.\Set-WindowsEmailAddress.ps1 -SMTPDomainName [SMTP domain]

to execute the script. Follow the prompts. Pretty simple stuff.

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.2 – 01-27-2014 Set-WindowsEmailAddress.v1.2.zip

v1.1 – 10-01-2011 Set-WindowsEmailAddress.v1.1.zip

v1.0 – 06-12-2011 Set-WindowsEmailAddress.zip

Changelog

See the changelog for this script which details all versions and their features.

Categories: PowerShell Tags: ,

[Redirect] Automated Prerequisite Installation via PowerShell for Lync Server 2010 on Windows Server 2008 R2

April 27th, 2011 No comments

Update: A newer version of this script has been released, and is available at Set-Lync2010Features.ps1 v5.0 – automated prereq PowerShell script for Lync Server 2010 released

If you’ve followed this blog for a while, you may remember one of the more popular posts, Automated prerequisite installation via PowerShell for Exchange Server 2010 on Windows Server 2008 R2. In that script, I used some simple techniques to help take some of the boring tasks out of installing Exchange 2010 servers. It was updated several times, and a new update is coming soon.

Well, Lync Server MVP Stale Hansen took that script and adapted it to installing the prerequisites for Lync Server 2010 and posted the results online. He did some neat things like installing the Best Practice Analyzer and the Resource Kit.

I got tasked with Lync responsibilities on my current project, so I took a closer look at the Stale’s version of the script, and decided to add some more code. Here are some of the changes:

  1. The script now prompts you about installing the telnet client. The telnet client isn’t REQUIRED, but is often recommended. Since my current project is in a high security environment, installing anything that isn’t absolutely required is generally frowned upon. So I made it optional.
  2. I added the option to disable IPv6. Lync Server 2010 doesn’t make use of IPv6 at all, so this option might prove handy.
  3. I added the option to install the Lync Server 2010 Stress and Performance Tool, the Visual C++ 2008 Redistributable that the Lync installer complains about, and options to install the recently released IM and Expert feature as well as starting Windows Update. Note that the IM an Expert piece is still early in testing. Note also that the Visual C++ install should be done after rebooting the server, otherwise it throws errors.
  4. Behind the scenes, I cleaned up a LOT of code – something I’ve been meaning to do to the Exchange prereq script as well. The script now looks to see if something is installed before trying to install it. The Resource Kit and BPA both have documentation that is MS Word based, so the script will ask you if you want to automatically install the free MS Word viewer and related filters.
  5. The OS detection method was cleaned up, and now supports any version of 2008 R2, including RTM and various service packs.

I’ve built 6 production servers and haven’t noticed any issues yet. But, as always, use at your own risk.

The code can be downloaded at Set-Lync2010Windows2008R2Features.zip. Rights required are local server admin. Also, set your execution policy to unrestricted in order to be able to run this unsigned script.

I welcome any comments, suggestions, concerns.

Script: Add-Cmdlets2Dictionary.ps1 – Adding PowerShell and Exchange Cmdlets to the Office Dictionary

May 12th, 2010 1 comment

I write a LOT of things that include technical terms, including email, book chapters, and client documentation. Of course, being the good soldier that I am, I try to spell and grammar check things so that the communications don’t make me look like a blabbering idiot.

The problem is that I’m constantly adding terms to the dictionary as I come across them. This include a ton of PowerShell, and usually, Exchange specific cmdlets. When I switched to a new workstation a week ago, I figured it was time to make life earlier. I mentioned online that there had to be a way to easily take a full list of PowerShell cmdlets and functions and dump them into the dictionary. Fellow Exchange MVP Michael B. Smith said it should be easy in PowerShell, and could be done in a single line of code. So we started to collaborate via Facebook (of all places!) on a solution. Michael posted the solution at Simplifying Life for Exchange Authors. I recommend you read his post for a way to do this painlessly. Thanks to Michael for indulging me.

After we came up with working code, it dawned on my that not everyone has remote PowerShell sessions into Exchange servers from their workstations. So I figured I’d make life a little easier, and compiled some various pieces together into a script. The script below will get a list of all Exchange servers in the same AD site as your workstation, randomly pick one, and establish a remote PowerShell session to it. Once established, it will dump all PowerShell functions and cmdlets to the Office dictionary. Copy the following code into Notepad on your workstation:

function Get-ExchangeServerInSite {
    $ADSite = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]
    $siteDN = $ADSite::GetComputerSite().GetDirectoryEntry().distinguishedName
    $configNC=([ADSI]"LDAP://RootDse").configurationNamingContext
    $search = new-object DirectoryServices.DirectorySearcher([ADSI]"LDAP://$configNC")
    $objectClass = "objectClass=msExchExchangeServer"
    $version = "versionNumber>=1937801568"
    $site = "msExchServerSite=$siteDN"
    $search.Filter = "(&($objectClass)($version)($site))"
    $search.PageSize=1000
    [void] $search.PropertiesToLoad.Add("name")
    [void] $search.PropertiesToLoad.Add("msexchcurrentserverroles")
    [void] $search.PropertiesToLoad.Add("networkaddress")
    $search.FindAll() | %{
        New-Object PSObject -Property @{
            Name = $_.Properties.name[0]
            FQDN = $_.Properties.networkaddress |
                %{if ($_ -match "ncacn_ip_tcp") {$_.split(":")[1]}}
            Roles = $_.Properties.msexchcurrentserverroles[0]
        }
    }
}

#add all servers in the local site to an array
$servers = New-Object System.Collections.ArrayList
Get-ExchangeServerInSite | %{ [void]$servers.Add(($_.fqdn)) }

#select a random server from the current site
if($servers.count -gt 1) {
    $random = Get-Random -Minimum 0 -Maximum $servers.count
    $fqdn = $servers[$random]
}
else {
    $fqdn = $servers[0]
}

#create the session
$session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$fqdn/PowerShell/" -Authentication Kerberos
#import the session
Import-PSSession $session
$dicFile = (Get-Content Env:AppData) + "\Microsoft\UProof\CUSTOM.DIC"
Get-Command -module $global:importresults | Where-Object {$_.CommandType -eq "cmdlet" -or $_.CommandType -eq "function" -and $_.Name -notmatch ":"} | select-Object Name | Out-file -append $dicfile
#kill the session
Remove-PSSession $session

Save it as Add-Cmdlets2Dictionary.ps1. Now, open PowerShell, navigate to where you saved the file, and execute it using .\Add-Cmdlets2Dictionary.ps1. Once it’s finished, open Word or Outlook and test it with some random cmdlets like New-Mailbox. Keep in mind that Michael’s one-liner works great if you’ve already got a remote session into an Exchange server. My script above is in case you don’t, or you want a reusable script to share with colleagues.

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.