Script Template Add-on for PowerGUI Script Editor
As a best practice, most script authors adds a header to their scripts containing some basic information like the script name, the author and version history.
Some commercial script editors, like Idera`s PowerShell Plus, contains script-templates to make this easier. PowerGUI is another commercial editor, available both in a free and Pro version. This product doesn`t contain any script templates by default, however it`s easy to extend the product with needed functionality by building Add-ons. I`ve created an add-on named Script Template, available here.
When creating a new document in the PowerGUI Script editor, the following template is automatically inserted when the Add-on is loaded:
The easiest way to install the Add-on is by using Tools->Find Add-ons Online:
Search for “script template” and then press “Install”.
If you want to customize the template you can open the file \Documents\WindowsPowerShell\Modules\Add-on.ScriptTemplate\Add-on.ScriptTemplate.psm1 and modify the following section:
|
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 |
$template = @"
########################################################################### # # NAME: # # AUTHOR: $env:USERNAME # # COMMENT: # # VERSION HISTORY: # 1.0 $((get-date).ToShortDateString()) – Initial release # ########################################################################### "@ |
Note that the first new document automatically created when opening the PowerGUI Script Editor won`t get pre-filled with the script template header, since the Script Template Add-on isn`t loaded yet. However, all consecutive new documents will get the template automatically.
A similar Add-on is available here for PowerShell ISE, created by PowerShell MVP Ravikanth Chaganti.
If you`re interested in creating your own Add-ons for the PowerGUI Script Editor, have a look at this excellent tutorial created by Kirk Munro and Dmitry Sotnikov.
DFS-R Health Report for SYSVOL
Distributed File System Replication (DFS-R) was introduced as a replacement for File Replication Service (FRS) in Windows Server 2008, and was further enhanced in Windows Server 2008 R2. When your domain functional level are set to Windows Server 2008, you have the option to migrate SYSVOL-replication from the deprecated FRS to the new and more reliable DFS-R service. A major advantage of using DFS-R over FRS is that FRS copies the whole file when a change are made, while DFS-R only copies the changed bits. This and further details are discussed here. I`ve also included some links in the resource section below on how to perform an FRS to DFS-R migration.
Included in the Windows Server 2008 and Windows Server 2008 R2 are the DFS Management-console as well as several command-line tools for administering DFS. A great built-in feature in these tools is the diagnostic reports.
This is available in the DFS Management-console:
As well as from the DfsrAdmin.exe command-line tool:
Using this feature we can generate an HTML-report containing a great overview of the replication health for the SYSVOL replication group:
Any errors and warnings will be shown with detailed explanations. In addition we can view general information and statistics i.e. regarding free diskspace on the domain controllers, bandwith savings and so on (click on the thumbnail to view):
Since the reporting feature are available from the DfsrAdmin.exe command-line tool, it makes it easy to set up a script as a scheduled task that also sends the generated report via e-mail i.e. every morning. I`ve published a simple PowerShell-script to accomplish this which is available here.
Resources
SYSVOL Replication Migration Guide: FRS to DFS Replication (Word-version available here)
Verifying File Replication during the Windows Server 2008 DFSR SYSVOL Migration
Configuring DFSR to a Static Port
FRS to DFSR Migration Tool (not for SYSVOL migration)
Retrieve number of mailboxes per database in Exchange Server 2010
Mailbox databases in Exchange Server 2010 doesn`t contain any information regarding the numbers of mailboxes stored in them. A common approach to retrieve this information is using the Exchange Management Shell.
Two examples
1: #Example 1
2: (Get-MailboxDatabase) | ForEach-Object {Write-Host $_.Name (Get-Mailbox -Database $_.Name).Count}
1: #Example 2
2: (Get-MailboxDatabase) | Select-Object Name,@{Name="Number of users";Expression={(Get-Mailbox -Database $_.name).Count}}
Either of these works fine if you want to get the number of mailboxes quick and dirty. However, in larger environments, these one-liners may run for a while.
I did a quick measurement using the Measure-Command cmdlet; Example 1 ran for 36 seconds and example 2 ran for 30 seconds. The environment I ran this in got 5 mailbox databases and approximately 1300 mailboxes.
When running these as one-liners from the Exchange Management Shell, or as part of a scheduled task, the performance might not be an issue.
A colleague of mine was using the Cmdlet Extension Scripting Agent to provision new mailboxes to the mailbox database with the least number of mailboxes in it. In this scenario the performance is key. To achieve better performance, we used an LDAP-query using System.DirectoryServices.DirectorySearcher.
Get-MDBMailboxCount function
I created a PowerShell function to retrieve the number of mailboxes per mailbox database using the DN of a mailbox database as an argument.
function Get-MDBMailboxCount ([string]$DN) {
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://$(([system.directoryservices.activedirectory.domain]::GetCurrentDomain()).Name)")
$Searcher.Filter = "(&(objectClass=user)(homeMDB=$DN))"
$Searcher.PageSize = 10000
$Searcher.SearchScope = "Subtree"
$results = $Searcher.FindAll()
$returnValue = $results.Count
#dispose of the search and results properly to avoid a memory leak
$Searcher.Dispose()
$results.Dispose()
return $returnValue
}
A problem we stumbled upon was that wildcards didn`t seem to work on the homeMDB-attribute, so we couldn`t use *Mailbox Database Name* for this value.
When looking up on MSDN, it turned out that wildcards are not supported on LDAP DN`s:
For queries that include attributes of DN syntax in a filter, specify full distinguished names. Wildcards (for example, cn=John*) are not supported.
That`s the reason for the function taking DN as an argument, and not the name of a mailbox database.
The function runs the query against the current domain, and hence you don`t need to specify a domain for the LDAP-query.
Back to the performance part, I now ran Measure-Command against the following one-liner:
1: (Get-MailboxDatabase) | ForEach-Object {Write-Host $_.Name (Get-MDBMailboxCount -DN $_.DistinguishedName)}
This time the performance was 3,7 seconds, almost 10x faster. Actually, using a foreach-loop instead of Foreach-Object also makes it slightly faster; 3,5 seconds. You can read up on the difference between these two approaches here.
Get-MDBMailboxCount script
Having the Get-MDBMailboxCount function, I also decided to create a script using this function, which generates a CSV-file.
The script uses a foreach-loop to go through each database in the Exchange-organization, and then creates a custom object for each database containing the name and number of mailboxes. The custom objects are added to an array which can be used for other things like determining the smallest/largest database. You could also use this information to generate graphs like I showed in this blog-post.
Note that I`ve only tested the above against Exchange Server 2010. I suppose it also should work against Exchange Server 2007, if someone can verify this it would be nice if you could leave a comment below.
Update 04.08.2011: The function has been updated to avoid a memory leak when the function is called multiple times. Thanks to Weston McNamee for the tip (see his comment below).
Generate an installation-report for specific hotfixes using Windows PowerShell
Windows PowerShell 2.0 contains a cmdlet called Get-HotFix, which retrieves installed hotfixes from the local computer or specified remote computers.
This is quite useful when you need to check if a particular hotfix is installed prior to installing new software that requires a specific set of hotfixes, or when a critical security hotfix is released and you want to make sure the hotfix is installed.
The Get-HotFix cmdlet retrieves all hotfixes installed by Component-Based Servicing. If you would like to know more about CBS, I would recommend the article Understanding Component-Based Servicing on the Windows Server Performance Team`s blog.
When you need to check several computers, and maybe also check for several hotfixes, it might be a time consuming process to run Get-HotFix against each computer and also keep track of the status for each computer/hotfix. To ease this scenario, I`ve written a script named Get-HotFixReport.ps1.
Get-HotFixReport.ps1 overview
- Retrieves the computers to run Get-HotFix against using the Get-ADComputer cmdlet, which is available in Microsoft`s PowerShell-module for Active Directory
- Loops through each computer and creates a custom object for every hotfix, containing information about the installation of the current hotfix for the current computer. Any error that occurs is also stored in the custom object. Get-HotFix are only run if the current computer responds to a ping request.
- Every custom object are added to an array, which at last are exported to a csv-file.
The following three variables must be customized before running the script:
1: $Computers = Get-ADComputer -Filter * -Properties Name,Operatingsystem | Where-Object {$_.Operatingsystem -like "*server*"} | Select-Object Name
2: $HotFix = "KB979744,KB979744,KB983440,KB979099,KB982867,KB977020"
3: $CsvFilePath = "C:\temp\$hotfix.csv"
The example above retrieves all computer objects in Active Directory running a Windows Server operating system. You may specify only one computer, or a secific Organizational Unit, if required.
In the HotFix-variable you need to specify one or more hotfixes to check for. If more than one are specified, they must be separated with a comma. The hotfix-variable in the example above contains all hotfixes that are the prerequisites to install Exchange Server 2010 SP1 on Windows Server 2008 R2.
The csv-file may be opened in Microsoft Office Excel where it`s possible to apply filters to sort on e.g. “true” in the HotfixInstalled-column:
Note that the Get-HotFix cmdlet actually leverages the Win32_QuickFixEngineering WMI-class, and thus, it would be possible to run the script under Windows PowerShell 1.0 if you replace Get-HotFix with Get-WmiObject -Class Win32_QuickFixEngineering.
Of course you might also choose other ways to retrieve the computer-list, e.g. a csv-file, a txt-file or by using Quest`s PowerShell commands for Active Directory.
Export BitLocker-information using Windows PowerShell
Active Directory can be used to store both Windows BitLocker Drive Encryption recovery information and Trusted Platform Module (TPM) owner information.
On the Microsoft Windows Support site, the following information are provided:
Storage of BitLocker Recovery Information in Active Directory
BitLocker recovery information is stored in a child object of a computer object in Active Directory. That is, the computer object is the container for the BitLocker recovery object.
More than one BitLocker recovery object can exist for each computer object, because there can be more than one recovery password associated with a BitLocker-enabled volume.
Each BitLocker recovery object on a BitLocker-enabled volume has a unique name and contains a globally unique identifier (GUID) for the recovery password.
The name of the BitLocker recovery object is limited to 64 characters because of Active Directory constraints. This name incorporates the recovery password GUID as well as date and time information. The form is:
<Object Creation Date and Time><Recovery Password GUID>
For example:
2005-09-30T17:08:23-08:00{063EA4E1-220C-4293-BA01-4754620A96E7}
The Active Directory common name (cn) for the BitLocker recovery object is ms-FVE-RecoveryInformation and includes attributes such as ms-FVE-RecoveryPassword and ms-FVE-RecoveryGuid.
Storage of TPM Recovery Information in Active Directory
There is only one TPM owner password per computer; therefore the hash of the TPM owner password is stored as an attribute of the computer object in Active Directory. It is stored in Unicode. The attribute has the common name (cn) of ms-TPM-OwnerInformation.
Active Directory Requirements
In order to store BitLocker and TPM information in Active Directory, all domain controllers must run Windows Server 2003 with Service Pack 1 or later. Schema extensions will also need to be installed on servers running Windows Server 2003.
To see if a computer has stored any BitLocker Recovery information in Active Directory, you must install the BitLocker Recovery Password Viewer and check the BitLocker Recovery tab on the computer object to see if a Recovery Password are present:
Doing this for every computer manually isn`t an option in a domain environment. To ease this task I`ve written a PowerShell-script, available here, that will generate a CSV-file containing all Windows Vista and Windows 7 computer objects in the domain. The CSV-file will contain the following information:
- Computername
- OperatingSystem
- HasBitlockerRecoveryKey
- HasTPM-OwnerInformation
I haven`t found a way to retrieve ms-FVE-RecoveryInformation objects or msTPM-OwnerInformation information on computer objects using Microsoft`s PowerShell-module for Active Directory. Because of that I`ve leveraged Quest`s free PowerShell Commands for Active Directory.
To retrieve correct information, you must run the script with a user that has been granted the following permission: Read-permission on msFVE-RecoveryInformation objects and Read-permissions on msTPM-OwnerInformation on computer-objects (e.g. Domain Admins).
When the CSV-file is generated, you can use the “Text to columns”-feature in Microsoft Office Excel and save the document as an Excel spreadsheet. Then you can apply filters to sort on e.g. HasBitlockerRecoveryKey or HasTPM-OwnerInformation.
If you`re using the BitLocker feature on other operatingsystems than Windows Vista or Windows 7, i.e. Windows Server 2008 or Windows Server 2008 R2, you may customize the filtering in the computers-variable.
BitLocker resources on Microsoft TechNet
BitLocker Drive Encryption Overview
Backing Up BitLocker and TPM Recovery Information to Active Directory
Using Cmdlet Extension Agents to customize mailbox provisioning in Exchange Server 2010
Exchange Server 2010 introduced a new feature for automatically assigning a database while provisioning mailboxes, which uses the Mailbox Resources Management cmdlet extension agent. This feature are demonstrated and described in further detail in this blog post by Mike Pfeiffer.
While this new feature is a great enhancement to Exchange Server, it`s actually possible to customize it further by using another cmdlet extension agent named Scripting Agent.
There are several common ways to distribute mailboxes to databases:
- Alphabetically
- Per location
- Per department
- Random
My example will focus on using the alphabetically approach to distribute the mailboxes. I`ve re-used the regular expression switch I wrote in this blog post.
Before we proceed, you might want to read this blog post by Pat Richard on how the Scripting Agent work, as well as reading up on Cmdlet Extension Agents on the Exchange Server TechCenter on Microsoft TechNet.
1. Create a file named ScriptingAgentConfig.xml and save it to the CmdletExtensionAgents-folder which by default are C:\Program Files\Microsoft\Exchange Server\V14\Bin\CmdletExtensionAgents. You can find my sample ScriptingAgentConfig.xml here. You will need to customize the names of the databases, as well as the domain name and the Exchange organization name in the $user.database property. Then you would need to copy the ScriptingAgentConfig.xml to the CmdletExtensionAgents-folder on all Exchange 2010 servers in your organization.
2. From the Exchange Management Shell, run Enable-CmdletExtensionAgent “Scripting Agent”. This will enable the Scripting Agent, which will use the ScriptingAgentConfig.xml file we created.
3. From the Exchange Management Shell, run Disable-CmdletExtensionAgent “Mailbox Resources Management Agent”. The Mailbox Resources Management Agent must be disabled when we want to assign the database for the new mailbox using the Scripting Agent, else the Mailbox Resources Management Agent will take precedence. Alternatively we could have changed the priority of the Scripting Agent.
When the above configuration are in place, it`s time to test the new configuration, which of course should be done in a lab environment prior to putting it to production.
From the Exchange Management Console, create a new mailbox and be sure to not select “Specify the mailbox database rather than using a database automatically selected”:
Alternatively, if using the New-Mailbox cmdlet from the Exchange Management Shell, do not specify the “-Database” parameter.
When the test mailbox are created, verify that the database the new mailbox was assigned to is the expected one. For troubleshooting, I would recommend you to use the “–Verbose” parameter on the New-Mailbox cmdlet.
You can use the very same technique to customize database selection based on i.e. the per department approach, by accessing other properties from $provisioningHandler.UserSpecifiedParameters
Getting an overview of all ActiveSync devices in the Exchange-organization
In Exchange 2007, we can get use the Get-ActiveSyncDeviceStatistics to retrieve statistics for a specific ActiveSync-device. We must supply either a device identity or a mailbox name. In Exchange 2010, the same cmdlet exists, but we also got a new useful cmdlet; Get-ActiveSyncDevice. This cmdlet lists all devices in the organization that have active Microsoft Exchange ActiveSync partnerships.
To provide a way to list all ActiveSync devices in the Exchange organization that works against both Exchange 2007 and Exchange 2010, I`ve created a script named Get-ActiveSyncDeviceInfo.ps1. The script outputs each device as an object, making it easy to work with the results. You might i.e. export all devices to a CSV-file by piping the results to Export-Csv.
You might also create graphs based on the results, like I demonstrated in a previous blog-post.
I`m aware that the script could be more effective, using Get-CASMailbox and a server-side filter to sort out only mailboxes that got the property HasActiveSyncDevicePartnership set to “true”. However, I`ve experienced that this property isn`t reliable. Several mailboxes that does have the HasActiveSyncDevicePartnership set to true doesn`t have an ActiveSync devices associated. You might want to use this approach anyway in larger organizations, although I chose to skip it and rather loop through all mailboxes.
Invoke Best Practices Analyzer on remote servers using PowerShell
Best Practices Analyzer (BPA) is a management tool integrated in Windows Server 2008 R2 used to scan server roles according to Microsoft best practice guidelines.
Included in the initial release for Windows Server 2008 R2 are the following BPA models:
- Active Directory Domain Services
- Active Directory Certificate Services
- Domain Name System (DNS) Server
- Remote Desktop Services
- Web Server (IIS)
Since then several new BPA models are released and available both as separate downloads as well as through Windows Update:
- Active Directory Rights Management Services (AD RMS)
- Application Server
- DHCP
- File Services
- Hyper-V
- Network Policy Server (NPS)
- Windows Server Update Services (WSUS)
At the time of this writing, a BPA model for 12 of 17 server roles in Windows Server 2008 R2 are available.
The 5 that are not available are:
- Active Directory Federation Services (ADFS)
- Active Directory Lightweight Directory Services (AD LDS)
- Fax Server
- Print and Document Services
- Windows Deployment Services
In Server Manager, a BPA summary are available for each installed server role that an BPA Model exists for:
When looking at the properties for an item in BPA you get more information as well as a link to Microsoft TechNet where more information are available for the specific subject:
BPA are built as a PowerShell module, meaning that a PowerShell cmdlet (Invoke-BPAModel) are run in the background when you scan a server role from Server Manager:
This is a great feature to examine if your server roles are configured according to Microsoft`s best practices, however, if you got many servers it will take some time to log on to each server and scan each server role. In addition you don`t get any centralized reporting this way.
Since BPA are based upon Windows PowerShell it`s possible to solve this using the BPA PowerShell module and PowerShell remoting:
I`ve created a sample script to accomplish this, named Invoke-BPAModeling, with the following functionality:
- Invoke BPA for all available server roles on specified remote servers
- E-mail reporting
- File reporting to CSV and HTML
You need to customize the initial variables on the top of the script. You can enable/disable reporting using these variables, as well as specify which servers to work against, SMTP server for e-mail reporting and paths to CSV/HTML reports.
By default, only items with a severity of “Error” and “Warning” are reported. You can change this to also include “Informational” severities by configuring IncludeAllSeverities to true.
On the server running the script from, the Active Directory module for PowerShell must be installed if you want to retrieve computer names from Active Directory. In the sample, the script are configured to retrieve all computer accounts listed with Windows Server 2008 R2 as operating system.
You might choose alternate methods, like importing the computer names from a csv-file.
.
I would recommend that you approve the new BPA models mentioned at the beginning of this blog post in WSUS prior to running the script.
The script requires that PowerShell remoting are enabled and configured on the remote servers. Also note that there is a known issue with the BPA module; When the PowerShell execution policy are set to any other than “Undefined” or “Unrestricted” , an error occurs. I`ll update this blog-post as soon as a fix are provided from Microsoft.
When the script executes, it displays the progress based upon the total number of computers running against:
Sample e-mail report containing both CSV and HTML reports as attachment:
Sample HTML-report:
Sample CSV-report converted to an Excel spreadsheet:
Feel free to customize the script for your needs, as well as suggest improvements.
Resources
Automate Active Directory Migration Tool using Windows PowerShell
Active Directory Migration Tool (ADMT) provides the ability to restructure Active Directory domain structures. It allows you to migrate users, groups and computers between domains, both intra-forest and inter-forest. Features includes password migration, SID migration and security translation among several others.
ADMT was recently released in version 3.2, adding support for Windows Server 2008 R2 and Managed Service Accounts. You can find the download here, and the ADMT migration guide here.
ADMT provides three options on how to use it, where the first and maybe most used is the GUI:
It`s wizard driven and pretty straightforward to use.
The second option is the admt.exe command line utility:
In my opinion this is a pretty good example on how inconsistent various command line tools are compared to PowerShell. Although ADMT is a great tool, hopefully the next version will be rebuilt based on PowerShell.
The third option is scripting. There is a COM-object called ADMT.Migration, and there is a sample VB-script (templatescript.vbs) in the ADMT installation directory (by default C:\Windows\ADMT). This sample script is a good place to start to explore how the COM-object works.
Based on this I`ve written a sample PowerShell script, Invoke-ADMTUserMigration, to migrate user accounts and passwords using Windows PowerShell. This script uses the “ADMTDomain” option, which means that all users from the source OU will be migrated to the target OU. To see other available options, see the ADMT documentation and the sample VBScript. Migrating other objects, like groups and computers, the same way should be pretty easy when comparing Invoke-ADMTUserMigration to templatescript.vbs.
Note that since ADMT is a 32-bit application the script must be run from an x86 instance of Windows PowerShell. It also requires elevated privileges (Run as Administrator). Trying to run it in an x64-bit PowerShell instance or without elevated privileges will result in the following error:
New-Object : Retrieving the COM class factory for component with CLSID {285029CC-5048-4D90-8B38-22D304F513DC} failed du
e to the following error: 80040154.
Before trying to run the script I would recommend that you read the ADMT migration guide and ensures that migration works as expected by running a few test migrations from the GUI.
When this is done, I also would recommend to split the migration in batches as recommended in the migration guide. A maximum of 100 objects at a time might be appropriate. Before moving on to the next batch, check the migration logs (by default in C:\Windows\ADMT\Logs) to see that everything went OK before proceeding.
To take this a step further, I`ve created a function, Migrate-ADMTUser, that migrates a single user object. This would allow you to easily do things like this:
Get-ADUser -Filter {Company -like “Oslo”} | Migrate-ADMTUser
This example combines the Active Directory module for PowerShell with ADMT, allowing us to more fine grained control on what objects to migrate.
Note that both the sample script and sample function I`ve created is meant as a guidance on how to use PowerShell to automate ADMT. The optimal way as I see it would be creating an ADMT PowerShell module with functions for migrate other objects like computers and groups in addition to users and passwords.
Resources
ADMT 3.2: Common Installation Issues
ADMT Guide: Migrating and Restructuring Active Directory Domains (TechNet-version)




