02 January 2020

Microsoft Endpoint Manager Configuration Manager PowerShell Upgrade Script

With the advent of Microsoft Endpoint Manager Configuration Manager 1910, I started researching the upgrade requirements. I happened to run into a great blog post by fellow MVP Martin Bengtsson on what should be done before the upgrade takes place. That got me to thinking that most of the tasks he listed can be automated with PowerShell.

The script below will automate the following tasks from his list of prerequisites:

  • Backs up the cd.latest directory to the specified UNC path
  • Disables the following maintenance tasks
    • Backup site server
    • Delete aged client operations
    • Delete aged discovery data
    • Delete aged log data
  • Checks if the server is fully patched
  • Backs up the Configuration Manager SQL database
For the script to complete all of these tasks, the following PowerShell modules are required:
  • SQLServer
  • ConfigurationManager.psd1
  • PSWindowsUpdate
The SQLServer and PSWindowsUpdate can both be installed from the PowerShell Gallery. The ConfigurationManager.psd1 resides on the Configuration Manager server. The script needs to be run on that server, along with the other two modules being installed. The script will automatically locate the ConfigMgr module. You will also need to have a UNC path that will house the cd.latest directory backup and the SQL database backup. 

When executing this script, I recommend doing so using PowerShell ISE. The reason for this is because as you can see in the screenshot below, ISE will give you a status on the SQL server backup so you do not think the script might be locked up. 



When the server has passed all prereqs, the output will look like this:



NOTE: The script is intended to be executed on the Configuration Manager server. 

Here is the script. It can also be downloaded from my GitHub site


 <#  
      .SYNOPSIS  
           Configuration Manager Upgrade Prerequisite  
        
      .DESCRIPTION  
           This script will prepare configuration manager for the upgrade to the newest version. This is based off of Martin Bengtsson's Updating MEMCM (Microsoft Endpoint Manager Configuration Manager) to version 1910 on Christmas Eve. https://www.imab.dk/early-christmas-present-updating-memcm-microsoft-endpoint-manager-configuration-manager-to-version-1910-on-christmas-eve/  
        
      .PARAMETER MEMCMModule  
           Name of the ConfigMgr PowerShell Module  
        
      .PARAMETER MEMCMServer  
           FQDN Name of the Configuration Manager server  
        
      .PARAMETER MEMCMSiteDescription  
           Arbitrary description of the configuration manager server  
        
      .PARAMETER SiteCode  
           Three letter ConfigMgr site code  
        
      .PARAMETER BackupLocation  
           Location for SQL and SCCM backups  
        
      .PARAMETER SQLServer  
           FQDN of the SQL server  
        
      .PARAMETER SQLDatabase  
           Name of the SQL database  
        
      .PARAMETER SQLModuleName  
           Name of the SQL PowerShell Module  
        
      .NOTES  
           ===========================================================================  
           Created with:     SAPIEN Technologies, Inc., PowerShell Studio 2017 v5.4.142  
           Created on:       12/26/2019 12:22 PM  
           Created by:       Mick Pletcher  
           Filename:         ConfigMgrUpgrade.ps1  
           ===========================================================================  
 #>  
 [CmdletBinding()]  
 param  
 (  
      [ValidateNotNullOrEmpty()]  
      [string]$MEMCMModule = 'ConfigurationManager.psd1',  
      [ValidateNotNullOrEmpty()]  
      [string]$MEMCMServer,  
      [ValidateNotNullOrEmpty()]  
      [string]$MEMCMSiteDescription = 'MEMCM Site Server',  
      [ValidateNotNullOrEmpty()]  
      [string]$SiteCode,  
      [ValidateNotNullOrEmpty()]  
      [string]$BackupLocation,  
      [ValidateNotNullOrEmpty()]  
      [string]$SQLServer,  
      [ValidateNotNullOrEmpty()]  
      [string]$SQLDatabase,  
      [ValidateNotNullOrEmpty()]  
      [string]$SQLModuleName = 'SQLServer'  
 )  
   
 #Import SQL Server PowerShell Module  
 If ((Get-Module -Name ((Import-Module -Name $SQLModuleName -ErrorAction SilentlyContinue -Force -PassThru).Name)) -eq $null) {  
      #Install module if it does not exist  
      Install-Module -Name $SQLModuleName -Confirm:$false -Force  
      #Verify module got installed. Exit the script if it failed  
      If ((Get-Module -Name ((Import-Module -Name $SQLModuleName -ErrorAction SilentlyContinue -Force -PassThru).Name)) -eq $null) {  
           Write-Host 'Failed'  
           Exit 2  
      } else {  
           Import-Module -Name $SQLModuleName -ErrorAction SilentlyContinue -Force  
      }  
 }  
 If ((Get-Module).Name -contains $SQLModuleName) {  
   Write-Host ('Successfully imported' + [char]32 + $SQLModuleName + [char]32 + 'PowerShell module')  
 } else {  
   Write-Host ('Failed to load' + [char]32 + $SQLModuleName + [char]32 + 'PowerShell module')  
 }  
 #Import ConfigMgr PowerShell Module  
 $Module = (Get-ChildItem ((Get-WmiObject -Class 'sms_site' -Namespace 'Root\sms\site_BNA').InstallDir) -Filter $MEMCMModule -Recurse -ErrorAction SilentlyContinue)  
 Import-Module -Name $Module[0].FullName -Force  
 If ((Get-Module).Name -contains $Module[0].BaseName) {  
   Write-Host ('Successfully imported' + [char]32 + $Module[0].BaseName + [char]32 + 'PowerShell module')  
 } else {  
   Write-Host ('Failed to load' + [char]32 + $MEMCMModule + [char]32 + 'PowerShell module')  
 }  
 #Import PSWindowsUpdate PowerShell Module  
 Import-Module -Name PSWindowsUpdate -Force  
 If ((Get-Module).Name -contains 'PSWindowsUpdate') {  
   Write-host 'Successfully imported PSWindowsUpdate PowerShell module'  
 }  
 #Map ConfigMgr Drive  
 If ((Test-Path ($SiteCode + ':')) -eq $false) {  
      New-PSDrive -Name $SiteCode -PSProvider 'AdminUI.PS.Provider\CMSite' -Root $MEMCMServer -Description $MEMCMSiteDescription | Out-Null  
 }  
 #Change directory to ConfigMgr drive  
 Set-Location -Path ($SiteCode + ':')  
 #Backup cd.latest directory  
 $DIR = Get-ChildItem -Path (Get-WmiObject -Class 'sms_site' -Namespace 'Root\sms\site_BNA').InstallDir -Filter 'cd.latest' -Directory -Recurse -ErrorAction SilentlyContinue  
 robocopy $DIR.FullName ($BackupLocation + '\cd.latest') /e /eta ('/log:' + $BackupLocation + '\Robocopy.log')  
 If ($LastExitCode -le 7) {  
   Write-Host 'cd.latest backup succeeded' -ForegroundColor Yellow  
 } else {  
   Write-Host 'cd.latest backup failed' -ForegroundColor Red  
 }  
 #Disable Maintenance tasks for upgrade  
 $Enabled = $false  
 Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -like 'backup* site server'} | Set-CMSiteMaintenanceTask -Enabled $Enabled  
 If (((Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -like 'backup* site server'}).Enabled) -eq $false) {  
      $BackupSiteServer = $true  
 } else {  
      $BackupSiteServer = $false  
 }  
 If ($BackupSiteServer -eq $true) {  
      Write-Host 'Backup site server is disabled' -ForegroundColor Yellow  
 } else {  
      Write-Host 'Backup site server is still enabled' -ForegroundColor Red  
 }  
 Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged client operations'} | Set-CMSiteMaintenanceTask -Enabled $Enabled  
 If (((Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged client operations'}).Enabled) -eq $false) {  
      $AgedClientOperations = $true  
 } else {  
      $AgedClientOperations = $false  
 }  
 If ($AgedClientOperations -eq $true) {  
      Write-Host 'Delete aged client operations is disabled' -ForegroundColor Yellow  
 } else {  
      Write-Host 'Delete aged client operations is still enabled' -ForegroundColor Red  
 }  
 Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged discovery data'} | Set-CMSiteMaintenanceTask -Enabled $Enabled  
 If (((Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged discovery data'}).Enabled) -eq $false) {  
      $AgedDiscoveryData = $true  
 } else {  
      $AgedDiscoveryData = $false  
 }  
 If ($AgedDiscoveryData -eq $true) {  
      Write-Host 'Delete aged discovery data is disabled' -ForegroundColor Yellow  
 } else {  
      Write-Host 'Delete aged discovery data is still enabled' -ForegroundColor Red  
 }  
 Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged log data'} | Set-CMSiteMaintenanceTask -Enabled $Enabled  
 If (((Get-CMSiteMaintenanceTask | Where-Object {$_.ItemName -eq 'delete aged log data'}).Enabled) -eq $false) {  
      $AgedLogData = $true  
 } else {  
      $AgedLogData = $false  
 }  
 If ($AgedLogData -eq $true) {  
      Write-Host 'Delete aged log data is disabled' -ForegroundColor Yellow  
 } else {  
      Write-Host 'Delete aged log data is still enabled' -ForegroundColor Red  
 }  
 #Verify all windows updates are applied  
 If ((Get-WindowsUpdate -WindowsUpdate) -eq $null) {  
      $AppliedUpdates = $true  
 } else {  
      $AppliedUpdates = $false  
 }  
 If ($AppliedUpdates -eq $true) {  
      Write-Host ((Get-WmiObject -Class Win32_OperatingSystem).Caption + [char]32 + 'is fully patched') -ForegroundColor Yellow  
 } else {  
      Write-Host ((Get-WmiObject -Class Win32_OperatingSystem).Caption + [char]32 + 'is not fully patched') -ForegroundColor Red  
 }  
   
 #Backup the Configuration Manager SQL Database  
 Backup-SqlDatabase -ServerInstance $SQLServer -Database $SQLDatabase -BackupFile ($BackupLocation + '\CM_SQL_Backup.bak') -Checksum  
 $Verbose = $($Verbose = Invoke-Sqlcmd -ServerInstance $SQLServer -Database $SQLDatabase -Query ('RESTORE VERIFYONLY FROM DISK = N' + [char]39 + $BackupLocation + '\CM_SQL_Backup.bak' + [char]39) -QueryTimeout 0 -Verbose) 4>&1  
 If ($Verbose -like '*The backup set on file 1 is valid*') {  
      $SQLBackup = $true  
 } else {  
      $SQLBackup = $false  
 }  
 #Output the results  
 If ($SQLBackup -eq $true) {  
      Write-Host 'SQL backup was successful' -ForegroundColor Yellow  
 } else {  
      Write-Host 'SQL backup failed' -ForegroundColor Red  
 }  
   
Reactions:

0 comments:

Post a Comment