VMware PowerCLI – Set Path Selection Policy on all LUNs for a host

Just a quick script to set the Path Selection Policy on any LUNs on a host that do not have your target policy enabled. The script sets the server to Maintenance mode first, evacuating any VMs if you are in a full DRS automated environment. While this is not strictly necessary, it was required for my production environment just to be safe.

param( [string] $vCenterServer = $(Read-Host -prompt "Enter vCenter Server Name"),
[string] $TargetPolicy = $(Read-Host -Prompt "Enter target policy (RoundRobin, Fixed or MostRecentlyUsed)"),
[string] $TargetHost = $(Read-Host -Prompt "Enter target Host"),
[switch] $WhatIf)

# Add the VI-Snapin if it isn't loaded already
if ((Get-PSSnapin -Name "VMware.VimAutomation.Core" -ErrorAction SilentlyContinue) -eq $null ) {Add-PSSnapin -Name "VMware.VimAutomation.Core"}

Connect-VIServer $vCenterServer | out-null

Write-Host "Connected to: " $vCenterServer -ForegroundColor Green
Write-Host "Target PSP: " $TargetPolicy -ForegroundColor Yellow

switch ($TargetPolicy) {
RoundRobin { $DisplayPolicy = "VMW_PSP_RR"; }
MostRecentlyUsed { $DisplayPolicy = "VMW_PSP_MRU"; }
Fixed { $DisplayPolicy = "VMW_PSP_FIXED"; }
default { Write-Warning "Unknown PSP selected! Please consult the help and try again."; exit }

Write-Host "Setting Policy to"$TargetPolicy" on "$TargetHost -ForegroundColor Green

if($WhatIf) {
$vHost = Get-VMHost -Name $TargetHost
$vHost | Set-VMHost -State Maintenance -Evacuate -WhatIf
$vHost | Get-ScsiLun -LunType "disk" -ErrorAction SilentlyContinue | where {$_.IsLocal -eq $false -and $_.MultipathPolicy -ne $TargetPolicy} | Set-ScsiLun -MultipathPolicy $TargetPolicy -WhatIf
$vHost | Set-VMHost -State Connected -WhatIf
} else {
$vHost = Get-VMHost -Name $TargetHost
Write-Host "Setting "$TargetHost" to Maintenance Mode" -ForegroundColor White
$vHost | Set-VMHost -State Maintenance -Evacuate
$vHost | Get-ScsiLun -LunType "disk" -ErrorAction SilentlyContinue | where {$_.IsLocal -eq $false -and $_.MultipathPolicy -ne $TargetPolicy} | Set-ScsiLun -MultipathPolicy $TargetPolicy
Write-Host "Exiting Maintenance mode on"$TargetHost -ForegroundColor White
$vHost | Set-VMHost -State Connected

SCOM 2007 DFS Backlog Monitoring – Distributing a RunAs account to only DFS replication members

The DFS monitoring tool in SCOM 2007 has some great features, which will replace many a custom VB script running in enterprises. As with a lot of Management Packs, to get the most out of it you need to have a dedicated RunAs account with local admin permissions on the servers you are monitoring (e.g. for the Backlogged Files reporting).

The easy (and wrong) option here is to go with the less secure option and distribute a RunAs account to ALL servers. There are lots of reasons why you wouldn’t want to distribute the credentials to every server in your SCOM installation – but just from a security standpoint, you shouldn’t do it! Selecting the “More Secure” option and distributing credentials only to servers which will require them is a much safer bet.

You can view the members of the DFS discovered inventory in the SCOM Console by going to the “Discovered Inventory” view and changing the target type to “Replication Member” – which is great: you can see all the Servers involved in the DFS replication topology. But there’s no easy way to add these to a RunAs credential to distribute.

To narrow it down to a short list, you can open a Operation Manager Shell prompt and  list any monitoring classes which have “DFS” in the name – there are about 6 or so:

Get-MonitoringClass | where {$_.Name –match “DFS”}

The one that matches my SCOM console view is “Microsoft.Windows.DfsReplication.ReplicationGroupMember” so I want to select all the monitoring-objects that match this discovery and export the “Path” (server name) to a csv file:

Get-MonitoringClass | where {$_.Name –match “Microsoft.Windows.DfsReplication.ReplicationGroupMember”} | get-monitoringobject | select-object Path | export-csv c:\DFS-Members.csv

I’ve not yet figured out how to add these to the RunAs account credential distribution via PowerShell, so I’m afraid it’s a manual process from here. To make it easier I opened the csv in Excel and filtered out duplicates (for servers with multiple DFS shares) before pasting the servers in individually to the distribution dialogue.

Once the RunAs account has been downloaded by the Agents, and if you’ve added it correctly to your “DFS Replication Monitoring Account” profile, you should start to see the Backlog Monitoring view beginning to populate.

Exchange 2010 – CreateTestUser : Mailbox could not be created. Verify that OU ( Users ) exists and that password meets complexity requirements


While using the New-TestCasConnectivityUser.ps1 script to create a test user for Exchange 2010’s connectivity testing, I ran into an issue:


CreateTestUser : Mailbox could not be created. Verify that OU ( Users ) exists and that password meets complexity requirements.
At C:\Program Files\Microsoft\Exchange Server\V14\Scripts\new-TestCasConnectivityUser.ps1:255 char:27
+   $result = CreateTestUser <<<<  $exchangeServer $mailboxServer $securePassword $OrganizationalUnit $UMDialPlan $UMExtension $Prompt
+ CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,CreateTestUser

Oddly enough, that OU does exist (as it will by default on any Windows Domain!) and the password complexity more than satisfied the complexity requirements. The issue is simple enough to fix, I opened the script in notepad and found the line beginning “new-mailbox” – and deleted the parameter “–OrgainisationalUnit:$OrganistationalUnit”. This means the new user defaults to the default OU – Users!

Just a simple fix to save some time! Thanks MS for the buggy script!

Update: Looks like this occurs when there’s more than one OU called Users – my fix will still sort it, but at least you know!

Reporting all ActiveSync devices and their Users in Exchange 2010

Exchange-2010-Logo-748516Recently I needed to report on the ActiveSync devices that were attached to our Exchange 2010 organisation, and which users they were accessing, and then export them to a CSV file. (more…)

Using PowerShell to add IP addresses to IIS7’s FTP IPv4 Address and Domain Restrictions

| 24/11/2010 | Tags: , , , , , ,

iis-self-signed-certificate-logo Today I was configuring a new FTP server based on IIS7 (well, 7.5 technically as it’s a Server 2008 R2 host), and I wanted an easy way to add and remove allowed IP addresses based on either an XML config file or a CSV import. Customers’ IP addresses are added or removed regularly, but I didn’t want to have to update their details twice, once on the server and once in the documents.


Creating/Enabling Exchange 2010 Personal Archives

Exchange-2010-Logo-748516 With the release of Exchange 2010 SP1, administrators can now use separate Mailbox Databases to store the Personal Archives of users – this is particularly useful if you have some larger, slower (and probably by virtue, older) storage that’s not really up to the I/O of your Exchange Server (that old SAN/NAS sitting in the corner of the server room?). It’s also useful if you just don’t have the capacity on your main storage.


Configuring Server 2008 R2 Core Series: Installing and Managing IIS

So, you’ve installed a new server with Server 2008 R2 Core – what next? Logging on, you’re presented with a shiny command prompt, you can run notepad or regedit…but aside from that, where do you go from there? In the next few series of posts I’ll hopefully point out the basics, and some not so basics!

In this post, I’m covering Installing the IIS web server (and a few useful bits) and managing it from the IIS Management Snap-in.

Installing the basic IIS installation

Installing optional components in Server 2008 R2 Core is handled by two commands, OCList and OCSetup. OCList, as the name suggests, lists the optional components and their status, installed or not installed. It’s a long list, so I recommend issuing the command with the “|more” pipe:

oclist | more

The output looks something like this:


OCSetup will accept any one, or multiple, of the roles listed in OCList as an argument to install. It’s recommended you use the command with “start /w” preceding so that the command prompt will wait for the installation to finish before continuing.

To install the basic IIS web server install, use

start /w ocsetup IIS-WebServerRole

As far as I can see, this installs the roles:
















In order to get .Net functioning and allow remote management, you’ll also need the following components installed, a registry key added and the Web Management Service Started (in order):

start /w ocsetup WAS-NetFxEnvironment
start /w ocsetup IIS-ISAPIExtensions
start /w ocsetup IIS-ISAPIFilter
start /w ocsetup IIS-NetFxExtensibility
start /w ocsetup IIS-ASPNET
start /w ocsetup IIS-ManagementService

reg add HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WebManagement\Server /v EnableRemoteManagement /t REG_DWORD /d 1

net start wmsvc

You should now be able to manage your IIS server via the IIS Management Console on a Windows Server 2008 or Windows 7 PC with Remote Server Administration Tools installed.


You can also manage IIS through a PowerShell addin, if you run powershell.exe on your Server Core installation, then import the WebAdministration Module:

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\Administrator> Import-Module WebAdministration
PS C:\Users\Administrator> Get-Command -PsSnapin WebAdministration

CommandType     Name                            Definition
-----------     ----                            ----------
Cmdlet          Add-WebConfiguration            Add-WebConfiguration [-Filte...
Cmdlet          Add-WebConfigurationLock        Add-WebConfigurationLock [-F...
Cmdlet          Add-WebConfigurationProperty    Add-WebConfigurationProperty...
Cmdlet          Backup-WebConfiguration         Backup-WebConfiguration [-Na...
Alias           Begin-WebCommitDelay            Start-WebCommitDelay
Cmdlet          Clear-WebConfiguration          Clear-WebConfiguration [-Fil...
Cmdlet          Clear-WebRequestTracingSettings Clear-WebRequestTracingSetti...
Cmdlet          ConvertTo-WebApplication        ConvertTo-WebApplication [[-...
Cmdlet          Disable-WebGlobalModule         Disable-WebGlobalModule [-Na...
Cmdlet          Disable-WebRequestTracing       Disable-WebRequestTracing [[...
Cmdlet          Enable-WebGlobalModule          Enable-WebGlobalModule [-Nam...
Cmdlet          Enable-WebRequestTracing        Enable-WebRequestTracing [[-...
Alias           End-WebCommitDelay              Stop-WebCommitDelay
Cmdlet          Get-WebAppDomain                Get-WebAppDomain [-InputObje...
Cmdlet          Get-WebApplication              Get-WebApplication [[-Name] ...
Cmdlet          Get-WebAppPoolState             Get-WebAppPoolState [[-Name]...
Cmdlet          Get-WebBinding                  Get-WebBinding [[-Name] <Str...
Cmdlet          Get-WebConfigFile               Get-WebConfigFile [[-PSPath]...
Cmdlet          Get-WebConfiguration            Get-WebConfiguration [-Filte...
Cmdlet          Get-WebConfigurationBackup      Get-WebConfigurationBackup [...
Cmdlet          Get-WebConfigurationLocation    Get-WebConfigurationLocation...
Cmdlet          Get-WebConfigurationLock        Get-WebConfigurationLock [-F...
Cmdlet          Get-WebConfigurationProperty    Get-WebConfigurationProperty...
Cmdlet          Get-WebFilePath                 Get-WebFilePath [[-PSPath] <...
Cmdlet          Get-WebGlobalModule             Get-WebGlobalModule [[-Name]...
Cmdlet          Get-WebHandler                  Get-WebHandler [[-Name] <Str...
Cmdlet          Get-WebItemState                Get-WebItemState [[-PSPath] ...
Cmdlet          Get-WebManagedModule            Get-WebManagedModule [[-Name...
Cmdlet          Get-WebRequest                  Get-WebRequest [-InputObject...
Cmdlet          Get-Website                     Get-Website [[-Name] <String...
Cmdlet          Get-WebsiteState                Get-WebsiteState [[-Name] <S...
Cmdlet          Get-WebURL                      Get-WebURL [[-PSPath] <Strin...
Cmdlet          Get-WebVirtualDirectory         Get-WebVirtualDirectory [[-N...
Function        IIS:                            set-location IIS:
Cmdlet          New-WebApplication              New-WebApplication [-Name] <...
Cmdlet          New-WebAppPool                  New-WebAppPool [-Name] <Stri...
Cmdlet          New-WebBinding                  New-WebBinding [[-Name] <Str...
Cmdlet          New-WebFtpSite                  New-WebFtpSite [-Name] <Stri...
Cmdlet          New-WebGlobalModule             New-WebGlobalModule [-Name] ...
Cmdlet          New-WebHandler                  New-WebHandler [-Name] <Stri...
Cmdlet          New-WebManagedModule            New-WebManagedModule [-Name]...
Cmdlet          New-Website                     New-Website [-Name] <String>...
Cmdlet          New-WebVirtualDirectory         New-WebVirtualDirectory [-Na...
Cmdlet          Remove-WebApplication           Remove-WebApplication [-Name...
Cmdlet          Remove-WebAppPool               Remove-WebAppPool [-Name] <S...
Cmdlet          Remove-WebBinding               Remove-WebBinding [-Protocol...
Cmdlet          Remove-WebConfigurationBackup   Remove-WebConfigurationBacku...
Cmdlet          Remove-WebConfigurationLocation Remove-WebConfigurationLocat...
Cmdlet          Remove-WebConfigurationLock     Remove-WebConfigurationLock ...
Cmdlet          Remove-WebConfigurationProperty Remove-WebConfigurationPrope...
Cmdlet          Remove-WebGlobalModule          Remove-WebGlobalModule [-Nam...
Cmdlet          Remove-WebHandler               Remove-WebHandler [-Name] <S...
Cmdlet          Remove-WebManagedModule         Remove-WebManagedModule [-Na...
Cmdlet          Remove-Website                  Remove-Website [-Name] <Stri...
Cmdlet          Remove-WebVirtualDirectory      Remove-WebVirtualDirectory [...
Cmdlet          Rename-WebConfigurationLocation Rename-WebConfigurationLocat...
Cmdlet          Restart-WebAppPool              Restart-WebAppPool [[-Name] ...
Cmdlet          Restart-WebItem                 Restart-WebItem [[-PSPath] <...
Cmdlet          Restore-WebConfiguration        Restore-WebConfiguration [-N...
Cmdlet          Select-WebConfiguration         Select-WebConfiguration [-Fi...
Cmdlet          Set-WebBinding                  Set-WebBinding [[-Name] <Str...
Cmdlet          Set-WebConfiguration            Set-WebConfiguration [-Filte...
Cmdlet          Set-WebConfigurationProperty    Set-WebConfigurationProperty...
Cmdlet          Set-WebGlobalModule             Set-WebGlobalModule [-Name] ...
Cmdlet          Set-WebHandler                  Set-WebHandler [-Name] <Stri...
Cmdlet          Set-WebManagedModule            Set-WebManagedModule [-Name]...
Cmdlet          Start-WebAppPool                Start-WebAppPool [[-Name] <S...
Cmdlet          Start-WebCommitDelay            Start-WebCommitDelay [-Verbo...
Cmdlet          Start-WebItem                   Start-WebItem [[-PSPath] <St...
Cmdlet          Start-Website                   Start-Website [[-Name] <Stri...
Cmdlet          Stop-WebAppPool                 Stop-WebAppPool [[-Name] <St...
Cmdlet          Stop-WebCommitDelay             Stop-WebCommitDelay [[-PSPat...
Cmdlet          Stop-WebItem                    Stop-WebItem [[-PSPath] <Str...
Cmdlet          Stop-Website                    Stop-Website [[-Name] <Strin...

Serve up a .NET page, to taste

Not that you’d doubt me (!) but there’s one last thing to do – prove it worked. Fortunately, there’s an easy way to do that. I borrowed the code from to create a little “hello world” page. Since the default website is c:\inetpub\wwwroot\ I saved the page there as default.aspx and fired up my browser – et voila!


Powershell script to zip all .bak files in a folder structure, then delete the .bak

| 20/01/2009 | Tags: , ,

Our development SQL server is a monster…there are many many databases, and hundreds, if not thousands of backup files. With each patch tested on the software we sell, there is a new backup. With each client deployment, a new database. With each new major version, a new database. Backups of the new databases inevitably occur, and so we have more files, in more folders – most of which need to be kept in case of roll-backs, bugs or deployment issues.

This all adds up to a bit of an administrative nightmare, especially since the backups eat away at my storage at a phenomonal rate. Zipping the .bak files is great, but since each DB has it’s own backup folder, it can become a bit of a nightmare to go through, zip and delete the .baks. For my first real foray into using PowerShell, I decided I’d write a script to take the legwork out of it for me.

# Powershell Script to recurse input path looking for .bak files, Zip them
# and delete the .bak.
function out-zip {
if (-not $path.EndsWith('.zip')) {$path += '.zip'}
if (-not (test-path $path)) {
set-content $path ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
$ZipFile = (new-object -com shell.application).NameSpace($path)
$input | foreach {$zipfile.CopyHere($_.fullname)} | out-null
$FileCount =0
$FilesZipped =0
$FilesDeleted =0
$InputPath = $args[0]
if($InputPath.Length -lt 2)
Write-Host "Please supply a path name as your first argument" -foregroundcolor Red
if(-not (Test-Path $InputPath))
Write-Host "Path does not appear to be valid" -foregroundcolor Red
$BakFiles = Get-ChildItem $InputPath -Include *.bak -recurse
Foreach ($Bak in $BakFiles)
write-host "File: $Bak" -foregroundcolor Yellow
$ZipFile = $Bak.FullName -replace ".bak", ".zip"
if (Test-Path $ZipFile)
Write-Host "$ZipFile exists already, aborted." -foregroundcolor Red
Get-Item $Bak | out-zip $ZipFile
if(Test-Path $ZipFile)
$Response = read-host -prompt "Please wait for zip to complete then type c<enter> to continue..."
if($Response = "c")
Remove-Item $Bak.FullName
if(Test-Path $Bak.FullName)
Write-Host "File not deleted, manually remove $Bak.Fullname" -foregroundcolor Red
Write-Host "OK" -foregroundcolor Green
Write-Host "File delete aborted by user" -foregroundcolor Red
Write-Host Files found: $FileCount
Write-Host Files Zipped: $FilesZipped
Write-Host Files Deleted: $FilesDeleted

Obviously, this is not something I’d recommend you running lightly without serious testing on your own systems – that said, I hope it helps! I make no warantee or any kind of promise that you won’t lose data by running this! It’s just an exercise in PowerShell for me.

Vista .msu installer fails with error: Installer encountered an error: 0×80070422

| 11/09/2008 | Tags: , , ,

I needed to use PowerShell for something today on my Vista laptop, but was unable to install the file. I had the normal UAC permission required when I ran it,  but then it failed with this error

Installer encountered an error: 0x80070422

The service cannot be started, either because it is disabled or because it
has no enabled devices associated with it.

It turns out that you need to enable the Windows Update service in order to use an MSU file. How backwards is that? Well, lets see, I have to run a SERVICE in order to be able to install applications.

Incidently, PowerShell is fantastic, I'm just getting used to using it at the moment, probably more to come on that!