NTS.Tools.MSConfigMgr.psm1
function Install-WADK { <# .Description this function can be used to install Windows ADK and Windows ADK PE .Parameter Latest use if you want the latest version .Parameter Features a list of Windows ADK to be installed .Parameter IncludeWinPE use if you want windows adk pe installed .Parameter Outpath path where the install and log files are saved .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Install-WADK -Latest -IncludeWinPE -Features OptionId.DeploymentTools, OptionId.UserStateMigrationTool .NOTES requires internet connection #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet("latest","W11_22H2","W11_21H2","W10_2004")] [string] $Version, [Parameter(Mandatory = $true)] [ValidateSet( "OptionId.ApplicationCompatibilityToolkit", "OptionId.DeploymentTools", "OptionId.ImagingAndConfigurationDesigner", "OptionId.ICDConfigurationDesigner", "OptionId.UserStateMigrationTool", "OptionId.VolumeActivationManagementTool", "OptionId.WindowsPerformanceToolkit", "OptionId.UEVTools", "OptionId.AppmanSequencer", "OptionId.AppmanAutoSequencer", "OptionId.MediaeXperienceAnalyzer", "OptionId.MediaeXperienceAnalyzer", "OptionId.WindowsAssessmentToolkit" )] [string[]] $Features, [Parameter(Mandatory = $false)] [switch] $IncludeWinPE, [Parameter(Mandatory = $false)] [string] $Outpath = "$($env:ProgramData)\NTS\Windows_ADK", [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath switch ($Version) { {($PSItem -eq "latest") -or ($PSItem -eq "W11_22H2")} { $WADK_Download_URL = "https://download.microsoft.com/download/6/7/4/674ec7db-7c89-4f2b-8363-689055c2b430/adk/adksetup.exe" $WADK_PE_Download_URL = "https://download.microsoft.com/download/5/2/5/525dcde0-c7b8-487a-894d-0952775a78c7/adkwinpeaddons/adkwinpesetup.exe" } "W11_21H2" { $WADK_Download_URL = "https://download.microsoft.com/download/1/f/d/1fd2291e-c0e9-4ae0-beae-fbbe0fe41a5a/adk/adksetup.exe" $WADK_PE_Download_URL = "https://download.microsoft.com/download/5/5/e/55e3e34a-5708-46cd-a90d-92044c29336b/adkwinpeaddons/adkwinpesetup.exe" } "W10_2004" { $WADK_Download_URL = "https://download.microsoft.com/download/8/6/c/86c218f3-4349-4aa5-beba-d05e48bbc286/adk/adksetup.exe" $WADK_PE_Download_URL = "https://download.microsoft.com/download/3/c/2/3c2b23b2-96a0-452c-b9fd-6df72266e335/adkwinpeaddons/adkwinpesetup.exe" } Default { throw "this version '$($Version)' is not supported" } } try { New-ItemIfNotExists -Path $Outpath -ItemType Directory $Outpath = (Get-Item -Path $Outpath).FullName if ($Features.count -gt 1) { $Features | ForEach-Object { [string]$Features_Selected = $Features_Selected + " " + $PSItem } } $WADK_Path = "$($Outpath)\adksetup-$($Version).exe" $WADK_LogPath = "$($Outpath)\install-adksetup-$($Version).log" $WADK_PE_Path = "$($Outpath)\adkwinpesetup-$($Version).exe" $WADK_PE_LogPath = "$($Outpath)\install-adkwinpesetup-$($Version).log" # download Write-ToLogOrConsole @LogParam -Severity Info -Message "downloading adk setup files" Start-FileDownload -DownloadURL $WADK_Download_URL -FileOutPath $WADK_Path # install Write-ToLogOrConsole @LogParam -Severity Info -Message "installing adk with the features $($Features_Selected)" $Process = Start-Process -FilePath $WADK_Path -ArgumentList "/quiet /norestart /features $($Features_Selected) /l $($WADK_LogPath)" -NoNewWindow -Wait -PassThru if ($Process.ExitCode -ne 0) { throw "check log at $($WADK_LogPath)" } if ($IncludeWinPE -eq $true) { # download Write-ToLogOrConsole @LogParam -Severity Info -Message "downloading adk pe setup files" Start-FileDownload -DownloadURL $WADK_PE_Download_URL -FileOutPath $WADK_PE_Path # install Write-ToLogOrConsole @LogParam -Severity Info -Message "installing adk pe" $Process = Start-Process -FilePath $WADK_PE_Path -ArgumentList "/quiet /norestart /features OptionId.WindowsPreinstallationEnvironment /l $($WADK_PE_LogPath)" -NoNewWindow -Wait -PassThru if ($Process.ExitCode -ne 0) { throw "check log at $($WADK_PE_LogPath)" } } Start-FolderCleanUp -FolderToRemove $Outpath } catch { $ErrorMessage = "something went wrong $($PSItem.Exception.Message)" Write-ToLogOrConsole @LogParam -Severity Error -Message $ErrorMessage throw $ErrorMessage } } function Initialize-CM_MP_Prereq { <# .Description use this function to install configmgr management point prerequesits .Example Initialize-CM_MP_Prereq .NOTES #> $Features = @( "NET-Framework-Core" "FileAndStorage-Services" "Storage-Services" "Web-Server" "Web-WebServer" "Web-Common-Http" "Web-Default-Doc" "Web-Dir-Browsing" "Web-Http-Errors" "Web-Static-Content" "Web-Http-Redirect" "Web-DAV-Publishing" "Web-Health" "Web-Http-Logging" "Web-Custom-Logging" "Web-Log-Libraries" "Web-ODBC-Logging" "Web-Request-Monitor" "Web-Http-Tracing" "Web-Performance" "Web-Stat-Compression" "Web-Dyn-Compression" "Web-Security" "Web-Filtering" "Web-Basic-Auth" "Web-CertProvider" "Web-Client-Auth" "Web-Digest-Auth" "Web-Cert-Auth" "Web-IP-Security" "Web-Url-Auth" "Web-Windows-Auth" "Web-App-Dev" "Web-Net-Ext" "Web-Net-Ext45" "Web-AppInit" "Web-ASP" "Web-Asp-Net" "Web-Asp-Net45" "Web-CGI" "Web-ISAPI-Ext" "Web-ISAPI-Filter" "Web-Includes" "Web-WebSockets" "Web-Ftp-Server" "Web-Ftp-Service" "Web-Ftp-Ext" "Web-Mgmt-Tools" "Web-Mgmt-Console" "Web-Mgmt-Compat" "Web-Metabase" "Web-Lgcy-Mgmt-Console" "Web-Lgcy-Scripting" "Web-WMI" "Web-Scripting-Tools" "Web-Mgmt-Service" "NET-Framework-Features" "NET-Framework-Core" "NET-Framework-45-Features" "NET-Framework-45-Core" "NET-Framework-45-ASPNET" "NET-WCF-Services45" "NET-WCF-HTTP-Activation45" "NET-WCF-MSMQ-Activation45" "NET-WCF-Pipe-Activation45" "NET-WCF-TCP-Activation45" "NET-WCF-TCP-PortSharing45" "BITS" "BITS-IIS-Ext" "BITS-Compact-Server" "MSMQ" "MSMQ-Services" "MSMQ-Server" "Windows-Defender" "RDC" "RSAT" "RSAT-Feature-Tools" "RSAT-Bits-Server" "System-DataArchiver" "PowerShellRoot" "PowerShell" "PowerShell-V2" "WAS" "WAS-Process-Model" "WAS-Config-APIs" "WoW64-Support" "XPS-Viewer" ) try { Write-Output "installing required features for configmgr management point" Install-WindowsFeature -Name $Features | Out-Null } catch { throw $PSItem.Exception.Message } } function Initialize-CM_DP_Prereq { <# .Description use this function to install configmgr distribution point prerequesits .Example Initialize-CM_DP_Prereq .NOTES #> $Features = @( "FileAndStorage-Services" "File-Services" "FS-FileServer" "Storage-Services" "Web-Server" "Web-WebServer" "Web-Common-Http" "Web-Default-Doc" "Web-Dir-Browsing" "Web-Http-Errors" "Web-Static-Content" "Web-Http-Redirect" "Web-Health" "Web-Http-Logging" "Web-Performance" "Web-Stat-Compression" "Web-Security" "Web-Filtering" "Web-Windows-Auth" "Web-App-Dev" "Web-ISAPI-Ext" "Web-Mgmt-Tools" "Web-Mgmt-Console" "Web-Mgmt-Compat" "Web-Metabase" "Web-WMI" "Web-Scripting-Tools" "NET-Framework-45-Features" "NET-Framework-45-Core" "NET-WCF-Services45" "NET-WCF-TCP-PortSharing45" "Windows-Defender" "RDC" "System-DataArchiver" "PowerShellRoot" "PowerShell" "WoW64-Support" "XPS-Viewer" ) try { if ((Test-Path -Path "$($env:SystemDrive)\NO_SMS_ON_DRIVE.SMS") -eq $false) { Write-Output "creating NO_SMS_ON_DRIVE.SMS on boot volume" New-Item -Path "$($env:SystemDrive)\NO_SMS_ON_DRIVE.SMS" -ItemType File | Out-Null } else { Write-Output "file NO_SMS_ON_DRIVE.SMS on boot volume already exists" } Write-Output "installing required features for configmgr distribution point" Install-WindowsFeature -Name $Features | Out-Null } catch { throw $PSItem.Exception.Message } } function Initialize-CM_SiteServer_Prereq { <# .Description use this function to install configmgr site server prerequesits .Example Initialize-CM_SiteServer_Prereq .NOTES #> $Features = @( "RDC" "UpdateServices-RSAT" "NET-Framework-Features" ) try { if ((Test-Path -Path "$($env:SystemDrive)\NO_SMS_ON_DRIVE.SMS") -eq $false) { Write-Output "creating NO_SMS_ON_DRIVE.SMS on boot volume" New-Item -Path "$($env:SystemDrive)\NO_SMS_ON_DRIVE.SMS" -ItemType File | Out-Null } else { Write-Output "file NO_SMS_ON_DRIVE.SMS on boot volume already exists" } Write-Output "installing required features for configmgr site server" Install-WindowsFeature -Name $Features -IncludeAllSubFeature | Out-Null } catch { throw $PSItem.Exception.Message } } function Initialize-CM_SUP_Prereq { <# .Description use this function to install configmgr software update point prerequesits .Example Initialize-CM_SUP_Prereq .NOTES #> try { Write-Output "installing required features for configmgr software update point" Install-WindowsFeature -Name RDC, UpdateServices-RSAT -IncludeAllSubFeature | Out-Null } catch { throw $PSItem.Exception.Message } } function Add-CM_ADContainer { <# .Description use this function to create the system management container in ad and add permissions to the local server .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Add-CM_ADContainer .NOTES this should be run on the site system server #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { Import-Module -Name "ActiveDirectory" $AD_DistinguishedName = (Get-ADDomain).DistinguishedName $CM_ContainerName = "SYSTEM MANAGEMENT" Write-ToLogOrConsole @LogParam -Severity Info -Message "adding container 'SYSTEM MANAGEMENT'" if ($null -eq (Get-ADObject -Filter 'ObjectClass -eq "container"' -SearchBase "CN=System,$($AD_DistinguishedName)" | Where-Object -Property Name -eq $CM_ContainerName)) { New-ADObject -Name $CM_ContainerName -Path "CN=System,$($AD_DistinguishedName)" -Type Container } Write-ToLogOrConsole @LogParam -Severity Info -Message "adding permissions for the ad container" $path = "AD:\CN=$($CM_ContainerName),CN=System,$($AD_DistinguishedName)" $ADCompObject = Get-ADComputer -Identity $env:COMPUTERNAME $adRights = [DirectoryServices.ActiveDirectoryRights]::GenericAll $accessType = [Security.AccessControl.AccessControlType]::Allow $inheritance = [DirectoryServices.ActiveDirectorySecurityInheritance]::All $fullAccessACE = New-Object -TypeName DirectoryServices.ActiveDirectoryAccessRule -ArgumentList @($ADCompObject.SID, $adRights, $accessType, $inheritance) $acl = Get-Acl -Path $path $acl.AddAccessRule($fullAccessACE) Set-Acl -Path $path -AclObject $acl } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Install-WSUS { <# .Description this will install the required functions for wsus and do the post install tasks .Parameter UseWID wsus with windows internal database .Parameter UseSQL wsus with mssql database .Parameter WSUSFilePath where should the file be stored .Parameter SQLInstance sql instance for wsus .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Set-Interface -InterfaceObject $SFP10G_NICs[0] -IPAddress $CLU1_IPAddress -NetPrefix $NetPrefix -DefaultGateway $CLU_DefaultGateway -DNSAddresses $CLU_DNSAddresses -NewName "Datacenter-1" .NOTES https://smsagent.blog/2014/02/07/installing-and-configuring-wsus-with-powershell/ #> [CmdletBinding()] param ( [Parameter(ParameterSetName = 'WID')] [switch] $UseWID, [Parameter(ParameterSetName = 'SQL')] [switch] $UseSQL, [Parameter(ParameterSetName = 'WID', Mandatory = $true)] [Parameter(ParameterSetName = 'SQL', Mandatory = $true)] [string] $WSUSFilePath, [Parameter(ParameterSetName = 'SQL', Mandatory = $true)] [string] $SQLInstance, # "MyServer\MyInstance" [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath New-ItemIfNotExists -Path $WSUSFilePath -ItemType Directory try { if ($UseWID -eq $true) { Write-ToLogOrConsole @LogParam -Severity Info -Message "installing required features for wsus" Install-WindowsFeature "UpdateServices" -IncludeManagementTools -WarningAction "SilentlyContinue" | Out-Null Write-ToLogOrConsole @LogParam -Severity Info -Message "doing postinstall with wid" Start-Process -FilePath "$($env:ProgramFiles)\Update Services\Tools\wsusutil.exe" -ArgumentList "postinstall CONTENT_DIR=$($WSUSFilePath)" -NoNewWindow -Wait } elseif ($UseSQL -eq $true) { Write-ToLogOrConsole @LogParam -Severity Info -Message "installing required features for wsus" Install-WindowsFeature -Name "UpdateServices-Services", "UpdateServices-DB" -IncludeManagementTools -WarningAction "SilentlyContinue" | Out-Null Write-ToLogOrConsole @LogParam -Severity Info -Message "doing postinstall with sql instance $($SQLInstance)" Start-Process -FilePath "$($env:ProgramFiles)\Update Services\Tools\wsusutil.exe" -ArgumentList "postinstall SQL_INSTANCE_NAME=$($SQLInstance) CONTENT_DIR=$($WSUSFilePath)" -NoNewWindow -Wait } } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Confirm-CM_Prerequisites { <# .Description this function will search for the configmgr install volume and run the prerequisite checks for a site server .Parameter PrereqchkFilePath path to the prereqchk.exe .Parameter CM_SiteServerFQDN fqdn of the site server .Parameter CM_SQL_Site_Instance database server with instance name, eg. <fqdn of the site server>\<instancename> .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Confirm-CM_Prerequisites -CM_SiteServerFQDN $CM_SiteServerFQDN -CM_SQL_Site_Instance ($CM_SiteServerFQDN + "\" + $using:CM_SQL_Site_InstanceName) .NOTES https://learn.microsoft.com/en-us/mem/configmgr/core/servers/deploy/install/prerequisite-checker #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $PrereqchkFilePath, [Parameter(Mandatory = $true)] [string] $CM_SiteServerFQDN, [Parameter(Mandatory = $true)] [string] $CM_SQL_Site_Instance, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath $CM_PrereqchkLogFilePath = "$($env:SystemDrive)\ConfigMgrPrereq.log" try { if ($PrereqchkFilePath -eq "") { $CM_SetupVolumes = Get-CM_Setup_Volume if ($CM_SetupVolumes.DriveLetter.count -eq 1) { $CM_Prereqchk_Filepath = "$(($CM_SetupVolumes).DriveLetter):\SMSSETUP\BIN\X64\prereqchk.exe" } else { throw "there are more than one or less than one installation media for configmgr" } } else { if (Test-Path -Path $PrereqchkFilePath) { $CM_Prereqchk_Filepath = $PrereqchkFilePath } else { throw "cannot find prereqchk.exe at $($PrereqchkFilePath)" } } if (Test-Path -Path $CM_PrereqchkLogFilePath) { Remove-Item -Path $CM_PrereqchkLogFilePath -Force | Out-Null } try { Write-ToLogOrConsole @LogParam -Severity Info -Message "checking prerequisites for the site server role & admin console" Start-Process -FilePath $CM_Prereqchk_Filepath -ArgumentList "/NOUI /PRI /SDK $($CM_SiteServerFQDN) /SQL $CM_SQL_Site_Instance /SCP" -Wait -NoNewWindow Start-Process -FilePath $CM_Prereqchk_Filepath -ArgumentList "/NOUI /ADMINUI" -Wait -NoNewWindow } catch { throw "failed to run $($CM_Prereqchk_Filepath) - $($PSItem.Exception.Message)" } $Content = Get-Content -Path $CM_PrereqchkLogFilePath $SuccessMessage = $Content -like "*Prerequisite checking is completed.*" $FailureMessage = $Content -like "*ERROR:*" if ($null -eq $SuccessMessage[0] -and $null -ne $FailureMessage[0]) { if ($FailureMessage -like "*ERROR: Failed to connect to SQL Server 'master' db.*" -and $FailureMessage.Count -gt 2) { throw "found errors in log $($CM_PrereqchkLogFilePath):`n$($FailureMessage)" } } Write-ToLogOrConsole @LogParam -Severity Info -Message "all prerequisites are met for configmgr installation" } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Uninstall-ConfigMgrAgent { <# .Description this function uninstalls the configmgr agent .Example Uninstall-ConfigMgrAgent .NOTES https://learn.microsoft.com/en-us/mem/configmgr/core/servers/deploy/install/prerequisite-checker #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [bool] $SkipCleanup = $false ) try { $CCMExecServiceName = "CcmExec" $CCMSetupFilePath = "$($env:windir)\ccmsetup\ccmsetup.exe" if ($null -ne (Get-Service -Name $CCMExecServiceName -ErrorAction SilentlyContinue) -or (Test-Path -Path $CCMSetupFilePath)) { Write-Output "starting configmgr Agent uninstall" Start-Process -FilePath $CCMSetupFilePath -ArgumentList "/uninstall" -Wait -NoNewWindow $LogFileContent = Get-Content -Path "$($env:windir)\ccmsetup\logs\CCMSetup.log" $SuccesMessage = $LogFileContent -like "*[LOG[Uninstall succeeded.]LOG]*" } else { Write-Output "Service $($CCMExecServiceName) not found and no ccmsetup.exe, skipping" $SkipCleanup = $true } } catch { throw "error while uninstalling the agent - $($PSItem.Exception.Message)" } try { if ($SkipCleanup -eq $false) { if ($SuccesMessage.Count -gt 0) { Write-Output "finished configmgr Agent uninstall" Write-Output "doing cleanup" if (Test-Path -Path "$($env:windir)\CCM") { $Items = Get-ChildItem -Path "$($env:windir)\CCM" $Items | ForEach-Object { if ((Test-FileLock -Path $PSItem.FullName) -ne $true) { Remove-Item -Path $PSItem.FullName -Force -Recurse | Out-Null } } } if (Test-Path -Path "$($env:windir)\ccmsetup") { Remove-Item -Path "$($env:windir)\ccmsetup" -Force -Recurse | Out-Null } Write-Output "finished doing cleanup" } else { throw "uninstall was not successful $($PSItem.Exception.Message)" } } if (Test-RebootPending) { Write-Output "reboot to complete uninstall" } } catch { throw "error doing cleanup - $($PSItem.Exception.Message)" } } function Get-CM_Setupfiles { <# .Description downloads the eval setup of configmgr current branch .Parameter Version version of the iso .Parameter Outpath path where the setup file is stored .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Get-CM_Setupfiles -Version 2303 -Outpath $Outpath .NOTES downloads the configmgr current branch eval setup #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [ValidateSet("current", "2303")] [string] $Version = "current", [Parameter(Mandatory = $true)] [string] $Outpath, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath switch ($Version) { "current" { $DownloadURL = "https://go.microsoft.com/fwlink/p/?LinkID=2195628&clcid=0x409&culture=en-us&country=us" } "2303" { $DownloadURL = "https://download.microsoft.com/download/0/0/1/001d97e2-c427-4d4b-ad30-1556ee0ff1b0/MCM_Configmgr_2303.exe?culture=en-us&country=us" } Default { throw "no version was selected" } } New-ItemIfNotExists -Path $Outpath -ItemType Directory $Outpath = (Get-Item -Path $Outpath).FullName $SetupPath = "ConfigMgr-$($Version)-CB-Eval.exe" $SetupFullPath = "$($Outpath)\$($SetupPath)" try { Start-FileDownload -DownloadURL $DownloadURL -FileOutPath $SetupFullPath Write-ToLogOrConsole @LogParam -Severity Info -Message "finished download, starting extraction to $($Outpath)" Start-Process -FilePath $SetupFullPath -ArgumentList "-s" -NoNewWindow -Wait -WorkingDirectory $Outpath $FoldersFound = Get-ChildItem -Path $Outpath | Where-Object -Property Attributes -Like "*Directory*" $FolderWithSetup = $FoldersFound | ForEach-Object { $Items = Test-Path -Path "$($PSItem.FullName)\SMSSETUP\BIN\X64\setup.exe" if($Items){ return $PSItem } } if($FolderWithSetup.count -gt 1) { throw "found more than one folder with the setup files" } Rename-Item -Path $FolderWithSetup.FullName -NewName SetupFiles Write-ToLogOrConsole @LogParam -Severity Info -Message "finished, setup files can be found at $($Outpath)\SetupFiles" } catch { $ErrorMessage = "error downloading eval setup - $($PSItem.Exception.Message)" Write-ToLogOrConsole @LogParam -Severity Error -Message $ErrorMessage throw $ErrorMessage } } function Get-CM_PrerequisiteFiles { <# .Description this function calls SMSSETUP\BIN\X64\Setupdl.exe from configmgr iso .Parameter SetupdlFilePath path to the Setupdl.exe .Parameter Outpath save path of the downloaded files .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Get-CM_PrerequisiteFiles -SetupdlFilePath "$($using:LocalConfigMgrSetupPath)\SMSSETUP\BIN\X64\setupdl.exe" -Outpath $PrerequisitePath .NOTES https://learn.microsoft.com/en-us/mem/configmgr/core/servers/deploy/install/setup-downloader #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $SetupdlFilePath, [Parameter(Mandatory = $true)] [string] $Outpath, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { New-ItemIfNotExists -Path $Outpath -ItemType Directory $Outpath = Resolve-Path $Outpath $LogFilePath = "$($env:SystemDrive)\ConfigMgrSetup.log" if ($SetupdlFilePath -eq "") { $CM_SetupVolumes = Get-CM_Setup_Volume if ($CM_SetupVolumes.DriveLetter.count -eq 1) { $CM_SetupFileDownloaderPath = "$(($CM_SetupVolumes).DriveLetter):\SMSSETUP\BIN\X64\Setupdl.exe" } else { throw "there are more than one or less than one installation media for configmgr" } } else { if (Test-Path -Path $SetupdlFilePath) { $CM_SetupFileDownloaderPath = $SetupdlFilePath } else { throw "cannot find Setupdl.exe at $($SetupdlFilePath)" } } if (Test-Path -Path $LogFilePath) { Remove-Item -Path $LogFilePath -Force | Out-Null } try { Write-ToLogOrConsole @LogParam -Severity Info -Message "starting download of configmgr setup prerequisite files" Start-Process -FilePath $CM_SetupFileDownloaderPath -ArgumentList "/NoUI $($Outpath)" -Wait -NoNewWindow } catch { throw "failed to run $($LogFilePath) - $($PSItem.Exception.Message)" } $Content = Get-Content -Path $LogFilePath $SuccessMessage = $Content -like "*INFO: Setup downloader * FINISHED*" if ($null -eq $SuccessMessage[0]) { throw "no success message, check log $($LogFilePath)" } Write-ToLogOrConsole @LogParam -Severity Info -Message "finished download of configmgr setup files" } catch { $ErrorMessage = "error downloading configmgr setup files - $($PSItem.Exception.Message)" Write-ToLogOrConsole @LogParam -Severity Error -Message $ErrorMessage throw $ErrorMessage } } function Get-CM_Setup_Volume { <# .Description this function searches all volumes for the setup.exe from the configmgr iso .Example Uninstall-ConfigMgrAgent .NOTES #> try { $Volumes = Get-Volume | Where-Object -FilterScript { $PSItem.DriveLetter -NE "C" -and $null -ne $PSItem.DriveLetter } $Volumes | ForEach-Object { if (Test-Path -Path "$($PSItem.DriveLetter):\SMSSETUP\BIN\X64\setup.exe") { return $PSItem } } } catch { throw $PSItem.Exception.Message } } function Initialize-CM_Schema_To_AD { <# .Description this function extends the schema using extadsch.exe for the configmgr .Parameter ExtadschFilePath path to the extadsch.exe .Example Initialize-CM_Schema_To_AD .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .NOTES should be run on the siteserver with domain admin privileges temporarily the current user is added to the schema admin group #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $ExtadschFilePath, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { if (((Get-ADGroupMember -Identity 'Schema Admins').Name -eq $env:USERNAME) -ne $true) { Write-ToLogOrConsole @LogParam -Severity Info -Message "adding current user to schema admins" Add-ADGroupMember -Identity 'Schema Admins' -Members $env:USERNAME } if ($ExtadschFilePath -eq "") { $CM_SetupVolumes = Get-CM_Setup_Volume if ($CM_SetupVolumes.DriveLetter.count -eq 1) { $CM_Extadsch_Filepath = "$(($CM_SetupVolumes).DriveLetter):\SMSSETUP\BIN\X64\extadsch.exe" } else { throw "there are more than one or less than one installation media for configmgr" } } else { if (Test-Path -Path $ExtadschFilePath) { $CM_Extadsch_Filepath = $ExtadschFilePath } else { throw "cannot find extadsch.exe at $($ExtadschFilePath)" } } Write-ToLogOrConsole @LogParam -Severity Info -Message "extending schema" Start-Process -FilePath $CM_Extadsch_Filepath -Wait -NoNewWindow $LogFilePath = "$($env:SystemDrive)\ExtADSch.log" $LogFileContent = Get-Content -Path $LogFilePath $SuccessMessage = $LogFileContent -like "*Successfully extended the Active Directory schema.*" $FailedMessages = $LogFileContent -like "*Failed to create*" if ($null -ne $SuccessMessage[0]) { Write-ToLogOrConsole @LogParam -Severity Info -Message "finished extending schema" } else { throw "something went wrong, check the log at $($LogFilePath):`n$($FailedMessages[0])" } if (((Get-ADGroupMember -Identity 'Schema Admins').Name -eq $env:USERNAME) -ne $true) { Write-ToLogOrConsole @LogParam -Severity Info -Message "removing current user from schema admins" Remove-ADGroupMember -Identity 'Schema Admins' -Members $env:USERNAME -Confirm:$false } } catch { throw "error extending schema - $($PSItem.Exception.Message)" } } function Install-CM_SiteServer { <# .Description this function extends the schema using extadsch.exe for the configmgr .Parameter SetupPath path to the Setup.exe .Parameter SiteName FriendlyName of the Site .Parameter SiteCode sitecode .Parameter PrerequisitePath path to prerequisite files .Parameter SQLServer fqdn of the sql server, can be the local server .Parameter SQLInstanceName name of the instance .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .Example Install-CM_SiteServer -SiteName $CM_SiteName ` -SiteCode $CM_SiteCode ` -PrerequisitePath $PrerequisitePath ` -SQLServer $CM_Site_SQLServer ` -SQLInstanceName $CM_SQL_Site_InstanceName .NOTES https://learn.microsoft.com/en-us/mem/configmgr/core/servers/deploy/install/command-line-options-for-setup #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $SetupPath, [Parameter(Mandatory = $true)] [string] $SiteName, [Parameter(Mandatory = $true)] [string] $SiteCode, [Parameter(Mandatory = $true)] [string] $PrerequisitePath, [Parameter(Mandatory = $true)] [string] $SQLServer, [Parameter(Mandatory = $true)] [string] $SQLInstanceName, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { $SiteServer = $($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN) $SetupIniPath = "$($env:ProgramData)\NTS\ConfigMgr\SetupConfig.ini" $LogFilePath = "$($env:SystemDrive)\ConfigMgrSetup.log" $ConfigurationIni = "[Identification] Action=InstallPrimarySite CDLatest=0 [Options] ProductID=Eval SiteCode=$($SiteCode) SiteName=$($SiteName) SMSInstallDir=$($env:SystemDrive)\Program Files\Microsoft Configuration Manager SDKServer=$($env:COMPUTERNAME + "." + $env:USERDNSDOMAIN) PrerequisiteComp=1 PrerequisitePath=$($PrerequisitePath) AdminConsole=1 JoinCEIP=0 MobileDeviceLanguage=0 RoleCommunicationProtocol=HTTPorHTTPS ClientsUsePKICertificate=0 [SQLConfigOptions] SQLServerName=$($SQLServer + "\" + $SQLInstanceName) DatabaseName=$("CM_" + $SiteCode) [CloudConnectorOptions] CloudConnector=1 CloudConnectorServer=$($SiteServer) UseProxy=0 [SABranchOptions] SAActive=0 CurrentBranch=1 " New-Item -Path $SetupIniPath -ItemType File -Force | Out-Null Set-Content -Path $SetupIniPath -Value $ConfigurationIni Write-ToLogOrConsole @LogParam -Severity Info -Message "starting configmgr site server installtion" Write-ToLogOrConsole @LogParam -Severity Info -Message "to see the progress please view this log $($env:SystemDrive)\ConfigMgrSetup.log on the site server" Write-ToLogOrConsole @LogParam -Severity Info -Message "this can take a while" if ($SetupPath -eq "") { $CM_SetupVolumes = Get-CM_Setup_Volume if ($CM_SetupVolumes.DriveLetter.count -eq 1) { Start-Process -FilePath "$(($CM_SetupVolumes).DriveLetter):\SMSSETUP\BIN\X64\setup.exe" -ArgumentList "/SCRIPT $($SetupIniPath)" -Wait -NoNewWindow } else { throw "there are more than one or less than one installation media for configmgr" } } else { if (Test-Path -Path $SetupPath) { Start-Process -FilePath $SetupPath -ArgumentList "/SCRIPT $($SetupIniPath)" -Wait -NoNewWindow } else { throw "cannot find the setup file at $($SetupPath)" } } $Content = Get-Content -Path $LogFilePath $SuccessMessage = $Content -like "*~===================== Completed Configuration Manager Server Setup =====================*" if ($null -eq $SuccessMessage[0]) { throw "no success message, check log $($LogFilePath)" } Write-ToLogOrConsole @LogParam -Severity Info -Message "finished the configmgr site server installtion" } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Convert-CMSiteUpdateState { <# .DESCRIPTION this function can be used to convert configmgr update status into a string .Parameter State current state of the configmgr site update .EXAMPLE Convert-CMSiteUpdateState -State (Get-CMSiteUpdate -Fast -Name $UpdateName).State .NOTES source: https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/setup-migrate-backup-recovery/understand-troubleshoot-updates-servicing#complete-list-of-state-codes #> # https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/setup-migrate-backup-recovery/understand-troubleshoot-updates-servicing#complete-list-of-state-codes [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $State ) switch ($State) { "" { $Message = "no state was provided" } "2" { $Message = "UNKNOWN" } "0x0" { $Message = "UNKNOWN" } "0x2" { $Message = "ENABLED" } "262145" { $Message = "DOWNLOAD_IN_PROGRESS" } "262146" { $Message = "DOWNLOAD_SUCCESS" } "327679" { $Message = "DOWNLOAD_FAILED" } "327681" { $Message = "APPLICABILITY_CHECKING" } "327682" { $Message = "APPLICABILITY_SUCCESS" } "393213" { $Message = "APPLICABILITY_HIDE" } "393214" { $Message = "APPLICABILITY_NA" } "393215" { $Message = "APPLICABILITY_FAILED" } "65537" { $Message = "CONTENT_REPLICATING" } "65538" { $Message = "CONTENT_REPLICATION_SUCCESS" } "131071" { $Message = "CONTENT_REPLICATION_FAILED" } "131073" { $Message = "PREREQ_IN_PROGRESS" } "131074" { $Message = "PREREQ_SUCCESS" } "131075" { $Message = "PREREQ_WARNING" } "196607" { $Message = "PREREQ_ERROR" } "196609" { $Message = "INSTALL_IN_PROGRESS" } "196610" { $Message = "INSTALL_WAITING_SERVICE_WINDOW" } "196611" { $Message = "INSTALL_WAITING_PARENT" } "196612" { $Message = "INSTALL_SUCCESS" } "196613" { $Message = "INSTALL_PENDING_REBOOT" } "262143" { $Message = "INSTALL_FAILED" } "196614" { $Message = "INSTALL_CMU_VALIDATING" } "196615" { $Message = "INSTALL_CMU_STOPPED" } "196616" { $Message = "INSTALL_CMU_INSTALLFILES" } "196617" { $Message = "INSTALL_CMU_STARTED" } "196618" { $Message = "INSTALL_CMU_SUCCESS" } "196619" { $Message = "INSTALL_WAITING_CMU" } "262142" { $Message = "INSTALL_CMU_FAILED" } "196620" { $Message = "INSTALL_INSTALLFILES" } "196621" { $Message = "INSTALL_UPGRADESITECTRLIMAGE" } "196622" { $Message = "INSTALL_CONFIGURESERVICEBROKER" } "196623" { $Message = "INSTALL_INSTALLSYSTEM" } "196624" { $Message = "INSTALL_CONSOLE" } "196625" { $Message = "INSTALL_INSTALLBASESERVICES" } "196626" { $Message = "INSTALL_UPDATE_SITES" } "196627" { $Message = "INSTALL_SSB_ACTIVATION_ON" } "196628" { $Message = "INSTALL_UPGRADEDATABASE" } "196629" { $Message = "INSTALL_UPDATEADMINCONSOLE" } Default { $Message = "could not map the state '$($State)' to state message" } } return $Message } function Get-ConfigMgrSiteUpdate { <# .DESCRIPTION fetches the information for a configmgr site update .Parameter UpdateName name of the update .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .EXAMPLE Get-ConfigMgrSiteUpdate -Updatename $UpdateName .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $UpdateName, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { do { try { $CMSiteUpdate = Get-CMSiteUpdate -Name $UpdateName -Fast } catch { if ($PSItem.Exception.Message -like "*The SMS Provider reported an error*") { Write-ToLogOrConsole @LogParam -Severity Info -Message "waiting on sms provider" } else { throw $PSItem.Exception.Message } } Start-Sleep -Seconds 10 } while ( $Null -eq $CMSiteUpdate) return $CMSiteUpdate } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Write-CMSiteUpdateStatus { <# .DESCRIPTION shows current status of the update .Parameter UpdateObj A single object returned from get-cmsiteupdate .Parameter Detailed turns on more detailed output of the current status .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .EXAMPLE Write-CMSiteUpdateStatus -UpdateObj $UpdateObj -Detailed .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.Object] $UpdateObj, [Parameter(Mandatory = $false)] [switch] $Detailed, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { try { $StatusMessage = Convert-CMSiteUpdateState -State $UpdateObj.State if ($Detailed) { $DetailStatusMessages = $UpdateObj | Get-CMSiteUpdateInstallStatus -Complete -Step All | Select-Object -Last 5 -Property Progress, orderid, SubStageName, Description | Format-Table -AutoSize } } catch { if ($PSItem.Exception.Message -like "*The SMS Provider reported an error.*") { Write-ToLogOrConsole @LogParam -Severity Info -Message "waiting on sms provider" } else { throw $PSItem.Exception.Message } } Write-ToLogOrConsole @LogParam -Severity Info -Message "$($UpdateName) - current status $($StatusMessage)" if ($Detailed -and $null -ne $DetailStatusMessages -and $DetailStatusMessages -ne "") { $DetailStatusMessages } } catch { $ErrorMessage = "error checking status - $($PSItem.Message.Exception)" Write-ToLogOrConsole @LogParam -Severity Error -Message $ErrorMessage throw $ErrorMessage } } function Confirm-CMSiteUpdatePackageDownloaded { <# .DESCRIPTION writes the current status of configmgr site update until the update is downloaded .Parameter UpdateName name of the configmgr site update .Parameter Detailed turns on more detailed output of the current status .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .EXAMPLE Confirm-CMSiteUpdatePackageDownloaded -UpdateName $UpdateToInstall.Name .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $UpdateName, [Parameter(Mandatory = $false)] [switch] $Detailed, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { # Define update check variables $CheckCount = 0 $ServiceName = "SMS_EXECUTIVE" Write-ToLogOrConsole @LogParam -Severity Info -Message "verifying update $($UpdateName) is downloaded" $StoppingStatus = "DOWNLOAD_SUCCESS", "UNKNOWN" do { if($Detailed) { Write-ToLogOrConsole @LogParam -Severity Info -Message "---" } $UpdateCheckStart = Get-Date $CheckCount++ $UpdateObj = Get-ConfigMgrSiteUpdate -UpdateName $UpdateName $CMUpdateStatus = Convert-CMSiteUpdateState -State $UpdateObj.State if ($CMUpdateStatus -ne "DOWNLOAD_SUCCESS") { if ($Detailed) { Write-ToLogOrConsole @LogParam -Severity Info -Message "times checked: $($CheckCount)" Write-CMSiteUpdateStatus -UpdateObj $UpdateObj -Detailed } else { Write-CMSiteUpdateStatus -UpdateObj $UpdateObj } if ($CheckCount -eq 40) { Write-ToLogOrConsole @LogParam -Severity Info -Message "downloading state detected for longer than $(((Get-Date) - $UpdateCheckStart).Minutes) minutes, restarting $($ServiceName) service" Restart-Service -Name $ServiceName -Force -Verbose:$false } } else { Write-ToLogOrConsole @LogParam -Severity Info -Message "update package $($UpdateName) is available - current status: $($CMUpdateStatus)" } if ($CheckCount -ge 150) { throw "update is not available, please check manually" } Start-Sleep -Seconds 15 } while ($StoppingStatus -notcontains $CMUpdateStatus) } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Confirm-CMSiteUpdatePrereqCheckFinished { <# .DESCRIPTION writes the current status of configmgr site update until the prereq checks are finished .Parameter UpdateName name of the configmgr site update .Parameter Detailed turns on more detailed output of the current status .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .EXAMPLE Confirm-CMSiteUpdatePrereqCheckFinished -UpdateName $UpdateToInstall.Name .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $UpdateName, [Parameter(Mandatory = $false)] [switch] $Detailed, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath try { # Define update check variables $CheckCount = 0 $ServiceName = "SMS_EXECUTIVE" $StoppingStatus = "PREREQ_ERROR", "PREREQ_WARNING", "INSTALL_IN_PROGRESS" Write-ToLogOrConsole @LogParam -Severity Info -Message "verifying prerequisite checks for update $($UpdateName) are finished" do { if($Detailed) { Write-ToLogOrConsole @LogParam -Severity Info -Message "---" } $UpdateCheckStart = Get-Date $CheckCount++ $UpdateObj = Get-ConfigMgrSiteUpdate -UpdateName $UpdateName $CMUpdateStatus = Convert-CMSiteUpdateState -State $UpdateObj.State if ($StoppingStatus -notcontains $CMUpdateStatus) { if ($Detailed) { Write-ToLogOrConsole @LogParam -Severity Info -Message "times checked: $($CheckCount)" Write-CMSiteUpdateStatus -UpdateObj $UpdateObj -Detailed } else { Write-CMSiteUpdateStatus -UpdateObj $UpdateObj } if ($CheckCount -eq 40) { Write-ToLogOrConsole @LogParam -Severity Info -Message "no state change detected for longer than $(((Get-Date) - $UpdateCheckStart).Minutes) minutes, restarting $($ServiceName) service" Restart-Service -Name $ServiceName -Force -Verbose:$false } } else { Write-ToLogOrConsole @LogParam -Severity Info -Message "prereq checks for $($UpdateName) finished - current status: $($CMUpdateStatus)" } if ($CheckCount -ge 150) { throw "prereq checks took to long, check manually" } Start-Sleep -Seconds 15 } while ($StoppingStatus -notcontains $CMUpdateStatus) if ($CMUpdateStatus -eq "INSTALL_IN_PROGRESS") { Write-ToLogOrConsole @LogParam -Severity Info -Message "installation was successfully initated for $($UpdateName), for more details, review the CMUpdate.log - current status: $(Convert-CMSiteUpdateState -State $CMUpdatePackage.State)" } elseif ($CMUpdateStatus -eq "PREREQ_ERROR") { Write-ToLogOrConsole @LogParam -Severity Info -Message "prerequisite checks found some errors, please check manually - current status: $(Convert-CMSiteUpdateState -State $CMUpdatePackage.State)" } elseif ($CMUpdateStatus -eq "PREREQ_WARNING") { Write-ToLogOrConsole @LogParam -Severity Info -Message "prerequisite checks found some warnings, please check manually - current status: $(Convert-CMSiteUpdateState -State $CMUpdatePackage.State)" } } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } function Confirm-CMSiteUpdatePackageInstallation { <# .DESCRIPTION checks the install status of a running configmgr update .Parameter UpdateName name of the configmgr site update .Parameter Detailed turns on more detailed output of the current status .PARAMETER LogFileName name of the log file .PARAMETER LogFileFolderPath path of the folder where to put the log file .EXAMPLE Confirm-CMSiteUpdatePackageInstallation -UpdateName $UpdateToInstall.Name .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $UpdateName, [Parameter(Mandatory = $false)] [switch] $Detailed, [Parameter(Mandatory = $false)] [string] $LogFileName = "", [Parameter(Mandatory = $false)] [string] $LogFileFolderPath = "" ) $ErrorActionPreference = 'Stop' $LogParam = Confirm-LogFileParameters -LogFileName $LogFileName -LogFileFolderPath $LogFileFolderPath $StoppingStatus = "INSTALL_SUCCESS", "INSTALL_FAILED" try { Write-ToLogOrConsole @LogParam -Severity Info -Message "verifying installation of update $($UpdateName) is finished" do { if($Detailed) { Write-ToLogOrConsole @LogParam -Severity Info -Message "---" } $UpdateObj = Get-ConfigMgrSiteUpdate -UpdateName $UpdateName $StatusMessage = Convert-CMSiteUpdateState -State $UpdateObj.State if ($Detailed) { Write-CMSiteUpdateStatus -UpdateObj $UpdateObj -Detailed } else { Write-CMSiteUpdateStatus -UpdateObj $UpdateObj } Start-Sleep -Seconds 15 } while ($StoppingStatus -notcontains $StatusMessage) $UpdateObj = Get-ConfigMgrSiteUpdate -UpdateName $UpdateName $StatusMessage = Convert-CMSiteUpdateState -State $UpdateObj.State if ($StatusMessage -eq "INSTALL_SUCCESS") { Write-ToLogOrConsole @LogParam -Severity Info -Message "installation of update $($UpdateName) is finished, post install steps are not finished yet." Write-ToLogOrConsole @LogParam -Severity Info -Message "admin console updates may be required" # do { # if($Detailed) { # Write-ToLogOrConsole @LogParam -Severity Info -Message "---" # } # $UpdateObj = Get-ConfigMgrSiteUpdate -UpdateName $UpdateName # $StatusMessage = Convert-CMSiteUpdateState -State $UpdateObj.State # Write-CMSiteUpdateStatus -UpdateObj $UpdateObj -Detailed # Start-Sleep -Seconds 15 # } # while ($true) # Write-ToLogOrConsole @LogParam -Severity Info -Message "post install of configmgr site update $($UpdateName) is finished" } else { Write-ToLogOrConsole @LogParam -Severity Info -Message "current status $($StatusMessage)" throw "something went wrong - please check the logs" } } catch { Write-ToLogOrConsole @LogParam -Severity Error -Message $PSItem.Exception.Message throw $PSItem.Exception.Message } } |