My First *REAL* PowerShell Script

Friday, 13. August 2010

So I’ve done a few one-liner’s in PowerShell to more efficiently collect certain information about a server, folder, etc, but today I wrote what I consider to be my first real PowerShell script.  It should be no coincidence that today’s workday was the conclusion of a week of PowerShell training from Microsoft.

Anyway, the concept behind the script is simple.  I have a few network shares to which our deployment of Microsoft Configuration Manager sites perform their backups to.  I wanted a quick and easy way to be assured that those backups were completing properly.  This script does the following:

  • Loop through each of the main SCCM backup folder shares (one at each major datacenter)
  • Loop through each site backup folder
  • Determine if folder was modified within the last 24 hours
  • Determine if the backup task’s log file was modified within the last24 hours
  • If above conditions are true, open the log file and ensure that it says that the task completed with no errors
  • If above conditions aren’t true, note the directory of the failed backups.
  • Report backup status to the console.  If backups failed, report SCCM site code.
function Check-SCCMBackups
{
# $arrBackupFolders contains a list of locations that contain SCCM backups.
$arrBackupFolders = "\\server1\share1","\\server2\share2","\\server3\share3"
$dateYesterday = (Get-Date).addDays(-1)
# Initialize variables
# variable to hold list of sites whose backups did not run
$strSiteBackupNotRun = ""
 # variable to hold list of sites whose backups ran but failes
$strSiteBackupFailed = ""
$boolTasksDidRun = 1 
# initialized to 1; set to 0 if any backup task did execute successfully
$boolTasksRanSuccessfully = 1 
Write-Host "Checking status of SCCM backup folders... " -nonewline
# Loop through each value of $arrBackupFolders....
ForEach ($strUNCPath in $arrBackupFolders)
{
# ... and get the directories it contains.
$objBackupFolders = Get-ChildItem $strUNCPath | where {$_.PsIsContainer}
# Loop through each discovered directory....
ForEach ($objDirectory in $objBackupFolders)
{
# ... and check it directory was modified within the last day.
if ($objDirectory.LastWriteTime -ge $dateYesterday)
{
# Task executed (we know because folder write time was updated)
# Open log file and determine if job completed successfully.
$strLogFilePath = $strUNCPath + "\" + $objDirectory.Name + "\" + $objDirectory.Name + "Backup\smsbkup.log"
# Check smsbkup.log file modify time to ensure it was
# modified within the last 24 hours.
$strLogFile = Get-Item $strLogFilePath
# File was NOT (!) modified in last 24 hours!
If (!$strLogFile.LastWriteTime -ge $dateYesterday)
{
# Since log file was not modified (something else must've written to the directory)....
# Add Site Code (directory name) to the list of sites that did not run and set the flag...
$strSiteBackupNotRun = $strSiteBackupNotRun + " " + $objDirectory.Name
$boolTasksDidRun = 0
# ...then stop processing this object and continue with the loop.
Continue
}
# Line indicating success is four lines from the bottom of the file.
$strLine = (Get-Content $strLogFilePath)[-4]
# If the line doesnt match criteria, backup did not complete successfully.
if ($strLine -notmatch "Backup task completed successfully with zero errors")
{
# Task ran but did not complete successfully
# Add Site Code (directory name) to the list of failed backups....
$strSiteBackupFailed = $strSiteBackupFailed + " " + $objDirectory.Name
# ... and set the flag indicating that one or more tasks did not complete successfully.
$boolTasksRanSuccessfully = 0
}
}
else
{
# Task did not run as the directory modified date is not within last day.
# Add Site Code (directory name) to the list of sites that did not run and set the flag
$strSiteBackupNotRun = $strSiteBackupNotRun + " " + $objDirectory.Name
$boolTasksDidRun = 0
}
}
}
Write-Host "Complete!"
# Check status of boolean flags and present data.
if ($boolTasksDidRun -eq 0 -OR $boolTasksRanSuccessfully -eq 0)
{
# One or more tasks did not execute or failed during execution.
if ($boolTasksDidRun -eq 0)
{
# One or more tasks did nto execute; list the site codes (directory names).
Write-Host "Backup tasks for the following sites did not run: $strSiteBackupNotRun" -foregroundcolor red -backgroundcolor black
}
if ($boolTasksRanSuccessfully -eq 0)
{
# One or more tasks did not run successfully; list the site codes (directory names).
Write-Host "Backup tasks for the following sites did not complete successfully: $strSiteBackupFailed" -foregroundcolor red -backgroundcolor black
}
}
elseif ($boolTasksDidRun -eq 1 -AND $boolTasksRanSuccessfully -eq 1)
{
# All tasks completed successfully
# (Directory modify dates are within 24 hours and all log files show last task execution was successful.)
Write-Host "All SCCM backup tasks executed and ran successfully." -foregroundcolor green
}
Write-Host ""
}

PowerShell Training This Week!

Monday, 9. August 2010

My employer has set up Microsoft training this week. We have scheduled to bring a trainer from Microsoft to our largest office who will provide a four day training starting tomorrow.

Because of this, I have been digging through some PowerShell resources I had previously found to get ahead of the curve for this training. I’m listing those resources here as a reminder to myself and in the hopes that somebody else may find them useful.

VMware – Determining ownership of a virtual disk using PowerShell

Tuesday, 29. June 2010

So as you may have guessed, I work with VMware a bit in my employment. On top of that, I’ve tried to start using PowerShell to automate repetitive tasks that I run into. (In fact, one of the major points of the existence of this page is to give myself a location to store these findings in a place which I can find them later, and if someone else finds them useful, then great.)

Anyway, the problem I ran into today was that I had a VM on a datastore whose name did not match a VM in my vCenter inventory. How could I tell if these files were in use by a legitimate VM or just wasting space? I could right-click and ‘Edit Settings’ on hundreds of VM’s… or I could use Powershell.

so I started off by connecting to the vCenter server and getting an inventory of VM’s:


Connect-VIServer servername
$VM = Get-VM

Then I grabbed a list of disks which matched my criteria:

$Disks = $VM | Get-HardDisk | Where {$_.FileName -like '*web*' }

Then I did…

$Disks | Get-Member and saw that there are Name, FileName, and ParentID properties. By doing $Disks | Select Name, FileName, ParentID,  I now have the parent Id of the VM.

So how do I know which VM the parent ID field references?

$VM | Where {$_.Id -like 'parent id from previous select'}

… which returns ….


Name PowerState Num CPUs Memory (MB)
---- ---------- -------- -----------
WebSrv1 PoweredOn 4 4096

Is there an easier way to do this? Probably, and I already have a couple of ideas. If I can get them working and cleaned up, I’ll post them here.

SCCM: Determining collection refresh time using PowerShell

Monday, 21. June 2010

I recently had a need to examine the last refresh time of a large number of SCCM collections. We had a group of collections which are used to define maintenance windows for various servers, and we wanted to ensure that all these collections were updating regularly (and to fix the ones that weren’t).

Normally, I would take such a boring task and look for some way to automate it. I have a co-worker who evangelizes about the merits of PowerShell every chance he gets and we were able to put together a few lines to get the information I was looking for.

So obviously this requires that you have PowerShell installed, and it does require PowerShell v2. We used the SCCM PowerShell module located here, which appears to be the most “complete” unofficial PowerShell module I’ve found so far.

Once your PowerShell environment is configured, connecting to an SCCM server is as easy as:
$SCCMServer = Connect-SCCMServer servername

Next, you can do other things like:
# get all SCCM collections
$AllCollections = Get-SCCMCollection -SccmServer $SCCMServer

#show all not updated today
$a | where {$_.LastRefreshTime -notlike ‘20100621*’} | select Name

Instead of getting a list of all collections, you could target only certain ones:
$a = Get-SCCMCollection -sccmserver $sccm | where {$_.name -like 'Test*'} | Select name

The lines above were enough to save me tons of time I would otherwise spend manually verifying each collections properties. There are plenty of other opprotunities in this module including the ability to gather all sorts of information about advertisements, collections, sites, and packages, and I plan to continue to look for opprotunities where using these tools can allow me to work more efficiently.