-
-
Save SkipToTheEndpoint/7bf177daea583cb61720c96c7c2f55a8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Installs Microsoft Managed Desktop Client Library and Microsoft Cloud Managed Desktop Extension | |
.EXAMPLE | |
.\SetupAutopatchClientPackage.ps1 | |
#> | |
param( | |
[switch] $DisableTranscript = $false | |
) | |
# Ensures that Invoke-WebRequest uses TLS 1.2 | |
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 | |
# Version and Location Parameters | |
$ClientLibraryVersion = 202206031 | |
$CloudManagementExtensionVersion = '1.2.01996.78' | |
$cabDownloadUri = 'https://mmdcustomer.microsoft.com/clientsetup/20220621.1/AutopatchSetupPackage.cab' | |
$cabDownloadUriBackup = 'https://mmdcustomer.azureedge.net/clientsetup/20220621.1/AutopatchSetupPackage.cab' | |
function Set-Registry { | |
<# | |
.Synopsis | |
Modify registry String value. | |
.Description | |
Function to Modify registry String value. | |
.Parameter | |
-Hive 'HKLM' -Path '\Software\Policies\Microsoft\Windows\System' -Name 'CleanupProfiles' -Value 'Test' -Type 'String' | |
.Example | |
Set-Registry -Hive 'HKLM' -Path '\Software\Policies\Microsoft\Windows\System' -Name 'CleanupProfiles' -Value 'Test' -Type 'String' | |
.Inputs | |
string. | |
.Outputs | |
None. | |
#> | |
param ( | |
[parameter(mandatory)] [string] $Hive, | |
[parameter(mandatory)] [string] $Path, | |
[parameter(mandatory)] [string] $Name, | |
[parameter(mandatory)] [string] $Value, | |
[parameter(mandatory)] [string] $Type | |
) | |
$regPath = ('{0}:\{1}' -f $Hive, $Path) | |
try { | |
if (!(Test-Path -Path $regPath)) { | |
$null = New-Item -Path $regPath -Force | |
} | |
Write-Host "[Set-Registry] Attempting to set registry value $Name at registry path $regpath and registry data $Value" | |
New-ItemProperty -Path $regPath -Name $Name -Value $Value -PropertyType $Type -Force -ErrorAction Continue | |
} | |
catch [System.InvalidOperationException] { | |
Write-Error $_.Exception.Message`n | |
Write-Host "[Set-Registry] Unable to create registry value $Name at registry path $regPath registry data $Value" | |
} | |
catch { | |
Write-Error $_.Exception.Message`n | |
} | |
} | |
function Get-Registry { | |
<# | |
.Synopsis | |
Get registry value. | |
.Description | |
Function to obtain registry value. | |
.Parameter | |
-Hive 'HKLM' -Path '\Software\Policies\Microsoft\Windows\System' -Name 'CleanupProfiles' | |
.Example | |
Get-Registry -Hive 'HKLM' -Path '\Software\Policies\Microsoft\Windows\System' -Name 'CleanupProfiles' | |
.Inputs | |
string | |
.Outputs | |
string | |
#> | |
param ( | |
[parameter(mandatory)] [string] $Hive, | |
[parameter(mandatory)] [string] $Path, | |
[parameter(mandatory)] [string] $Name | |
) | |
$regPath = '{0}:\{1}' -f $Hive, $Path | |
$regValues = Get-ItemProperty -Path $regPath -ErrorAction SilentlyContinue | |
if (!$regValues) | |
{ | |
return $null | |
} | |
else | |
{ | |
return $regValues.$Name | |
} | |
} | |
function VerifyMicrosoftFileSignature { | |
<# | |
.Synopsis | |
Verifies if a file is Microsoft Signed | |
.Description | |
Checks if a file is signed by a certificate with a microsoft subject and rooted to the microsoft root | |
.Parameter | |
-FilePath | |
.Example | |
VerifyMicrosoftFileSignature -FilePath 'C:\temp\temp.cab' | |
.Inputs | |
string. | |
.Outputs | |
None. | |
#> | |
param ( | |
[parameter(mandatory)] [string] $FilePath | |
) | |
$signature = Get-AuthenticodeSignature -FilePath $FilePath | |
Write-Host 'Digital signature validity:' $signature.status | |
Write-Host 'Signer Certificate subject:' $signature.signerCertificate.subject | |
$cert = $signature.SignerCertificate | |
$chain = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Chain | |
$chain.Build($cert) | |
$rootCertSubject = ($chain.ChainElements | Select-Object -Last 1).Certificate.Subject | |
Write-Host 'Root Certificate subject:' $rootCertSubject | |
Write-Host 'Chain Status:' ($chain.ChainStatus | Format-List) | |
if ($chain.ChainStatus) | |
{ | |
foreach ($chainStatus in $chain.ChainStatus) | |
{ | |
# We are only okay with chain statuses that are NoError(0) or NotTimeValid(1) | |
if (($chainStatus.Status -eq 0) -or ($chainStatus.Status -eq 1)) | |
{ | |
continue | |
} | |
else | |
{ | |
throw "Chain Not Trusted. Chain Status: $chainStatus.Status" | |
} | |
} | |
} | |
# Check that the signing cert is syntactically valid, has the Microsoft subject, is rooted to the Microsoft root cert, and has no errors in the chain | |
if (-not ($signature.status -eq 'valid' -and ($signature.signerCertificate.subject -eq 'CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US' -and ($rootCertSubject -eq 'CN=Microsoft Root Certificate Authority 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US')))) | |
{ | |
throw "Not signed by Microsoft PRS Cert with Microsoft Root" | |
} | |
} | |
function InstallClientLibrary | |
{ | |
if ($CabClientLibraryVersion -ne $CurrentClientLibraryVersion) | |
{ | |
Write-Host "Installing Client Library Version $CabClientLibraryVersion" | |
Write-Host 'Creating Client Library temp directory' | |
$ClientLibraryTempPath = "$ExpandedCabDirectory\ClientLibrary" | |
New-Item -Force -ItemType directory -Path "$ExpandedCabDirectory\ClientLibrary" | |
Expand-Archive -LiteralPath "$ExpandedCabDirectory\MmdBroker.zip" -DestinationPath $ClientLibraryTempPath | |
$ClientLibraryInstallScript = "$ClientLibraryTempPath\InstallMmdBroker.ps1" | |
& $ClientLibraryInstallScript -DisableTranscript $true | |
Write-Host "Exit code from installing Client Library: $LASTEXITCODE" | |
} | |
else | |
{ | |
Write-Host 'Skipping Client Library Installation' | |
} | |
} | |
function InstallManagementExtension | |
{ | |
if ($CabManagementExtensionVersion -ne $CurrentManagementExtensionVersion) | |
{ | |
$CmdAgentLogPath = "$LogPath\AutopatchCloudManagedDesktopExtensionInstall" + $stampDate.ToFileTimeUtc() + '.log' | |
$ManagementExtensionMsi = "$ExpandedCabDirectory\cmdextension.msi" | |
Write-Host "Installing Management Extension Version $CabManagementExtensionVersion" | |
try | |
{ | |
VerifyMicrosoftFileSignature -FilePath $ManagementExtensionMsi | |
} | |
catch | |
{ | |
Write-Host 'Management Extension MSI is not trusted. Skipping' | |
return | |
} | |
# Attempt to save certain values for reinstall scenarios | |
$IoTDeviceId = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'IoTDeviceId' | |
$IoTHostName = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'IoTHostName' | |
$DeviceRegistrationType = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'DeviceRegistrationType' | |
$Partners = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'Partners' | |
$PartnersNextSyncTime = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'PartnersNextSyncTime' | |
$ClientAgentNames = @( | |
"Microsoft CMD Client Agent" | |
"Windows Client Agent", | |
"Microsoft Cloud Managed Desktop Extension" | |
) | |
foreach ($AgentName in $ClientAgentNames){ | |
$agent = Get-WmiObject -class Win32_Product -Filter "Name=`'$($AgentName)`'" | |
if ($agent) { | |
$productCode = $agent.IdentifyingNumber | |
$currentVersion = Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$productCode" -Name "DisplayVersion" | |
$MSIUninstallArguments = @( | |
"/x" | |
$productCode | |
"/q" | |
"/norestart" | |
) | |
Start-Process "msiexec.exe" -ArgumentList $MSIUninstallArguments -Wait -NoNewWindow | |
Write-Host "Finished uninstall command line with param $($MSIUninstallArguments) for agent `'$($AgentName)`' with version $($currentVersion)" | |
} | |
} | |
# Restore Reg Values for Upgrade Scenario | |
if ($IoTDeviceId) | |
{ | |
Write-Host "Rewriting IoTDeviceId: $IoTDeviceId" | |
Set-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'IoTDeviceId' -Value $IoTDeviceId -Type 'String' | |
} | |
if ($IoTHostName) | |
{ | |
Write-Host "Rewriting IoTHostName: $IoTHostName" | |
Set-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'IoTHostName' -Value $IoTHostName -Type 'String' | |
} | |
if ($DeviceRegistrationType) | |
{ | |
Write-Host "Rewriting DeviceRegistrationType: $DeviceRegistrationType" | |
Set-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'DeviceRegistrationType' -Value $DeviceRegistrationType -Type 'String' | |
} | |
if ($Partners) | |
{ | |
Write-Host "Rewriting Partners: $Partners" | |
Set-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'Partners' -Value $Partners -Type 'String' | |
} | |
if ($PartnersNextSyncTime) | |
{ | |
Write-Host "Rewriting PartnersNextSyncTime: $PartnersNextSyncTime" | |
Set-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\Settings" -Name 'PartnersNextSyncTime' -Value $PartnersNextSyncTime -Type 'String' | |
} | |
$MSIInstallArguments = @( | |
"/i" | |
"$ManagementExtensionMsi" | |
"SVCENV=$CabManagementExtensionEnvironment" | |
"/qb!" | |
"/l*v $CmdAgentLogPath" | |
"/norestart" | |
) | |
Start-Process "msiexec.exe" -ArgumentList $MSIInstallArguments -Wait -NoNewWindow | |
} | |
else | |
{ | |
Write-Host 'Skipping Management Extension Installation' | |
} | |
} | |
function Cleanup { | |
<# | |
.Synopsis | |
Cleans up files, directories, and scheduled tasks associated with this install process except this script | |
.Description | |
Cleans up files, directories, and scheduled tasks associated with this install process except this script | |
.Parameter | |
-SuccessfulRun | |
.Example | |
Cleanup -SuccessfulRun $true | |
.Inputs | |
boolean. | |
.Outputs | |
None. | |
#> | |
param ( | |
[boolean] $SuccessfulRun = $false | |
) | |
cd $PSScriptRoot | |
if (Test-Path $CabDirectory) | |
{ | |
Write-Host 'Removing Cab directory' | |
Remove-Item -LiteralPath $CabDirectory -Force -Recurse | |
} | |
if ($SuccessfulRun) | |
{ | |
# Cleanup scheduled task if it exists | |
Unregister-ScheduledTask -TaskName 'Autopatch Client Setup Installer' -Confirm:$false -ErrorAction SilentlyContinue | |
Write-Host "[SUCCESSFUL_AUTOPATCH_CLIENT_SETUP]" | |
} | |
else | |
{ | |
Write-Host "[FAILED_AUTOPATCH_CLIENT_SETUP]" | |
} | |
if (-not $DisableTranscript) | |
{ | |
# Stop Logging | |
Stop-Transcript -Verbose | |
} | |
} | |
# If Powershell is running the 32-bit version on a 64-bit machine, we need to force powershell to run in | |
# 64-bit mode. | |
if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") { | |
if ($myInvocation.Line) { | |
&"$env:WINDIR\sysnative\windowspowershell\v1.0\powershell.exe" -NonInteractive -NoProfile $myInvocation.Line | |
} | |
else | |
{ | |
&"$env:WINDIR\sysnative\windowspowershell\v1.0\powershell.exe" -NonInteractive -NoProfile -file "$($myInvocation.InvocationName)" $args | |
} | |
exit $lastexitcode | |
} | |
$CabDirectory = "$env:SystemDrive\ProgramData\Microsoft\AutopatchSetup" | |
$LogPath = "$Env:windir\ccm\logs" | |
$LogPrefix = "$LogPath\AutopatchClientSetupInstallTask" | |
$stampDate = Get-Date | |
# Start Logging | |
if (-not $DisableTranscript) | |
{ | |
$logFile = $LogPrefix + $stampDate.ToFileTimeUtc() + '.log' | |
Start-Transcript -Path $logFile | |
} | |
# Remove Autopatch Install logs that are 7+ days old | |
$cutOffDate = (Get-Date).AddDays(-7).ToFileTimeUtc() | |
foreach ($logFile in Get-ChildItem $LogPath) { | |
if ($logFile.Name.StartsWith($LogPrefix) -and $logFile.Extension.Equals('.log')) { | |
$logDateStr = $logFile.Name.Substring($LogPrefix.Length) | |
$logDateStr = $logDateStr.Substring(0, $logDateStr.IndexOf('.')) | |
$logDate = 0 | |
if ([uint64]::TryParse($logDateStr, [ref]$logDate) -and $logDate -lt $cutOffDate) { | |
Remove-Item ('{0}{1}' -f $LogPath, $logFile) | |
Write-Host "Removed old log file $logFile" | |
} | |
} | |
} | |
$MmdRegistryKey = '\Software\Microsoft\MMD' | |
$CmdRegistryKey = 'Software\Microsoft\CloudManagementDesktop\Extension' | |
#Check Installed versions for Client Library and Management Extension | |
$CurrentClientLibraryVersion = Get-Registry -Hive 'HKLM' -Path "$MmdRegistryKey\Broker" -Name 'Version' | |
$CurrentManagementExtensionVersion = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\ProductInfo" -Name 'Version' | |
Write-Host "Installed Client Library Version: $CurrentClientLibraryVersion; Installed Management Extension Verison: $CurrentManagementExtensionVersion" | |
if (($ClientLibraryVersion -eq $CurrentClientLibraryVersion) -and ($CloudManagementExtensionVersion -eq $CurrentManagementExtensionVersion)) | |
{ | |
Write-Host 'Client Library and Management Extension are up-to-date. Exiting Installation' | |
Cleanup -SuccessfulRun $true | |
exit 0 | |
} | |
$ExpandedCabDirectory = "$env:SystemDrive\ProgramData\Microsoft\AutopatchSetup\Files" | |
$CabFile = "$CabDirectory\AutopatchSetupPackage.cab" | |
if (Test-Path $CabDirectory) | |
{ | |
Write-Host 'Removing old Cab directory' | |
Remove-Item -LiteralPath $CabDirectory -Force -Recurse | |
} | |
Write-Host 'Creating Cab directory' | |
New-Item -Force -ItemType directory -Path $CabDirectory | |
Write-Host "Downloading Cab from $cabDownloadUri" | |
try | |
{ | |
Invoke-WebRequest -URI $cabDownloadUri -OutFile $CabFile | |
} | |
catch | |
{ | |
Write-Host $_.Exception.Message`n | |
Write-Host "Failed to download cab from $cabDownloadUri" | |
} | |
if (-not (Test-Path $CabFile -PathType Leaf)) | |
{ | |
Write-Host "Downloading Cab from $cabDownloadUriBackup" | |
try | |
{ | |
Invoke-WebRequest -URI $cabDownloadUriBackup -OutFile $CabFile | |
} | |
catch | |
{ | |
Write-Host $_.Exception.Message`n | |
Write-Host "Failed to download cab from $cabDownloadUriBackup" | |
Cleanup | |
exit 2 | |
} | |
} | |
#Verify microsoft signature on cab | |
try | |
{ | |
VerifyMicrosoftFileSignature -FilePath $CabFile | |
} | |
catch | |
{ | |
Write-Host 'Cab is not trusted. Exiting' | |
Cleanup | |
exit 3 | |
} | |
Write-Host 'Creating expanded Cab directory' | |
New-Item -Force -ItemType directory -Path $ExpandedCabDirectory | |
Write-Host 'Extract cab' | |
expand.exe $CabFile -F:* $ExpandedCabDirectory | |
Write-Host 'Parse config file' | |
$ConfigFileName = "$ExpandedCabDirectory\config.json" | |
$configProperties = @{} | |
$json = Get-Content $ConfigFileName | Out-String | |
(ConvertFrom-Json $json).psobject.properties | Foreach { $configProperties[$_.Name] = $_.Value } | |
$CabManagementExtensionEnvironment = $configProperties['ManagementExtensionEnvironment'] | |
$CabClientLibraryVersion = $configProperties['ClientLibraryVersion'] | |
$CabManagementExtensionVersion = $configProperties['ManagementExtensionVersion'] | |
Write-Host "Cab Client Library Version: $CabClientLibraryVersion; Cab Management Extension Version: $CabManagementExtensionVersion" | |
InstallClientLibrary | |
InstallManagementExtension | |
# Verify Installations | |
$NewClientLibraryVersion = Get-Registry -Hive 'HKLM' -Path "$MmdRegistryKey\Broker" -Name 'Version' | |
$NewManagementExtensionVersion = Get-Registry -Hive 'HKLM' -Path "$CmdRegistryKey\ProductInfo" -Name 'Version' | |
if (($ClientLibraryVersion -eq $NewClientLibraryVersion) -and ($CloudManagementExtensionVersion -eq $NewManagementExtensionVersion)) | |
{ | |
Write-Host 'Client Library and Management Extension have installed successfully. Exiting Installation' | |
# Setting Autopatch registry value | |
Set-Registry -Hive 'HKLM' -Path "$MmdRegistryKey\Broker" -Name 'Autopatch' -Value '1' -Type 'Dword' | |
Cleanup -SuccessfulRun $true | |
exit 0 | |
} | |
else | |
{ | |
Write-Host 'Client Library and Management Extension have not installed successfully.' | |
Write-Host "Expected Client Library Version: $ClientLibraryVersion; Installed Client Library Version: $NewClientLibraryVersion." | |
Write-Host "Expected Management Extension Version: $CabManagementExtensionVersion; Installed Management Extension Version: $NewManagementExtensionVersion." | |
Cleanup | |
exit 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment