PowerShell for Windows Admins

September 6, 2013  2:45 PM

Cleaning up my AD

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I decided it was time to clean some of the rubbish out of my test AD. I’ll be upgrading to Windows Server 2012 R2 next month so a bi tof a clean up now is a good idea.

I decided to start with the computer objects. I’ve created & deleted quite a few virtual machines over the years so there’s a good chance of finding something to remove. Computes in an AD domain have a secure channel to the domain controller to which they authenticate on startup. The password on this channel is reset automatically every 30 days. Any machines that haven’t reset their password in a while a probably good candidtes for removal:

Get-ADComputer -Filter * -Properties PasswordLastSet |
select Name, PasswordLastSet |
sort PasswordLastSet

That shows me a few machines to remove. Anything that hasn’t reset its password for 12 months is fair game.

$date = (Get-Date).AddYears(-1)
Get-ADComputer -Filter {PasswordLastSet -lt $date} -Properties PasswordLastSet |
select Name, PasswordLastSet | sort PasswordLastSet

Its odd but I couldn’t get the search to work when I was calculating the date in the filter

Now I can delete them:

PS> Get-ADComputer -Filter {PasswordLastSet -lt $date} -Properties PasswordLastSet | Remove-ADComputer -Confirm:$false

Remove-ADComputer : The directory service can perform the requested operation only on a leaf object
At line:1 char:82
+ … swordLastSet | Remove-ADComputer -Confirm:$false
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (CN=W08SQL05,OU=…anticore,DC=org:ADComputer) [Remove-ADComputer], ADExce
+ FullyQualifiedErrorId : ActiveDirectoryServer:8213,Microsoft.ActiveDirectory.Management.Commands.RemoveADComputer

Not what I was expecting. The error message is what you get when trying to delete an OU with objects still in it but a computer object is a leaf object.

It turns out that the computer object can contain other objects especially when its a virtual machine. Unfortunately, the only way to see this is to use ADSIEdit. This is the full ADSIedit you need not the Attribute Editor in AD Users & Computers or AD Administrative Center. When I looked in ADSIEdit I saw there was indeed a child object

CN=Windows Virtual Machine,CN=W08SQL05,OU=SQL Server,OU=Servers,DC=Manticore,DC=org

Both of the affected machines were Windows 2000 VMs but later versions of Windows up to and including Windows 2012 are also affected.

So how to delete:

Option 1 – use the GUI and force deletion. Who me? Not likely. Smile

Option 2 – use Remove-ADObject

Get-ADComputer -Filter {PasswordLastSet -lt $date } |
Remove-ADObject -Recursive -Verbose -Confirm:$false

That’s computers cleaned up. Just leaves users, groups & OUs

September 6, 2013  2:18 AM

Multiple services on multiple servers–another way

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A potentially simpler way to solve the issue of multiple servers and multiple services is to use a hash table

$data = @{
“server02” = “BITS”, “NtFrs”, “MSMQ”, “Kdc”;
“exch10” = “MSExchangeAB”, “W32Time”, “W3SVC”

foreach ($server in $data.Keys){
Get-Service -ComputerName $server -Name ($data[$server]) |
select @{N=”Server”; E={$server}}, Status, Name, DisplayName

The data structure is a bit more intuitive as you can see the direct link between the server and its list of services.

A version using PSObjects is available on the forum – http://powershell.org/wp/forums/topic/help-to-output-array/

September 5, 2013  3:38 PM

Getting remote services

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Getting the service on a remote server is easy

Get-Service -ComputerName Exch10

Getting a set of services on a remote machine isn’t difficult

Get-Service -ComputerName Exch10 -Name “MSExchangeAB”, “W32Time”, “W3SVC”

Ok so what about the scenario with multiple servers

Get-Service -ComputerName Exch10, server02

Multiple servers with a set of servers just means adding the –Name parameter and the list of services

Get-Service -ComputerName Exch10, server02 -Name “W32Time”, “W3SVC”

but you have to have the same list of services for each server.

Now lets get really picky and go for multiple servers where each one has a different set of services.

I thought about csv files but then how do you repent the list? Nested arrays – yuck.

I ended up with this approach. Its a bit messy but works and is easily expandable & changeable

$servers = “server02”, “exch10”
$server02_services = “BITS”, “NtFrs”, “MSMQ”, “Kdc”
$exch10_services = “MSExchangeAB”, “W32Time”, “W3SVC”

foreach ($server in $servers){
Get-Service -ComputerName $server -Name (Get-Variable -Name ($server + “_services”)).value |
select @{N=”Server”; E={$server}}, Status, Name, DisplayName

Create a list of servers. Create a list of services per server. Notice the naming convention for the variables.

Iterate over the servers using foreach. The server name comes from the foreach iteration variable. The list of services is created by using get-variable. Substitute the server name to create the variable name and use the Value property to give you the services of interest. Use select to add the computer name to the output.

I don’t use the *Variable cmdlets very often and this was a neat use of get-variable

September 5, 2013  2:46 PM

Discovering a users OU

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Interesting question – how do you discover the OU in which an AD user is sitting? The Quest cmdlets were very helpful because they had a ParentContainer property. With the Microsoft cmdlets you have to do a bit of work

There are two places to look – the distinguished name and the canonical name

PS> $user = Get-ADUser -Identity Richard -Properties Canonicalname
PS> $user

CanonicalName : Manticore.org/Users/Richard
DistinguishedName : CN=Richard,CN=Users,DC=Manticore,DC=org
Enabled : True
GivenName : Richard
Name : Richard
ObjectClass : user
ObjectGUID : b94a5255-28d0-4f91-ae0f-4c853ab92520
SamAccountName : Richard
SID : S-1-5-21-3881460461-1879668979-35955009-1104
Surname :
UserPrincipalName : Richard@Manticore.org

Notice the different formats

The distinguished name is easiest

PS> ($user.DistinguishedName -split “,”, 2)[1]

use split on the DistinguishedName. Note the format of the split command – – – “,”, 2

It means split on a comma and give me two elements – one containing the data before the first comma & the second containing all data after the first comma

The canonical name needs a bit more work

PS> $elements = $user.CanonicalName -split ‘/’
PS> $elements[0..($elements.Count – 2)] -join ‘/’

split the canonical name on ‘/’ and then recreate the string dropping the last element

September 4, 2013  2:26 PM

Integer sizes save the day

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I was looking to convert some string data into integers – the data was bytes & I needed it in GB. The value I needed was buried in the middle of a string so I needed to do some string processing. Some other conditions forced me to use the Parse method of the integer class

Lets say I get a number like 2147483169 back in the data & I need to divide by 1gb

£> [int]::Parse(“2147483169”) / 1gb

I’ve put the string value directly in here rather than the complicated string processing I was actually using.

The point came when I realised that I could have much bigger numbers than 2Gb so what was the maximum I could work with

£> [int]::maxValue
£> [int]::maxValue / 1gb

OK thats not enough. The standard [int] is 32bits & I know its big brother has 64 bits so how big is big brother

£> [int64]::maxValue
£> [int64]::maxValue / 1gb

That’s more than big enough so I went with 64 bit integers.

This isn’t just a developer type thing because I was working with Exchange mail box sizes. So the moral of the story is to think about the possible values you’ll see in you data while you are coding to avoid errors or failure when you are running the script.

September 3, 2013  2:24 PM

Import and Export PSSession

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A couple of comdlets that I’ve not really used much before have just come into prominance for me to solve a problem I’ve been working on. These cmdlets are:


The two cmdlets work with commands in a remote session but have subtle differences in terms of what they actually do.


This cmdlet imports commands (cmdlets, functions and aliases) from a PSsession on a remote (or local) machine into your current PowerShell session. You must have a remoting session open to the machine from which you want to import and that session must remain open while you are using the commands.

A temporary module is created to hold the imported commands. when you close PowerShell it is removed. Each command has an –AsJob parameter added.

The commands are imported into your session as functions and rely on remoting to access the remote machine. This means that you get inert objects back exactly as if you had run the command on the remote machine through a remoting session.


This cmdlet imports commands from a Powershell remotign session BUT it saves them as a module on your local machine. You have to then import the module to use the commands. You will need a remoting session the remote machine for the module to work – Powershell will create it for you when you import the module if one doesn’t exist.

The advantage is that you are importing a local module which should be faster than using import-pssession each time.

September 2, 2013  3:28 PM

Get-Alias surprise

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

On the principle that any day on which you learn something new is not a complete waste I stumbled over this today:

Get-Alias has a –definition parameter

I’d always done this

Get-Alias | where Definition -eq ‘select-string’

now I can save some typing

get-alias -Definition ‘select-string’


September 2, 2013  11:29 AM

Winter Scripting Games–maybe?

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Did you enjoy the Summer Scripting Games? Do you want more?

We are exploring the possibility of running a Winter Scripting Games – see http://powershell.org/wp/forums/topic/winter-games-2013/

This will be a team event (minimum of 2) that would run late this year or early next year.


Add a comment with your thoughts on the process and the timing to the thread at the above URL

September 1, 2013  1:56 PM

Get-Command trick

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A useful trick with Get-Command. Use the –ListImported parameter

Get-Command -ListImported -CommandType cmdlet

shows the cmdlets imported into your session

Get-Command -ListImported -CommandType function

shows the functions imported into your session

Get-Command –ListImported

shows cmdlets and functions

useful for knowing what you’ve imported against what’s available

September 1, 2013  4:57 AM

Find the key of a WMI class

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

At least one property of a WMI class will be marked as a key. This information is held in the property Qualifiers which is a collection of data. The key is needed if you need to create an instance of the class. I’ve shown how to find the key with Get-WmiObject before but not with the new CIM cmdlets.

Its actually easier with the CIM cmdlets

$class = Get-CimClass -ClassName Win32_Process

foreach ($property in $class.CimClassProperties) {

$property | select -ExpandProperty Qualifiers |
foreach {
if ($_.Name -eq ‘key’){


Get the CIM class. Iterate over the properties and expand the Qualifiers of the property. Check the names of the qualifiers for key & when you find it print out the property data

Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: