Add Microsoft Update opt out to MSI install (#15727)

This commit is contained in:
Travis Plunk
2021-07-14 15:41:56 -07:00
committed by GitHub
parent b7d6ef2e50
commit 6317e4e39a
5 changed files with 144 additions and 25 deletions
+57 -2
View File
@@ -84,6 +84,16 @@
Execute="deferred"
Return="ignore"
Impersonate="no" />
<SetProperty Id="EnableMU"
Before="EnableMU"
Sequence="execute"
Value="&quot;[VersionFolder]pwsh.exe&quot; -NoProfile -ExecutionPolicy Bypass -Command &quot;$null=(New-Object -ComObject Microsoft.Update.ServiceManager).AddService2('7971f918-a847-4430-9279-4a52d1efe18d', 7, '')&quot;" />
<CustomAction Id="EnableMU"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="deferred"
Return="ignore"
Impersonate="no" />
<InstallExecuteSequence>
<!-- Do not remove shortcuts on upgrade to preserve user shortcuts pinned to Taskbar, see https://stackoverflow.com/a/33402698/1810304 -->
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
@@ -93,6 +103,9 @@
<Custom Action="EnablePSRemoting" After="InstallFiles">
<![CDATA[NOT Installed AND (ENABLE_PSREMOTING=1)]]>
</Custom>
<Custom Action="EnableMU" After="InstallFiles">
<![CDATA[NOT Installed AND (ENABLE_MU=1)]]>
</Custom>
</InstallExecuteSequence>
<UI>
<Dialog Id="MyExitDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
@@ -135,6 +148,7 @@
<Feature Id="ProductFeature" Title="PowerShell" Level="1">
<ComponentGroupRef Id="ApplicationFiles"/>
<ComponentRef Id="ApplicationProgramsMenuShortcut"/>
<ComponentRef Id="MURegistryEntries"/>
<ComponentRef Id="RegistryEntries"/>
<ComponentRef Id="SharedRegistryEntries"/>
<ComponentRef Id="SetPath"/>
@@ -176,6 +190,13 @@
<!-- Should retain component GUID since this is a shared, non-file, non-registry resource. -->
<Environment Id="PowerShellDistributionChannel" Action="create" Name="POWERSHELL_DISTRIBUTION_CHANNEL" Permanent="no" System="yes" Value="MSI:[WINDOWS_PRODUCT_NAME]"/>
</Component>
<Component Id="MURegistryEntries">
<Condition>USE_MU=1</Condition>
<!-- Create registry key to allow opt-out of MU servicing without opting out of MU completely. -->
<RegistryKey Root="HKLM" Key="Software\Microsoft\PowerShellCore" Action="create" ForceCreateOnInstall="yes">
<RegistryValue Type="integer" Value="1" Name="UseMU" KeyPath="yes"/>
</RegistryKey>
</Component>
<Component Id="RegistryEntries">
<!-- create key for easy detection of a particular version of a powershell core package
The upgrade code is used in the key because it will change when we allow SxS -->
@@ -317,6 +338,37 @@
</UI>
</Fragment>
<!-- Microsoft Update Menu Dialog -->
<Fragment>
<UI>
<Property Id="USE_MU" Value="1" />
<Property Id="ENABLE_MU" Value="1" />
<Dialog Id="MuDialog" Width="370" Height="270" Title="!(loc.ExitDialog_Title)">
<!-- Banner -->
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="yes" Text="!(loc.InstallDirDlgBannerBitmap)"/>
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="30" Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Title}Use Microsoft Update to help keep your computer secure and up to date" TabSkip="yes" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" TabSkip="yes" />
<Control Id="Description" Type="Text" X="25" Y="54" Width="280" Height="100" Transparent="yes" NoPrefix="yes" Text="Microsoft Update helps keep your computer secure and up to date for Windows and other Microsoft products, including PowerShell 7. Updates will be delivered based on your current update settings. You can review or change these settings from the Windows Update control panel." TabSkip="yes" />
<!-- If the checkboxes are defined first, then they are first in the tab order and can be ticked and unticked using the spacebar -->
<Control Id="UseMuCheckBox" Type="CheckBox" X="20" Y="100" Width="290" Height="27" Property="USE_MU" Text="Enable updating PowerShell through Microsft Update or WSUS (recommended)"/>
<Control Id="EnableMuCheckBox" Type="CheckBox" X="20" Y="130" Width="290" Height="17" Property="ENABLE_MU" Text="Use Microsoft Update when I check for updates (recommended)"/>
<Control Id="MuWarningText" Type="Text" X="20" Y="160" Width="290" Height="27" Text="Without Microsoft Update enabled, you will need to use another update solution like WSUS or SCCM in order to receive automatic updates."/>
<Control Id="MuFAQ" Type="Hyperlink" X="20" Y="190" Width="214" Height="17">
<Text><![CDATA[<a href="https://aka.ms/PowerShell-Microsoft-Update-FAQ">See the Microsoft Update FAQ</a>]]></Text>
</Control>
<Control Id="PrivacyLink" Type="Hyperlink" X="20" Y="210" Width="290" Height="17">
<Text><![CDATA[<a href="https://aka.ms/PowerShell-Microsoft-Update-Privacy-Statement">Read the Microsoft Update Priacy Statement</a>]]></Text>
</Control>
<!-- divider and bottom buttons -->
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" TabSkip="yes"/>
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)"/>
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Cancel="yes" Default="yes" Text="!(loc.WixUINext)"/>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
</Dialog>
</UI>
</Fragment>
<!-- Customized version of WixUI_InstallDir, which is necessary to add custom dialogs. https://github.com/wixtoolset/wix3/blob/master/src/ext/UIExtension/wixlib/WixUI_InstallDir.wxs -->
<Fragment>
<UI Id="CustomWixUI_InstallDir">
@@ -325,6 +377,7 @@
- WixUI_WelcomeDlg
- WixUI_InstallDirDlg
- ExplorerContextMenuDialog
- MuDialog
- WixUI_VerifyReadyDlg
- WixUI_DiskCostDlg
Maintenance dialog sequence:
@@ -364,7 +417,9 @@
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
<Publish Dialog="ExplorerContextMenuDialog" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
<Publish Dialog="ExplorerContextMenuDialog" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">1</Publish>
<Publish Dialog="ExplorerContextMenuDialog" Control="Next" Event="NewDialog" Value="MuDialog">1</Publish>
<Publish Dialog="MuDialog" Control="Back" Event="NewDialog" Value="ExplorerContextMenuDialog">1</Publish>
<Publish Dialog="MuDialog" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="2">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">1</Publish>
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
@@ -375,7 +430,7 @@
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ExplorerContextMenuDialog" Order="1">NOT Installed</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MuDialog" Order="1">NOT Installed</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2">Installed AND PATCH</Publish>
+74 -8
View File
@@ -3,6 +3,7 @@
Describe -Name "Windows MSI" -Fixture {
BeforeAll {
Set-StrictMode -Off
function Test-Elevated {
[CmdletBinding()]
[OutputType([bool])]
@@ -14,6 +15,43 @@ Describe -Name "Windows MSI" -Fixture {
return (([Security.Principal.WindowsIdentity]::GetCurrent()).Groups -contains "S-1-5-32-544")
}
function Test-IsMuEnabled {
$sm = (New-Object -ComObject Microsoft.Update.ServiceManager)
$mu = $sm.Services | Where-Object { $_.ServiceId -eq '7971f918-a847-4430-9279-4a52d1efe18d' }
if ($mu) {
return $true
}
return $false
}
function Invoke-TestAndUploadLogOnFailure {
param (
[scriptblock] $Test
)
try {
& $Test
}
catch {
Send-VstsLogFile -Path $msiLog
throw
}
}
function Get-UseMU {
$useMu = 0
$key = 'HKLM:\SOFTWARE\Microsoft\PowerShellCore\'
if ($runtime -like '*x86*') {
$key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore\'
}
try {
$useMu = Get-ItemPropertyValue -Path $key -Name UseMU -ErrorAction SilentlyContinue
} catch {}
return $useMu
}
function Invoke-Msiexec {
param(
[Parameter(ParameterSetName = 'Install', Mandatory)]
@@ -45,6 +83,7 @@ Describe -Name "Windows MSI" -Fixture {
}
$argumentList = "$switch $MsiPath /quiet /l*vx $msiLog $additionalOptions"
Write-Verbose -Message "running msiexec $argumentList"
$msiExecProcess = Start-Process msiexec.exe -Wait -ArgumentList $argumentList -NoNewWindow -PassThru
if ($msiExecProcess.ExitCode -ne 0) {
$exitCode = $msiExecProcess.ExitCode
@@ -55,6 +94,7 @@ Describe -Name "Windows MSI" -Fixture {
$msiX64Path = $env:PsMsiX64Path
$channel = $env:PSMsiChannel
$runtime = $env:PSMsiRuntime
$muEnabled = Test-IsMuEnabled
# Get any existing powershell in the path
$beforePath = @(([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |
@@ -71,16 +111,14 @@ Describe -Name "Windows MSI" -Fixture {
}
$uploadedLog = $false
}
AfterAll {
Set-StrictMode -Version 3.0
}
BeforeEach {
$error.Clear()
}
AfterEach {
if ($error.Count -ne 0 -and !$uploadedLog) {
Copy-Item -Path $msiLog -Destination $env:temp -Force
Write-Verbose "MSI log is at $env:temp\msilog.txt" -Verbose
$uploadedLog = $true
}
}
Context "Upgrade code" {
BeforeAll {
@@ -149,7 +187,7 @@ Describe -Name "Windows MSI" -Fixture {
Context "Add Path disabled" {
It "MSI should install without error" -Skip:(!(Test-Elevated)) {
{
Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 0}
Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 0; USE_MU = 1; ENABLE_MU = 1}
} | Should -Not -Throw
}
@@ -160,6 +198,34 @@ Describe -Name "Windows MSI" -Fixture {
$psPath | Should -BeNullOrEmpty
}
It "UseMU should be 1" -Skip:(!(Test-Elevated)) {
Invoke-TestAndUploadLogOnFailure -Test {
$useMu = Get-UseMU
$useMu | Should -Be 1
}
}
It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {
{
Invoke-MsiExec -Uninstall -MsiPath $msiX64Path
} | Should -Not -Throw
}
}
Context "USE_MU disabled" {
It "MSI should install without error" -Skip:(!(Test-Elevated)) {
{
Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{USE_MU = 0}
} | Should -Not -Throw
}
It "UseMU should be 0" -Skip:(!(Test-Elevated)) {
Invoke-TestAndUploadLogOnFailure -Test {
$useMu = Get-UseMU
$useMu | Should -Be 0
}
}
It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {
{
Invoke-MsiExec -Uninstall -MsiPath $msiX64Path
@@ -219,22 +219,21 @@ function Send-VstsLogFile {
}
}
$newName = ([System.Io.Path]::GetRandomFileName() + "-$LogName.txt")
if($Contents)
{
$logFile = Join-Path -Path $logFolder -ChildPath ([System.Io.Path]::GetRandomFileName() + "-$LogName.txt")
$name = Split-Path -Leaf -Path $logFile
$logFile = Join-Path -Path $logFolder -ChildPath $newName
$Contents | Out-File -path $logFile -Encoding ascii
}
else
{
$name = Split-Path -Leaf -Path $path
$logFile = Join-Path -Path $logFolder -ChildPath ([System.Io.Path]::GetRandomFileName() + '-' + $name)
$logFile = Join-Path -Path $logFolder -ChildPath $newName
Copy-Item -Path $Path -Destination $logFile
}
Write-Host "##vso[artifact.upload containerfolder=$name;artifactname=$name]$logFile"
Write-Verbose "Log file captured as $name" -Verbose
Write-Host "##vso[artifact.upload containerfolder=$newName;artifactname=$newName]$logFile"
Write-Verbose "Log file captured as $newName" -Verbose
}
# Tests if the Linux or macOS user is root
+6 -8
View File
@@ -546,23 +546,21 @@ function Invoke-CIFinish
Start-PSBuild -Restore -Runtime win-arm64 -PSModuleRestore -Configuration 'Release' -ReleaseTag $releaseTag
$arm64Package = Start-PSPackage -Type zip -WindowsRuntime win-arm64 -ReleaseTag $releaseTag -SkipReleaseChecks
$artifacts.Add($arm64Package)
}
finally {
$pushedAllArtifacts = $true
$artifacts | ForEach-Object {
Write-Log -Message "Pushing $_ as CI artifact"
if(Test-Path $_)
{
if (Test-Path $_) {
Push-Artifact -Path $_ -Name 'artifacts'
}
else
{
} else {
$pushedAllArtifacts = $false
Write-Warning "Artifact $_ does not exist."
}
}
if(!$pushedAllArtifacts)
{
if (!$pushedAllArtifacts) {
throw "Some artifacts did not exist!"
}
}
+2 -1
View File
@@ -3282,7 +3282,8 @@ function Start-MsiBuild {
Write-Log "running light..."
# suppress ICE61, because we allow same version upgrades
# suppress ICE57, this suppresses an error caused by our shortcut not being installed per user
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixLightExePath -sice:ICE61 -sice:ICE57 -out $msiLocationPath -pdbout $msiPdbLocationPath $objectPaths $extensionArgs }
# suppress ICE40, REINSTALLMODE is defined in the Property table.
Start-NativeExecution -VerboseOutputOnError {& $wixPaths.wixLightExePath -sice:ICE61 -sice:ICE40 -sice:ICE57 -out $msiLocationPath -pdbout $msiPdbLocationPath $objectPaths $extensionArgs }
foreach($file in $objectPaths)
{