MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3 4Describe -Name "Windows MSI" -Fixture {5 BeforeAll {6 Set-StrictMode -Off7 function Test-Elevated {8 [CmdletBinding()]9 [OutputType([bool])]10 Param()11 12 # if the current Powershell session was called with administrator privileges,13 # the Administrator Group's well-known SID will show up in the Groups for the current identity.14 # Note that the SID won't show up unless the process is elevated.15 return (([Security.Principal.WindowsIdentity]::GetCurrent()).Groups -contains "S-1-5-32-544")16 }17 18 function Test-IsMuEnabled {19 $sm = (New-Object -ComObject Microsoft.Update.ServiceManager)20 $mu = $sm.Services | Where-Object { $_.ServiceId -eq '7971f918-a847-4430-9279-4a52d1efe18d' }21 if ($mu) {22 return $true23 }24 return $false25 }26 27 function Invoke-TestAndUploadLogOnFailure {28 param (29 [scriptblock] $Test30 )31 32 try {33 & $Test34 }35 catch {36 Send-VstsLogFile -Path $msiLog37 throw38 }39 }40 41 function Get-UseMU {42 $useMu = $null43 $key = 'HKLM:\SOFTWARE\Microsoft\PowerShellCore\'44 if ($runtime -like '*x86*') {45 $key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore\'46 }47 48 try {49 $useMu = Get-ItemPropertyValue -Path $key -Name UseMU -ErrorAction SilentlyContinue50 } catch {}51 52 if (!$useMu) {53 $useMu = 054 }55 56 return $useMu57 }58 59 function Set-UseMU {60 param(61 [int]62 $Value63 )64 $key = 'HKLM:\SOFTWARE\Microsoft\PowerShellCore\'65 if ($runtime -like '*x86*') {66 $key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore\'67 }68 69 Set-ItemProperty -Path $key -Name UseMU -Value $Value -Type DWord70 71 return $useMu72 }73 74 function Invoke-Msiexec {75 param(76 [Parameter(ParameterSetName = 'Install', Mandatory)]77 [Switch]$Install,78 79 [Parameter(ParameterSetName = 'Uninstall', Mandatory)]80 [Switch]$Uninstall,81 82 [Parameter(Mandatory)]83 [ValidateScript({Test-Path -Path $_})]84 [String]$MsiPath,85 86 [Parameter(ParameterSetName = 'Install')]87 [HashTable] $Properties88 89 )90 $action = "$($PSCmdlet.ParameterSetName)ing"91 if ($Install.IsPresent) {92 $switch = '/I'93 } else {94 $switch = '/x'95 }96 97 $additionalOptions = @()98 if ($Properties) {99 foreach ($key in $Properties.Keys) {100 $additionalOptions += "$key=$($Properties.$key)"101 }102 }103 104 $argumentList = "$switch $MsiPath /quiet /l*vx $msiLog $additionalOptions"105 Write-Verbose -Message "running msiexec $argumentList"106 $msiExecProcess = Start-Process msiexec.exe -Wait -ArgumentList $argumentList -NoNewWindow -PassThru107 if ($msiExecProcess.ExitCode -ne 0) {108 $exitCode = $msiExecProcess.ExitCode109 throw "$action MSI failed and returned error code $exitCode."110 }111 }112 113 $msiX64Path = $env:PsMsiX64Path114 $channel = $env:PSMsiChannel115 $runtime = $env:PSMsiRuntime116 $muEnabled = Test-IsMuEnabled117 118 if ($runtime -like '*x86*') {119 $propertiesRegKeyParent = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore"120 } else {121 $propertiesRegKeyParent = "HKLM:\SOFTWARE\Microsoft\PowerShellCore"122 }123 124 if ($channel -eq "preview") {125 $propertiesRegKeyName = "PreviewInstallerProperties"126 } else {127 $propertiesRegKeyName = "InstallerProperties"128 }129 130 # Rename the registry key that contains the saved installer131 # properties so that the tests don't overwrite them.132 $propertiesRegKeyPath = Join-Path -Path $propertiesRegKeyParent -ChildPath $propertiesRegKeyName133 $propertiesBackupRegKeyName = "BackupInstallerProperties"134 $propertiesBackupRegKeyPath = Join-Path -Path $propertiesRegKeyParent -ChildPath $propertiesBackupRegKeyName135 if (Test-Path -Path $propertiesRegKeyPath) {136 Rename-Item -Path $propertiesRegKeyPath -NewName $propertiesBackupRegKeyName137 }138 139 # Get any existing powershell in the path140 $beforePath = @(([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |141 Where-Object {$_ -like '*files\powershell*'})142 143 $msiLog = Join-Path -Path $TestDrive -ChildPath 'msilog.txt'144 145 foreach ($pathPart in $beforePath) {146 Write-Warning "Found existing PowerShell path: $pathPart"147 }148 149 if (!(Test-Elevated)) {150 Write-Warning "Tests must be elevated"151 }152 $uploadedLog = $false153 }154 155 AfterAll {156 Set-StrictMode -Version 3.0157 158 # Restore the original saved installer properties registry key.159 Remove-Item -Path $propertiesRegKeyPath -ErrorAction SilentlyContinue160 if (Test-Path -Path $propertiesBackupRegKeyPath) {161 Rename-Item -Path $propertiesBackupRegKeyPath -NewName $propertiesRegKeyName162 }163 }164 165 BeforeEach {166 $error.Clear()167 Remove-Item -Path $propertiesRegKeyPath -ErrorAction SilentlyContinue168 }169 170 Context "Upgrade code" {171 BeforeAll {172 Write-Verbose "cr-$channel-$runtime" -Verbose173 $pwshPath = Join-Path $env:ProgramFiles -ChildPath "PowerShell"174 $pwshx86Path = Join-Path ${env:ProgramFiles(x86)} -ChildPath "PowerShell"175 $regKeyPath = "HKLM:\SOFTWARE\Microsoft\PowerShellCore\InstalledVersions"176 177 switch ("$channel-$runtime") {178 "preview-win7-x64" {179 $versionPath = Join-Path -Path $pwshPath -ChildPath '7-preview'180 $revisionRange = 0, 99181 $msiUpgradeCode = '39243d76-adaf-42b1-94fb-16ecf83237c8'182 $regKeyPath = Join-Path $regKeyPath -ChildPath $msiUpgradeCode183 }184 "stable-win7-x64" {185 $versionPath = Join-Path -Path $pwshPath -ChildPath '7'186 $revisionRange = 500, 500187 $msiUpgradeCode = '31ab5147-9a97-4452-8443-d9709f0516e1'188 $regKeyPath = Join-Path $regKeyPath -ChildPath $msiUpgradeCode189 }190 "preview-win7-x86" {191 $versionPath = Join-Path -Path $pwshx86Path -ChildPath '7-preview'192 $revisionRange = 0, 99193 $msiUpgradeCode = '86abcfbd-1ccc-4a88-b8b2-0facfde29094'194 $regKeyPath = Join-Path $regKeyPath -ChildPath $msiUpgradeCode195 }196 "stable-win7-x86" {197 $versionPath = Join-Path -Path $pwshx86Path -ChildPath '7'198 $revisionRange = 500, 500199 $msiUpgradeCode = '1d00683b-0f84-4db8-a64f-2f98ad42fe06'200 $regKeyPath = Join-Path $regKeyPath -ChildPath $msiUpgradeCode201 }202 default {203 throw "'$_' not a valid channel runtime combination"204 }205 }206 }207 208 It "$Channel MSI should not be installed before test" -Skip:(!(Test-Elevated)) {209 $result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")210 $result.Count | Should -Be 0 -Because "Query should return nothing if $channel $runtime is not installed"211 }212 213 It "MSI should install without error" -Skip:(!(Test-Elevated)) {214 {215 Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 1}216 } | Should -Not -Throw217 }218 219 It "Upgrade code should be correct" -Skip:(!(Test-Elevated)) {220 $result = @(Get-CimInstance -Query "SELECT Value FROM Win32_Property WHERE Property='UpgradeCode' and Value = '{$msiUpgradeCode}'")221 $result.Count | Should -Be 1 -Because "Query should return 1 result if Upgrade code is for $runtime $channel"222 }223 224 It "Revision should be in correct range" -Skip:(!(Test-Elevated)) {225 $pwshDllPath = Join-Path -Path $versionPath -ChildPath "pwsh.dll"226 [version] $version = (Get-ChildItem $pwshDllPath).VersionInfo.FileVersion227 Write-Verbose "pwsh.dll version: $version" -Verbose228 $version.Revision | Should -BeGreaterOrEqual $revisionRange[0] -Because "$channel revision should between $($revisionRange[0]) and $($revisionRange[1])"229 $version.Revision | Should -BeLessOrEqual $revisionRange[1] -Because "$channel revision should between $($revisionRange[0]) and $($revisionRange[1])"230 }231 232 It 'MSI should add ProductCode in registry' -Skip:(!(Test-Elevated)) {233 234 $productCode = if ($msiUpgradeCode -eq '39243d76-adaf-42b1-94fb-16ecf83237c8' -or235 $msiUpgradeCode -eq '31ab5147-9a97-4452-8443-d9709f0516e1') {236 # x64237 $regKeyPath | Should -Exist238 Get-ItemPropertyValue -Path $regKeyPath -Name 'ProductCode'239 } elseif ($msiUpgradeCode -eq '86abcfbd-1ccc-4a88-b8b2-0facfde29094' -or240 $msiUpgradeCode -eq '1d00683b-0f84-4db8-a64f-2f98ad42fe06') {241 # x86 - need to open the 32bit reghive242 $wow32RegKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry32)243 $subKey = $wow32RegKey.OpenSubKey("Software\Microsoft\PowerShellCore\InstalledVersions\$msiUpgradeCode")244 $subKey.GetValue("ProductCode")245 }246 247 $productCode | Should -Not -BeNullOrEmpty248 $productCodeGuid = [Guid]$productCode249 $productCodeGuid | Should -BeOfType "Guid"250 $productCodeGuid.Guid | Should -Not -Be $msiUpgradeCode251 }252 253 It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {254 {255 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path256 } | Should -Not -Throw257 }258 }259 260 Context "Add Path disabled" {261 BeforeAll {262 Set-UseMU -Value 0263 }264 265 It "UseMU should be 0 before install" -Skip:(!(Test-Elevated)) {266 $useMu = Get-UseMU267 $useMu | Should -Be 0268 }269 270 It "MSI should install without error" -Skip:(!(Test-Elevated)) {271 {272 Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 0; USE_MU = 1; ENABLE_MU = 1}273 } | Should -Not -Throw274 }275 276 It "MSI should have not be updated path" -Skip:(!(Test-Elevated)) {277 $psPath = ([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |278 Where-Object { $_ -like '*files\powershell*' -and $_ -notin $beforePath }279 280 $psPath | Should -BeNullOrEmpty281 }282 283 It "UseMU should be 1" -Skip:(!(Test-Elevated)) {284 Invoke-TestAndUploadLogOnFailure -Test {285 $useMu = Get-UseMU286 $useMu | Should -Be 1287 }288 }289 290 It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {291 {292 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path293 } | Should -Not -Throw294 }295 }296 297 Context "USE_MU disabled" {298 BeforeAll {299 Set-UseMU -Value 0300 }301 302 It "UseMU should be 0 before install" -Skip:(!(Test-Elevated)) {303 $useMu = Get-UseMU304 $useMu | Should -Be 0305 }306 307 It "MSI should install without error" -Skip:(!(Test-Elevated)) {308 {309 Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{USE_MU = 0}310 } | Should -Not -Throw311 }312 313 It "UseMU should be 0" -Skip:(!(Test-Elevated)) {314 Invoke-TestAndUploadLogOnFailure -Test {315 $useMu = Get-UseMU316 $useMu | Should -Be 0317 }318 }319 320 It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {321 {322 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path323 } | Should -Not -Throw324 }325 }326 327 Context "Add Path enabled" {328 It "MSI should install without error" -Skip:(!(Test-Elevated)) {329 {330 Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 1}331 } | Should -Not -Throw332 }333 334 It "MSI should have updated path" -Skip:(!(Test-Elevated)) {335 if ($channel -eq 'preview') {336 $pattern = '*files*\powershell*\preview*'337 } else {338 $pattern = '*files*\powershell*'339 }340 341 $psPath = ([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |342 Where-Object { $_ -like $pattern -and $_ -notin $beforePath }343 344 if (!$psPath) {345 ([System.Environment]::GetEnvironmentVariable('PATH', 'MACHINE')) -split ';' |346 Where-Object { $_ -notin $beforePath } |347 ForEach-Object { Write-Verbose -Verbose $_ }348 }349 350 $psPath | Should -Not -BeNullOrEmpty351 }352 353 It "MSI should uninstall without error" -Skip:(!(Test-Elevated)) {354 {355 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path356 } | Should -Not -Throw357 }358 359 Context "Disable Telemetry" {360 It "MSI should set POWERSHELL_TELEMETRY_OPTOUT env variable when MSI property DISABLE_TELEMETRY is set to 1" -Skip:(!(Test-Elevated)) {361 try {362 $originalValue = [System.Environment]::GetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', [System.EnvironmentVariableTarget]::Machine)363 [System.Environment]::SetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', '0', [System.EnvironmentVariableTarget]::Machine)364 {365 Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{DISABLE_TELEMETRY = 1 }366 } | Should -Not -Throw367 [System.Environment]::GetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', [System.EnvironmentVariableTarget]::Machine) |368 Should -Be 1369 }370 finally {371 [System.Environment]::SetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', $originalValue, [System.EnvironmentVariableTarget]::Machine)372 {373 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path374 } | Should -Not -Throw375 }376 }377 378 It "MSI should not change POWERSHELL_TELEMETRY_OPTOUT env variable when MSI property DISABLE_TELEMETRY not set" -Skip:(!(Test-Elevated)) {379 try {380 $originalValue = [System.Environment]::GetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', [System.EnvironmentVariableTarget]::Machine)381 [System.Environment]::SetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', 'untouched', [System.EnvironmentVariableTarget]::Machine)382 {383 Invoke-MsiExec -Install -MsiPath $msiX64Path384 } | Should -Not -Throw385 [System.Environment]::GetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', [System.EnvironmentVariableTarget]::Machine) |386 Should -Be 'untouched'387 }388 finally {389 [System.Environment]::SetEnvironmentVariable('POWERSHELL_TELEMETRY_OPTOUT', $originalValue, [System.EnvironmentVariableTarget]::Machine)390 {391 Invoke-MsiExec -Uninstall -MsiPath $msiX64Path392 } | Should -Not -Throw393 }394 }395 }396 }397}398 