My Solution to the Impractical One-liner Challenge

I recently participated in an Impractical One-liner Challenge that Stephen Owen posted on his blog site where he challenged his readers to come up with a PowerShell one-liner that would allow the person running it to select a single OU (Organizational Unit) from a list of all the OU’s in Active Directory and then move a list of computer accounts that are contained in a text file to that OU.

My solution requires the Active Directory PowerShell module and at least PowerShell version 3.

I decided to do a little extra work and add some additional functionality to my solution. I start out by getting a list of all OU’s and giving you a human readable OU name and also the distinguished name for that OU since the same name could exist in more than one place in Active Directory. I display that information to the user with Out-GridView:

Get-ADOrganizationalUnit -Filter * -Properties OU |
Select-Object -Property @{label='OU';expression={$_.OU -replace '{|}'}},
                        DistinguishedName |
Out-GridView -OutputMode Single -Title 'Select Destination OU'

As shown in the previous example, the -OutputMode Single parameter and value prevents the user from selecting more than one OU. I also set the title of the Out-GridView windows throughout this solution so the user has an idea of what they’re suppose to do.

I know that only one item will be returned at this point, but in order to make this command a one-liner, I pipe the results of the OU that the user selected to the ForEach-Object cmdlet, then get the contents of the text file that contains the computer names. Those results (the list of computer names) are piped to Get-ADComputer because we need the computer’s distinguished name to be able to move it with the Move-ADObject cmdlet. Those results are then piped to Out-GridView because I also wanted to allow the user to be able to select the computers to be moved instead of blindly moving all computers that were listed in the text file. The -OutputMode Multiple parameter and value is specified this time to allow the user to pick more than one computer to be moved if they so desire:

ForEach-Object {
    Get-Content -Path .\computers.txt |
    Get-ADComputer |
    Out-GridView -OutputMode Multiple -Title 'Select Computers to Move' |

Those results are piped to Move-ADObject and the target OU is specified via parameter input and the value is used from the first portion of the command (what was piped to ForEach-Object). By default Move-ADObject doesn’t return any results, it just “makes it so” without any feedback unless there’s a problem. I wanted to know what computers were moved, where they were moved from, and what OU they were moved to so I added the PassThru parameter to Move-ADObject so it would return results:

Move-ADObject -TargetPath $_.DistinguishedName -PassThru

I only wanted those specific items returned so I piped the results of the previous command to Select-Object specifying the Name property and since I already had the computer name, I wanted that portion of the distinguished name removed which required a custom property to be created and the easiest way to remove it was with a regular expression:

Select-Object -Property Name,
                        @{label='DestinationOU';expression={$_.DistinguishedName -replace '^(.*?),'}

I also wanted to know where the computer account was before it was moved, so I went back and added the PipelineVariable parameter to the end of the command just prior to Move-ADObject to obtain those results by storing them in a variable named info:

Out-GridView -OutputMode Multiple -Title 'Select Computers to Move' -PipelineVariable info |

Another custom property later and I had what I needed:

@{label='SourceOU';expression={$info.DistinguishedName -replace '^(.*?),'}},

Here’s the PowerShell code for my solution in it’s entirety:

Get-ADOrganizationalUnit -Filter * -Properties OU |
Select-Object -Property @{label='OU';expression={$_.OU -replace '{|}'}},
                        DistinguishedName |
Out-GridView -OutputMode Single -Title 'Select Destination OU' |
ForEach-Object {
    Get-Content -Path .\computers.txt |
    Get-ADComputer |
    Out-GridView -OutputMode Multiple -Title 'Select Computers to Move' -PipelineVariable info |
    Move-ADObject -TargetPath $_.DistinguishedName -PassThru |
    Select-Object -Property Name,
                            @{label='SourceOU';expression={$info.DistinguishedName -replace '^(.*?),'}},
                            @{label='DestinationOU';expression={$_.DistinguishedName -replace '^(.*?),'}} |
    Out-GridView -Title 'Results'
}

Although the command shown in the previous block of code is on more than one physical line, it is still a PowerShell one-liner command because it is one continuous pipeline.

When that command is run, you’re prompted with a selection of OU’s:

oneliner-challenge1.jpg

After clicking , a list of computers is shown so you can select the computer(s) you want to move to the OU that was selected in the previous step:

oneliner-challenge2.jpg

I’ve selected all of my SQL servers and when I click <OK>, the computer accounts are moved to the OU we previously selected and the results of what was done are displayed:

oneliner-challenge3.jpg

ยต