Scripting Games 2011 – be prepared! :)

Only 12 days to Scripting Games 2011! Be prepared!

Why would I care?

If you like PowerShell, and like to learn, and like to have fun doing both – Scripting Games are there for you. If you are just starting – read awesome posts co-authored by Scripting Wife and try it with beginner division. If you are scripting a while already and feel that you can do more than solve simple tasks – there is division for you (us… :D) as well! If you are worried what good can you get from solving some fictional problems – take a look at last year’s scripting games. Do you see something that will never-ever happen to you? If you are admin you probably had such and/ or similar problems on your plate in the past. Wouldn’t it be great to have skills required to automate solution next time they will come? And trust me, knowing techniques when you need a prompt solution is good thing. Learning new things while your head is on fire is straight way to heart attack or something worse than that… ;)

Where do I start?

The best place to start is 2011 Scripting Games: All Links on One Page article on Hey, Scripting Guys! blog. You will get there from any other blog that is supporting Scripting Games. All you need is click on Mr Scripto. Suspect looks like that and is wanted alive:

2011 Scripting Games

Grab this badge here!

You will see all articles tagged for Scripting Games there. :) I would suggest starting with study guide, and don’t miss any of ‘Scripting Wife’ articles. Even if you knew ‘all that’ already, it’s really fun to read.

But you see… I’m busy around here?

If you reading this you probably know that PowerShell is there to make your life easier. So… First of all you can make title of your PowerShell window remind you about days left till Scripting Games will start:

$Host.UI.RawUI.WindowTitle = "Only {0} days to Scripting Games!" -f (New-TimeSpan -End '4/4/11').Days

If you live in a PowerShell.exe you may want to have it your prompt, so you won’t miss it:

function prompt {            
<#
    Whatever you need goes here, eg:
#>            
    Write-Host "$pwd To SG: " -NoNewline            
# piece of code to produce info about days to SG with proper colour. ;)            
    $DaysToSG = (New-TimeSpan -End '4/4/11').Days            
    switch ($DaysToSG) {            
        { $_ -lt 3 } { $Fore = 'Red'; break }            
        { $_ -lt 7 } { $Fore = 'Yellow'; break }            
        { $_ -lt 12} { $Fore = 'Green'; break }            
        default { $Fore = 'Gray' }            
    }            
            
    Write-Host $DaysToSG -Fore $fore -NoNewline            
# and close the prompt in a way you want it to be closed, such as '> '            
    return ' > '            
}

ScriptingGames

As you can see mine is a bit different (that’s because I’m using Jaykuls prompt function) – but it tells me how much time left. I could use calendar for that too, but well… I prefer to see every day how this number decreases. The reason is simple – I can’t wait when the Scripting Games will start! :)

2011 Scripting Games warm-up. ;)

Yesterday Don Jones started short PowerShell challenge on his blog. More details here.

I decided to go short-and-quick way. Not sure if that’s the shortest way to do that (except for using aliases here and there) but I almost managed to finish all tasks in single pipe. It’s probably not most efficient, and also with requirement to use full names of cmdlets it’s just so long, that I had to break it in several lines each time to make it readable on my blog… ;)

Scripting Games are almost at our doors, so I treat it more like warm-up. Not expect to win, other will do it better for sure. But you know as it goes: reading input from others on the same problem is another thing that makes SG so exciting event: some parts of you brain start to kick in when you write your next script. :) OK, let the code speak… ;)

1. Change login details for service on servers from single OU.

Using WMI, maybe not very clear because of number of parameters passed to Change() method:

(New-Object ADSISearcher $([ADSI]'LDAP://OU=Database Servers,DC=company,DC=com'), '(objectClass=Computer)').FindAll() |             
    ForEach-Object {            
        (Get-WmiObject -ComputerName $($_.Properties.Item('Name')) -Class Win32_Service -filter "Name='DataServer'").Change(            
            $null, $null, $null, $null, $null, $null,             
            'company\DataServers', 'P@$$w0rd',             
            $null, $null, $null) }

2. Table report about disks on file servers from list kept in FileServers.txt.

Since requirement was for a table report I use Format-Table here. Normally I avoid it functions/ scripts – but this is neither so here you go:

Get-WmiObject -Class Win32_LogicalDisk -ComputerName (Get-Content FileServers.txt) -filter "FreeSpace < $(20GB)" |             
    Format-Table @{ Name = 'Server Name'; Expression = { $_.__SERVER}},             
        @{ Name = 'Drive Letter'; Expression = { $_.DeviceID }},             
        @{ Name = 'Free (GB)'; FormatString = 'N2'; Expression = { $_.FreeSpace / 1GB}},             
        @{ Name = 'Disk Size (GB)'; FormatString = 'N2'; Expression = { $_.Size / 1GB}},            
        @{ Name = 'Percent Free'; FormatString = 'P0'; EXpression = { $_.FreeSpace/$_.Size }} -AutoSize            

I really would love to have @{n=’Name’;e={$_.Property};f=’n2’} here, but I assume it should be full also for that formatting hashtables. If not that would be much shorter… ;)

3. Inventory of workstations – exportable and easy to filter.

Now THAT is something I already did for myself. But it’s whole module, with types defined, the methods to filter stuff… SQL to store info almost done too. Wanted to do something similar here, so here is my version:

(New-Object ADSISearcher -Property @{             
    SearchRoot = [ADSI]''            
    Filter = '(objectClass=Computer)'            
    PageSize = 1000 } ).FindAll() |             
ForEach-Object -begin {            
    $PCList = New-Object Collections.ArrayList            
} -process {            
    $Name = $_.Properties.Item('Name')            
    if (Test-Connection -Quiet $Name -Count 1) {            
        $OS = Get-WmiObject -ComputerName $Name -Class Win32_OperatingSystem -Property BuildNumber, ServicePackMajorVersion            
        $PCList.Add($(            
        New-Object PSObject -Property @{            
            Name = [string]$Name            
            OSVersion = $OS.BuildNumber            
            SP = $OS.ServicePackMajorVersion            
            Serial = (Get-WmiObject -ComputerName $Name -Class Win32_Bios).SerialNumber             
        })) | Out-Null              
    } else {            
        $PCList.Add($(            
        New-Object PSObject -Property @{            
            Name = [string]$Name            
            OSVersion = 'Unknown'            
            SP = 'Unknown'            
            Serial = 'Unknown'            
        })) | Out-Null            
    }            
                    
} -end {            
    , $PCList | Add-Member -MemberType ScriptMethod -Name ToCSV -Value {             
            $this | Export-Csv -NoTypeInformation $args[0] } -PassThru |            
        Add-Member -MemberType ScriptMethod -Name ToHTML -Value {             
            $this | ConvertTo-Html | Out-File $args[0] } -PassThru |            
        Add-Member -MemberType ScriptMethod -Name ByOSVersion -Value {             
            param([string]$pattern) $this | where { $_.OSVersion -match $pattern } } -PassThru |            
        Add-Member -MemberType ScriptMethod -Name BySP -Value {             
            param([string]$pattern) $this | where { $_.SP -match $pattern } }            
}

Yes, that is technically one line… ;) And you get $PCList object with methods .ToCsv([string]$Path); .ToHTML(‘[string]$Path); .ByOSVersion([string]$pattern) and .BySP([string]$pattern. Imagine you have it in your profile and now when you need info about PCs you just to $PClist.BySP(2) and get all PCs with Service Pack 2. ;) Of course in production it requires some extra work, because it would get only online machines.

4. Create users from Users.csv and add them to Employees AD group.

At first I was tempted to use New-ADUser (with one select to change UserName to Name acceptable by cmdlet), but Don said: no loading. So there it is, ADSI solution:

Import-Csv Users.csv | Foreach-Object -begin {             
    $OU = [ADSI]'LDAP://CN=Users,DC=company,DC=com'            
    $Group = [ADSI]'LDAP://CN=Employees,CN=Users,DC=company,DC=com'            
} -process {            
    $User = $OU.Create('user',"CN=$($_.UserName)")            
    $User.Put('sAMAccountName',$_.UserName)            
    $User.Put('l',$_.City)            
    $User.Put('Department',$_.Department)            
    $User.Put('Title',$_.Title)            
    $User.SetInfo()            
    $Group.Add($User.ADSPath)            
}

As you see it’s all one-pipe events. I also managed to avoid end-line-backtick. I don’t like this technique because it fails miserably if someone copy it to his script and add space after grave accent. Newline is no longer escaped and it just breaks. And go figure, find reason for this error… ;) When it all looks just fine! ;)