Weekend Scripter: Some PowerShell Automation Is Better than No Automation

Weekend Scripter: Some PowerShell Automation Is Better than No Automation

  • Comments 1
  • Likes

Summary: Microsoft Windows PowerShell MVP Bartek Bielawski discusses Windows PowerShell automation.

Microsoft Scripting Guy, Ed Wilson, is here. Today, the Scripting Wife and I are in Warsaw, Poland, to meet with a very special person—Microsoft Windows PowerShell MVP Bartek Bielawski. Bartek is a past winner of the Scripting Games, and has written a number of guest blog articles. He loves Windows PowerShell and is a fun person to hang out with. I thought it would be great to provide him another chance to talk to you.

Take it away, Bartek …

Windows PowerShell is all about automation. It’s usually very handy if you can automate the whole task, but there are times when you can’t automate everything. Should it stop you from automating part of the process? My answer is: NO! I would like to share two scenarios where I was not able to automate the whole process I had on my plate, but I decided to write a script that would support me in part.

The first one is related to the inventory application that we use at work. Let’s call it “NotFriendly.” Even though it’s based on some SQL Server engine, I had no luck finding a way to connect to the database directly to do a bulk import of data I already had in another application (“Friendly”). We have a written procedure for how to fill NotFriendly with information about our computers. It’s a manual process that requires re-writing information we already keep in the Friendly system, but in a slightly different format. So, I could either reinvent the wheel, or use Windows PowerShell.

To read data more easily from Friendly, I exported information into the CSV file. Imported information required some transformation to match format (and order) that was required by the NotFriendly system. But, first, I defined a very simple function that I was using during pasting data:

function Set-ClipBoard {

param ([string[]]$Text)

 

foreach ($string in $Text) {

    [System.Windows.Clipboard]::SetText(

        $string

    )

    [System.Windows.Forms.MessageBox]::Show(

        "Clipboard set to: $string - OK!"

    )

}

}

The idea is simple: I will run this function and pass an array of strings related to each computer. Each string will get to my clipboard and, at the same time, I will get a popup message with information about what the clipboard currently contains. The rest is automating myself: Click OK, paste the next phrase in the correct field, click OK … until all data is copied over. And the transformation itself? It was mixing data from different sources (database with local admin passwords, Active Directory, and information stored in the Friendly system):

$DataFromFriendlyInCsv | Foreach-Object {

    if (-not ($Sam = (Get-QADUser –LastName $_.User).SamAccountName)) {

        $SAM = $_.User

    }

    $Admin = $UserAdmin = 'CHECK!'

 

    $Admin, $UserAdmin = Import-W7Pass -ComputerName $_.Shortcode |

        foreach { $_.Pass }

    $Passwords = "User Admin PW: {0} | Admin PW: {1}" -f $UserAdmin, $Admin

    $Date = Get-QADComputer -Name $_.ShortCode | foreach { $_.WhenCreated }

    $Formatted = "{0:d MMMM yyyy}" -f $Date

 

    Set-Clipboard $_.ShortCode,

        $_.Serial,

        $_.Class,

        $_.Product,

        $Sam,

        $_.Supplier,

        'Active',

        'Computer',

        $Formatted,

        'USES BUILD',

        'PXL32DEP001V5',

        $Passwords

}

As you can see, some fields are static and most are modifications of data taken from the CSV. Some fields are taken from different sources, but based on information stored in the CSV. Before I wrote this little script, it would take me 5-10 minutes to add one computer. After that, it was around 20 seconds. And I haven’t made a single mistake with the script. Doing the same thing without it was a source of several errors that were usually hard to fix, and it often meant I had to start from scratch.

The second scenario is related to the Excel tracker that I was recently asked to update daily. I would love to automate the whole thing, but the tracker is kept in a system that I haven’t (yet!) found a way to talk to from Windows PowerShell (it’s similar to SharePoint, so it stores files with metadata attached to them). But because I just need to paste a “daily row” in simple Excel, I decided that if I can grab all information automatically, I will just put it on my clipboard and paste into Excel. I wanted to know the current values anyway, so being forced to paste the whole row was non-issue.

Tracker contains information about System Center: Configuration Manager (SCCM) 2007 in our organization. We have several collections that we pay special attention to and want to combine data about the object’s count in each of those. Obviously, before we collect data we need to make sure that the collection is updated (so that we get current, accurate numbers). This is also done by the helper function:

function Measure-Collection {

[CmdletBinding()]

param (

    $CollectionID,

    [switch]$Recurse,

    [string]$Filter

)

    $Now = Get-Date

    $CollectionData = @{

        Class = 'SMS_Collection'

        Filter = "CollectionID = '$CollectionID'"

    }

    Get-WmiObject @WMIStatic @CollectionData |

        ForEach-Object { $_.RequestRefresh($Recurse) } | Out-Null

 

    do {

        $Collection = Get-WmiObject @WMIStatic @CollectionData

        Start-Sleep -Seconds 1

    } until (

        $Collection.ConvertToDateTime(

            $Collection.LastRefreshTime

        ) -gt $Now

    )

 

    $Class = @{

        Class = "SMS_CM_RES_COLL_{0}" -f $CollectionID

    }

    if ($Filter) {

        $Class.Filter = $Filter

    }

 

    (Get-WmiObject @WMIStatic @Class | Measure-Object).Count

}

 It uses WMI and talks to the site server. Information about the site server is kept in $WMIStatic:

$WMIStatic = @{

    ComputerName = 'Server'

    Namespace = "root\sms\site_{0}" -f 'SiteCode'

}

The whole script uses this function to measure several collections (including SMS00001) and uses the Tee-Object cmdlet to display information on the screen and save them to the variable:

Measure-Collection -CollectionID SMS00001 |

    Tee-Object -Variable All |

    Foreach-Object { "All {0}" -f $_ }

Finally, I combine all this data, add information about current date and time (using a format that matches the locale I use), and use a simple way to prepare this data to paste into Excel:

$Date = "{0:yyyy-MM-dd}" -f (Get-Date)

$Time = "{0:HH:mm}" -f (Get-Date)

$Date, $Time, $All, $Windows, $PUR, $NoClient, $NA, $Dups -join "`t" | clip

Because whenever you have tab-separated data in your clipboard and paste it into Excel – each value will make its way to correct column – it’s all you need to paste a few values in separate Excel columns in a single row. And that’s it: I just run this script, and once it finishes I can paste data into Excel. First, I open it in a way that is required by this system and necessary to keep metadata attached to this file. I get most of the work done by Windows PowerShell even though I haven’t found a way to automate the whole process.

And this is what you should take from this article: If there is any process you can automate, even partially, and you save yourself some manual and repetitive work, you should not hesitate. It’s always a few seconds of your time saved. And if you add those seconds together it will eventually turn into hours and days.

~Bartek

Awesome job, Bartek. Thank you so very much for sharing.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment