SCCM 2012 R2 – Exporting an application with PowerShell. Revision 1

I recently wrote a script for exporting an application from SCCM using PowerShell and another post about logging.

I thought the best thing to do would be to combine the two and provide a new script to export an application

Please note that this will export from a primary SCCM site as long as you have only got 1. If you have a CAS please modify the script from:


$PRIServerName = Get-CMSite | Where {$_.Type -eq "2"}


to


$PRIServerName = Get-CMSite | Where {$_.Type -eq "4"}


You can also change the variable names and log file details to match



##Script written by Liam Matthews
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Starting Script" -PercentComplete 10
 
##User defined variables
$SCCMSiteCode = "C01"
$ExportPathDirect = "E:\SCCM-Export\"
$LogLocation = "E:\Scripts\"
$LogFileName = "Application-Export.log"
 
##Machine Variables
$SCCMModule = $env:SMS_ADMIN_UI_PATH.Replace("\bin\i386","\bin\configurationmanager.psd1")
$CurrentUser = [Environment]::UserName
$SCCMDrive = $SCCMSiteCode + ":"
$StartTime = Get-Date -Format "HH:mm:ss.fff"
$StartDate = Get-Date -Format "MM-dd-yyyy"
$StartDateTime = Get-Date -Format "HHmmss"
$TZbias = (Get-WmiObject -Query "Select Bias from Win32_TimeZone").bias
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Starting new thread+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for Configuration 1Manager PowerShell module"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of current PowerShell modules
$CurrentModules = Get-Module
 
IF ($CurrentModules.Name -contains "ConfigurationManager")
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "2"
$LogText = "Configuration Manager PowerShell module has already been imported. This is unexpected but script will continue"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Configuration Manager PowerShell module is not currently present. Attempting import now"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Checking if SCCM PowerShell module is available for import
IF (Test-Path $SCCMModule)
{
##Import SCCM PowerShell module
Import-Module $SCCMModule
 
##Get list of current PowerShell modules
$CurrentModules = Get-Module
 
IF ($CurrentModules.Name -contains "ConfigurationManager")
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Configuration Manager PowerShell module has been imported successfully"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Configuration Manager PowerShell module has failed to import. The script will now Exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Configuration Manager PowerShell module does not exist on the current machine. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Checking for SCCM Drive: $SCCMSiteCode"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get list of PowerShell drives
$PSDrives = Get-PSDrive | Select Name
 
IF ($PSDrives.Name -contains $SCCMSiteCode)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "$SCCMSiteCode drive exists"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Connecting to drive $SCCMSiteCode"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Connect to SCCM site
CD $SCCMDrive
 
##Get current drive location
$CurrentPSDrive = (get-location).Drive.Name
 
IF ($CurrentPSDrive -eq "$SCCMSiteCode")
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Successfully connected to $SCCMSiteCode"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Unable to connect to $SCCMSiteCode. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
Exit
}
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "SCCM drive $SCCMSiteCode does not exist. Module may have issues or variable may be incorrect. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Searching for SCCM Primary site"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get SCCM CAS Server
$PRIServerName = Get-CMSite | Where {$_.Type -eq "2"}
$PRIServerName = $PRIServerName.ServerName
 
IF ($PRIServerName -ne $Null)
{
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Found SCCM CAS site $PRIServerName"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Unable to locate SCCM Primary site. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Searching for applications" -PercentComplete 30
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Searching for applications"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Get applicaion list via WMI
$Applications = get-wmiobject -ComputerName $PRIServerName -Namespace Root\SMS\Site_$SCCMSiteCode -Class "SMS_Application" | Select -unique LocalizedDisplayName | sort LocalizedDisplayName
$ApplicationsCount = $Applications.Count
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Found $ApplicationsCount applications"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Requesting use input"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
 
$form1 = New-Object System.Windows.Forms.Form
$form1.Text = "Application Import"
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = "CenterScreen"
 
$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = "OK"
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)
 
$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = "Cancel"
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)
 
$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = "Select an application to import"
$form1.Controls.Add($label1)
 
$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
 
IF ($Applications -eq $Null)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "No Applications were found. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}
$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
IF ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "User selected $SelectedApplication"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "User cancelled input request. The script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Exporting $SelectedApplication" -PercentComplete 60
 
##Clearing error list
$Error.Clear()
 
$ExportPath = $ExportPathDirect + $SelectedApplication + ".zip"
 
IF (Test-Path $ExportPath)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "$SelectedApplication has already been exported. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Attempting application export"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication
 
IF ($Error.Count -ge "1")
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Error: " + $Error[0].Exception
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Something appears to have gone wrong. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Successfully exported $SelectedApplication"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Finalizing" -PercentComplete 90
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Retrieving deployment type information"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Attempting to rename folders"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Modify deployment type folders
foreach ($DeploymentType in $DeploymentTypes)
{
$Error.Clear()
$OldContentName = $ExportPathDirect + $SelectedApplication + "_files\" + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace("|","")
$NewContentName = $NewContentName.Replace("\","")
$NewContentName = $NewContentName.Replace("/","")
$NewContentName = $NewContentName.Replace("?","")
$NewContentName = $NewContentName.Replace("*","")
$NewContentName = $NewContentName.Replace("<","")
$NewContentName = $NewContentName.Replace(">","")
Rename-Item -NewName $NewContentName -Path $OldContentName
 
$NewContentPath = $ExportPathDirect + $SelectedApplication + "_files\" + $NewContentName
 
IF (Test-Path $NewContentPath)
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Successfully renamed " + "'" + $OldContentName + "' to '" + $NewContentPath + "'"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
}
ELSE
{
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Error: " + $Error[0].Exception
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "3"
$LogText = "Something appears to have gone wrong. Script will now exit"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
Exit
}
}
 
##Progress bar
Write-Progress -Activity "Application Export" -Status "Export of $SelectedApplication Completed" -PercentComplete 100
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "Application export script successfully completed"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"
 
##Output to Log
$Time = Get-Date -Format "HH:mm:ss.fff"
$Date = Get-Date -Format "MM-dd-yyyy"
$Type = "1"
$LogText = "+++Finished current thread+++"
$LogOutput = "<![LOG[$($LogText)]LOG]!><time=`"$($Time)+$($TZBias)`" date=`"$($Date)`" component=`"$($LogFileName)`" context=`"$($Context)`" type=`"$($Type)`" thread=`"$($StartDateTime)`" file=`"$($CurrentUser)`">"
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath "$LogLocation$LogFileName"


Make sure that the export destination folder exists prior to starting the script

As usual, if you have any issues or questions please post them in the comments section below

Windows PowerShell 3.0 – Outputting to a log for CMTrace

Recently I have been writing some very long PowerShell scripts for many different purposes but I ran into a common problem… How to deal with error handling.

There are many options available and the Windows PowerShell inbuilt -OnError function works fine, but I wanted something more, and more to the point, the customer wanted more. With this in mind I decided that the best option was going to be to export to a log file in a format that was readable with the CMTrace tool that comes packaged with SCCM.

Using this method means that I can output all information, including success and failures for each task that I run. It also means that as the script is running, users can monitor the progress in real-time.

 

The first thing you need to do is place the following variables somewhere at the top of your script

$CurrentUser = [Environment]::UserName
$StartDateTime = Get-Date -Format “HHmmss”
$TZbias = (Get-WmiObject -Query “Select Bias from Win32_TimeZone”).bias
$Component = “Application Export”
$LogFile = “C:\Logs\My Script.Log”

$CurrentUser will get the currently logged on username to use for the “File” column. This is not required but could (and usually is) set to the currently running script by way of the following line. I have chosen this method because my output file is the same as the script name making this useless, and many people will be logged on to the same server running the same script so i would like to be able to identify which user is running the script

$CurrentFile = $MyInvocation.MyCommand.Name

$StartDateTime fetches the current date and time as of the script launch time, this is going to be used for the “Thread” column. This is not required and is a personal choice. You could put many other things here, like the current username, script name, or just about anything else you want.

$TZbias gets the current timezone. This is required for the “Time” column. Without this the time will not be recognized and the log will not be in the correct format for CMTrace.exe.

$Component can be defined to anything you cant. I have it set to the current script. Have a play around with some of these variables and see what you get as the output

##Output to Log
$Time = Get-Date -Format “HH:mm:ss.fff”
$Date = Get-Date -Format “MM-dd-yyyy”
$Type = “1”
$LogText = “+++Starting new thread+++”
$LogOutput = “<![LOG[$($LogText)]LOG]!><time=`”$($Time)+$($TZBias)`” date=`”$($Date)`” component=`”$($Component)`” context=`”$($Context)`” type=`”$($Type)`” thread=`”$($StartDateTime)`” file=`”$($CurrentUser)`”>”
Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath “$LogLocation$LogFileName”

Each time you would like to output a line to the log you will need to paste the whole text above. Let’s go through now and break it down to see what is going on

$Time = Get-Date -Format “HH:mm:ss.fff”
$Date = Get-Date -Format “MM-dd-yyyy”

Both of these fetch the date or the time and bind it to a variable. This will need to be set each time to ensure that the time stamp for your log matches the time of the task running

$Type = “1”

This line will define the formatting of the line. “1” means information and will be a plain line with a white background. “2” means warning and will have a yellow background. “3” means error and will have a red background so set accordingly

$LogText = “+++Starting new thread+++”

This is the main meat of the script. This is where you can put all of your information about what is going on. I will always start and finish a script with a clear maker such as this.  Setting this as a variable and not putting it directly into the next line means that you ave more flexibility to change it, something like the following line

$LogText = “The following function has failed:” + $Function

This variable can also be set to a function (such as Get-Item) and will correctly display multiple lines in CMTrace

$LogOutput = “<![LOG[$($LogText)]LOG]!><time=`”$($Time)+$($TZBias)`” date=`”$($Date)`” component=`”$($Component)`” context=`”$($Context)`” type=`”$($Type)`” thread=`”$($StartDateTime)`” file=`”$($CurrentUser)`”>”

This is something that you should not need to alter. All of the data here that you could possibly want to modify is defined as variables. Just in case it is not clear, this is a single line. WordPress has limitations

Out-File -InputObject $LogOutput -Append -NoClobber -Encoding Default –FilePath “$LogFile”

This is where we output all of our hard work to a file. The “Append” function will place all text at the end of the file. “NoClobber” prevents the script from overwriting the log file with a new one each time you run this command

If you have any questions feel free to write something in the comments below

SCCM 2012 R2 – Exporting an application with PowerShell

Recently i have been doing some work for a client that likes to segregate their environment for test and production. While this is great for many reasons, one of the downsides is having to export an application and then import it into the next environment.

In this post i will be showing a PowerShell script that I developed to export applications from SCCM 2012 R2 to a predetermined folder. I will also do my best to explain each step of the script. As usual this script should work on SCCM 2012 and SCCM 2012 SP1 but I have only tested it on SCCM 2012 R2


##Script written by Liam Matthews 2015

##Variable list
$SCCMModule = “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1”
$SCCMSiteCode = “001”
$SCCMDrive = $SCCMSiteCode + “:”
$SCCMServer = “SCCM2012.liammatthewsit.com”
$ExportPathDirect = “E:\SCCM Export\”

##Progress bar
Write-Progress -Activity “Application Export” -Status “Starting Script” -PercentComplete 10

##Import SCCM PowerShell module
Import-Module $SCCMModule

##Connect to SCCM site
CD $SCCMDrive

##Pause to show previous display message
Start-Sleep 5

##Progress bar
Write-Progress -Activity “Application Export” -Status “Searching for applications” -PercentComplete 30

##Get applicaion list via WMI
$Applications = Get-WMIObject -ComputerName $SCCMServer -Namespace Root\SMS\Site_$SCCMSiteCode -Class “SMS_Application” | Select -unique LocalizedDisplayName | sort LocalizedDisplayName

##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form1 = New-Object System.Windows.Forms.Form
$form1.Text = “Application Import”
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = “CenterScreen”

$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = “OK”
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)

$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = “Cancel”
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)

$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = “Select an application to import”
$form1.Controls.Add($label1)

$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right

##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}

$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
if ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
}
else
{
exit
}

##Progress bar
Write-Progress -Activity “Application Export” -Status “Exporting $SelectedApplication” -PercentComplete 60

##Export selected application
$ExportPath = $ExportPathDirect + $SelectedApplication + “.zip”
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication

##Progress bar
Write-Progress -Activity “Application Export” -Status “Finalizing” -PercentComplete 90

##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
foreach ($DeploymentType in $DeploymentTypes)
{
$OldContentName = $ExportPathDirect + $SelectedApplication + “_files\” + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace(“|”,””)
$NewContentName = $NewContentName.Replace(“\”,””)
$NewContentName = $NewContentName.Replace(“/”,””)
$NewContentName = $NewContentName.Replace(“?”,””)
$NewContentName = $NewContentName.Replace(“*”,””)
$NewContentName = $NewContentName.Replace(“<“,””)
$NewContentName = $NewContentName.Replace(“>”,””)
Rename-Item -NewName $NewContentName -Path $OldContentName
}

##Progress bar
Write-Progress -Activity “Application Export” -Status “Export of $SelectedApplication Completed” -PercentComplete 100

##Pause to show previous display message
Start-Sleep 5


OK, so there is the script in its entirety, lets go ahead and start breaking this down

I will be skipping over anything to do with the progress bar and anything that has Start-Sleep as these are both for the user to see


##Variable list

$SCCMModule = “C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1”
$SCCMSiteCode = “001”
$SCCMDrive = $SCCMSiteCode + “:”
$SCCMServer = “SCCM2012.liammatthewsit.com”
$ExportPathDirect = “E:\SCCM Export\”

In this section we are defining the variables that we will be using in the script. Not all variables can be created at this stage because they may rely on information that we do not currently have, anything that does not will be listed here so that it is easy to find


##Import SCCM PowerShell module
Import-Module $SCCMModule

##Connect to SCCM site
CD $SCCMDrive

These commands import the SCCM module and then connect to the appropriate drive (Which would be your site code)


##Get applicaion list via WMI
$Applications = Get-WMIObject -ComputerName $SCCMServer -Namespace Root\SMS\Site_$SCCMSiteCode -Class “SMS_Application” | Select -unique LocalizedDisplayName | sort LocalizedDisplayName

This will connect to WMI on the SCCM server and fetch a list of all applications that currently exist, note that it filters out duplicated via “Select -unique”. Within WMI there will be several versions of an application listed, this is because each revision will be displayed as though it is a different application

Some people might be asking “Why are you not using the module command to get a list of applications?” I have used WMI because it is a faster query than “Get-CMApplication”. This may not be a big issue if you are using this in a lab or you only have a few dozen applications, but when the number of applications exceeds 500 then you might as well take your lunch break while it runs

If you are keen you can change this line to the following (you will also be able to remove the $SCCMServer variable

$Applications = Get-CMApplication | Select LocalizedDisplayName | sort LocalizedDisplayName


##Application Import Selection Form
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form1 = New-Object System.Windows.Forms.Form
$form1.Text = “Application Import”
$form1.Size = New-Object System.Drawing.Size(425,380)
$form1.StartPosition = “CenterScreen”

$OKButton1 = New-Object System.Windows.Forms.Button
$OKButton1.Location = New-Object System.Drawing.Point(300,325)
$OKButton1.Size = New-Object System.Drawing.Size(75,23)
$OKButton1.Text = “OK”
$OKButton1.DialogResult = [System.Windows.Forms.DialogResult]::OK
$OKButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.AcceptButton = $OKButton1
$form1.Controls.Add($OKButton1)

$CancelButton1 = New-Object System.Windows.Forms.Button
$CancelButton1.Location = New-Object System.Drawing.Point(225,325)
$CancelButton1.Size = New-Object System.Drawing.Size(75,23)
$CancelButton1.Text = “Cancel”
$CancelButton1.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$CancelButton1.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$form1.CancelButton = $CancelButton1
$form1.Controls.Add($CancelButton1)

$label1 = New-Object System.Windows.Forms.Label
$label1.Location = New-Object System.Drawing.Point(10,5)
$label1.Size = New-Object System.Drawing.Size(280,20)
$label1.Text = “Select an application to import”
$form1.Controls.Add($label1)

$listBox1 = New-Object System.Windows.Forms.Listbox
$listBox1.Location = New-Object System.Drawing.Size(10,30)
$listBox1.Width = 400
$listBox1.Height = 296
$listBox1.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right

This is a long one, I am not going to go through each line one at a time. this section creates a list box. The bulk of this code was taken from the Microsoft Technet article on list boxes, however is was modified to make it expandable when dragged


##Add items to form
foreach($Application in $Applications)
{
[void] $ListBox1.Items.Add($Application.LocalizedDisplayName)
}

$form1.Controls.Add($listBox1)
$form1.Topmost = $True
$result1 = $form1.ShowDialog()
if ($result1 -eq [System.Windows.Forms.DialogResult]::OK)
{
$SelectedApplication = $listBox1.SelectedItems
$SelectedApplication = $SelectedApplication[0]
}
else
{
exit
}

Here we are feting the data from the $Applications variable that we specified earlier and turning each entry into an item in the list box. The bottom half of this code also specifies the action that is taken when the OK button is pressed. In this case it is to assign the selected item to a variable that we can use later


##Export selected application
$ExportPath = $ExportPathDirect + $SelectedApplication + “.zip”
Export-CMApplication -IgnoreRelated -Path $ExportPath -Name $SelectedApplication

This is where all the magic happens. Here we are using all the variables that we have created to export the application to the folder the we specified at the top of the script


##Rename exported folders to deployment type name
$DeploymentTypes = Get-CMDeploymentType -ApplicationName $SelectedApplication
foreach ($DeploymentType in $DeploymentTypes)
{
$OldContentName = $ExportPathDirect + $SelectedApplication + “_files\” + $DeploymentType.ContentId
$NewContentName = $DeploymentType.LocalizedDisplayName
$NewContentName = $NewContentName.Replace(“|”,””)
$NewContentName = $NewContentName.Replace(“\”,””)
$NewContentName = $NewContentName.Replace(“/”,””)
$NewContentName = $NewContentName.Replace(“?”,””)
$NewContentName = $NewContentName.Replace(“*”,””)
$NewContentName = $NewContentName.Replace(“<“,””)
$NewContentName = $NewContentName.Replace(“>”,””)
Rename-Item -NewName $NewContentName -Path $OldContentName
}

This section is entirely optional, however, when the application is exported is puts the source files in a folder which is named with the Deployment Type GUID. When we are using a script to import this we will run into several problems because it will have no idea which folder belongs to which Deployment Type

This part of the script will rename each folder to the Deployment Type name so that it is easier to reference this later. When using this script though you will need to make sure that every Deployment Type name in your environment is unique, otherwise you are going to run into duplicate folder problems


Hopefully this script will prove useful and you have a good understanding of how it functions so that you can modify to suite your environment. If you have any questions or don’t understand something in the script then please comment

Stay tuned to the import script that will be following shortly

SCCM 2012 R2 – Deploying Microsoft Office 2013 Professional Plus

In this post I will give detailed instructions on how to deploy Microsoft Office 2013 Professional Plus

While I have done this with SCCM 2012 R2 in this guide the process is identical for SCCM 2012, 2012 SP1 and Microsoft Office 2010 Professional Plus

  1. Navigate to the location of the install media and open a CMD windowSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 01
  2. Type “setup.exe /admin” and press EnterSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 02
  3. When the product selection screen appears, select the product you wish to install (there will usually only be one)SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 03
  4. Navigate to “Install location and organization name”
  5. Type in your organization name (This step is not required)SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 05
  6. Navigate to “Licensing and User Interface”
  7. At my location I have a KMS server running which services all my clients for me. If you do not have this you can enter you MAK key here
  8. Click “I accept the terms in the License Agreement”
  9. Change “Display level” to “None”
  10. Ensure “Complete notice” remains unchecked and check “Suppress modal” and “No cancel”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 06
  11. Navigate to “Modify user settings”
  12. In the left of the two panes, navigate to “Microsoft Office 2013\Privacy\Trust Center”
  13. In the right pane double click on “Disable Opt-in Wizard on first run”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 07
  14. Select “Enabled” and click “OK”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 08
  15. Confirm that the status is now set to enabledSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 09
  16. Navigate to “Set feature installation states”
  17. In the right pane, click on “Microsoft Office” at the top of the tree and select “Run all from My Computer” from the drop-down menuSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 10
  18. One this is done save the MSP file. I have chosen to create a folder in the source media folder and call it “AdminFiles” and then the file name as Full.MSP, so that i can create individual installs for Excel, Word, etc. you can set any name, but it is much easier later if you don’t include spaces in the nameSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 11
  19. Within the SCCM console, navigate to “Software Library”, “Application Management”.
  20. Right click on “Applications” or a folder that you have created under this and click “Create Application”.SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 12
  21. Select “Manually specify the application information” then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 13
  22. Fill the the appropriate fields with the information you wish to provide, then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 14
  23. Fill the the appropriate fields with the information you wish to provide. If you wish to have a custom icon for the Application Catalog choose it here, then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 15
  24. When you reach the “Deployment Types” field, click “Add”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 16
  25. Within the wizard that opens up, select “Manually specify the deployment type information”. Then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 17
  26. In the “Name” field, type “Microsoft Office 2013 Professional Plus 32-Bit” as this is a 32-Bit deploymentSCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 18
  27. Enter the content location of the source media (extracted folder obviously)
  28. In the “Installation program” field, type “Setup.exe /AdminFile AdminFiles\Full.MSP” (Or whatever you called your MSP file)
  29. In the “Uninstall program” field, type “Setup.exe /Uninstall PROPLUS”
  30. Tick “Run installation and uninstall program as 32-bit process on 64-bit clients” and click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 19
  31. Click “Add Clause”
  32. Change “Setting Type” to “Windows Installer”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 20
  33. In the “Product code” field, click “Browse”
  34. Navigate to the source media, then proplus.ww. Select “proplusww.msi” and click “Open”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 21
  35. Select “This MSI product code must exit on the target system and the following condition must be met to indicate the presence of  this application”. Then click “OK”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 22
  36. Review information and click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 23
  37. Change “Installation behavior” to “Install for system”
  38. Change “Logon requirements” to “Whether or not a user is logged on”
  39. Change “Installation program visibility” to “Hidden”
  40. Change “Estimated installation time (minutes)” to “20”, then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 24
  41. If you would like to set requirements for the installation, such as minimum system requirements or primary device you can do so here, otherwise click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 25
  42. If you would like to set a dependency for this installation such as .Net you can do so here, otherwise click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 26
  43. Review all details here then click “Next”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 27
  44. Click “Close”SCCM 2012 R2 - Deploying Microsoft Office 2013 Professional Plus - 28
  45. Once you have completed all the above steps you can then deploy this package to your collections and test installation (preferable to a small test group such as yourself first)

SCOM 2012 R2 – Monitor a Windows Service

In this post I will give detailed instructions on how to monitor a Windows service through SCOM 2012 R2.

For this example I will be creating a monitor to monitor a service called ‘LP360 License Server’ which only occurs on a single server

  1. Within the SCOM console, Navigate to the ‘Authoring’ tab, expand ‘Management Pack Objects’ and select ‘Monitors’SCOM - Authoring Monitors
  2. Right click on ‘Monitors’, expand ‘Create a Monitor’ and select ‘Unit Monitor…’SCOM - Authoring Monitors - Create Unit Monitor
  3. Expand ‘Windows Services’ and select ‘Basic Service Monitor’SCOM - Create Unit Monitor - Monitor Type
  4. From the drop-down menu, select the management pack that you wish to place this monitor into. For custom monitors I create a management pack which i have called ‘Service Monitoring’. Then click ‘Next’
  5. Give your monitor a name that is unique and specific to what you are monitoring, I called mine ‘LP360 License Server’SCOM - Create Unit Monitor - General
  6. Select a monitor target. if this service is specific to an operating system such as Server 2008 R2, then search for and select ‘Windows Server 2008 R2 Computer’ of the Discovery management pack. Otherwise select ‘Windows Computer’ to cover all Windows server and clients. Then click ‘Next’
  7. Select a ‘Parent monitor’ from the drop-down box. for custom monitors I create an Aggregate Rollup Monitor which is called ‘Custom Services’. If you have not got a custom one, select ‘Availability’, it can always be changed later
  8. Because this service is only on one server, I do not want the monitor enabled by default. Untick ‘Montor is enabled’, then click ‘Next’
  9. If you are confident of the service name then type it in the box. I strongly suggest clicking on the ‘…’ buttonSCOM - Create Unit Monitor - Service Details
  10. Click ‘…’, type the server name and click ‘OK’SCOM - Create Unit Monitor - Select Windows Service - Select Computer
  11. Locate the service you wish to monitor and click ‘OK’. When you use this method there can be no doubt as to the service nameSCOM - Create Unit Monitor - Select Windows Service
  12. Click ‘Next’
  13. The default health states are fine for monitoring services, so unless you have specific requirements for something different, click ‘Next’SCOM - Create Unit Monitor - Configure Health
  14. I turn on alerts for all service monitors, select ‘Generate alerts for this monitorSCOM - Create Unit Monitor - Configure Alerts
  15. Change the ‘Alert description’ to something that makes sense for you, mine is ‘The ‘LP360 Server’ Service has stopped.”
  16. Click ‘Create’
  17. Search ‘Monitors’ for your monitor, locate it under ‘Windows Computer’. Right click, expand ‘Overrides’, then ‘Override the Monitor’. Select ‘For a specific object of class: Windows Computer’SCOM - Create Unit Monitor - Search - Override
  18. Search for the server that is running the service, select the server and click ‘OK’SCOM - Create Unit Monitor - Override - Select Object
  19. Tick the ‘Override’ box the corresponds to the ‘Enabled’ parameter, then change the ‘Override Value’ to ‘True’. Click ‘OK’SCOM - Create Unit Monitor - Override - Enable
  20. Navigate to the ‘Monitoring’ tab, then select ‘Windows Computers’SCOM - Search for server - Right Click - Open
  21. Search for the server you just created the override for. Right click on the server, expand ‘Open’, select ‘Health Explorer’ for your server
  22. Navigate to where you placed the monitor, in my case it was ‘Custom Services’. Depending on your setup, you may have to clear the filter, it will be a yellow bar at the top of the left paneSCOM - Health Explorer - LP360
  23. Click on State Change Events to confirm when the monitor became active. It could take up to an hour for the monitor to become active. This is dependent on the amount of clients you are monitoring and the performance of your SCOM infrastructure

SCSM 2012 R2 – Authoring Tools prerequisites check fails

Symptoms

When attempting to install the “Service Manager 2012 R2 Authoring Tools”, the prerequisites check fails on the “Microsoft Visual Studio Shell 2008 Check”Server Manager 2012 R2 Authoring Tools Prerequisites check fails

Troubleshooting

Verify that you have installed Visual Studio Shell 2008 from the Microsoft site

http://go.microsoft.com/fwlink/?LinkId=310149

Resolution

  1. Open the folder where the installation contents are locatedServer Manager 2012 R2 Authoring Tools location
  2. Browse to <InstallMedia>\Setup\enServer Manager 2012 R2 Authoring Tools Prerequisites XML file location
  3. Open Authoring_PrerequisiteInputFile.xmlServer Manager 2012 R2 Authoring Tools Prerequisites XML file contents
  4. Find the line <Check Order=”3″>Server Manager 2012 R2 Authoring Tools Prerequisites XML file contents1
  5. Remove this line from the XML document
  6. Find the next instance of </Check>Server Manager 2012 R2 Authoring Tools Prerequisites XML file contents2
  7. Remove this line from the XML document (The contents between these two points could also be removed, but this method makes it easier to undo the changes)
  8. Save the XML, overwriting the existing file
  9. Run Setup.exe again to verify that the process has worked, it should no longer check for Visual Studio 2008 ShellServer Manager 2012 R2 Authoring Tools Prerequisites check passed

SCOM 2012 SP1 – Agent Not Monitored

Symptoms

After installing/attaching agents the state is displayed as “Not monitored”

Image

Note: The agent will likely appear this way after first installing/attaching. wait 24 hours for it to auto-configure and only assume failure if it is still showing the same status after this

Resolution

  1. Connect to server that is displayed as “Not monitored”
  2. Open the run box and type “Services.msc”Run - services.msc
  3. Locate service named “System Center Management”Services - System Center Management
  4. Right click and stop serviceServices - Right click menu
  5. Browse to C:\Program Files\System Center Operations Manager\Agent
  6. Change folder name to “Health Service State Old”SCOM - Health Service State folder
  7. Start service and make sure that a new folder is createdSCOM - Health Service State folder new
  8. Wait 5 minutes and refresh SCOM console to ensure that agent is now monitoredSCOM - Agent monitored