PowerShell/test/powershell/engine/Basic/GroupPolicySettings.Tests.ps1

249 lines
12 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe 'Group policy settings tests' -Tag CI,RequireAdminOnWindows {
BeforeAll {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
if ( ! $IsWindows ) {
$PSDefaultParameterValues["it:skip"] = $true
}
else {
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('BypassGroupPolicyCaching', $true)
}
}
AfterAll {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
if ( $IsWindows ) {
[System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('BypassGroupPolicyCaching', $false)
}
}
Context 'Group policy settings tests' {
BeforeEach {
$KeyRoot = 'HKCU:\Software\Policies\Microsoft\PowerShellCore'
if (-not (Test-Path $KeyRoot)) {$null = New-Item $KeyRoot}
$WinPSKeyRoot = 'HKCU:\Software\Policies\Microsoft\Windows\PowerShell'
if (-not (Test-Path $WinPSKeyRoot)) {$null = New-Item $WinPSKeyRoot}
}
AfterEach {
Remove-Item $KeyRoot -Recurse -Force > $null
Remove-Item $WinPSKeyRoot -Recurse -Force > $null
}
It 'Execution policy test' {
function TestFeature
{
param([string]$KeyPath)
Set-ItemProperty -Path $KeyPath -Name EnableScripts -Value 1 -Force
Set-ItemProperty -Path $KeyPath -Name ExecutionPolicy -Value 'Unrestricted' -Force
(Get-ExecutionPolicy) | Should -Be 'Unrestricted'
Set-ItemProperty -Path $KeyPath -Name ExecutionPolicy -Value 'AllSigned' -Force
(Get-ExecutionPolicy) | Should -Be 'AllSigned'
Set-ItemProperty -Path $KeyPath -Name ExecutionPolicy -Value 'RemoteSigned' -Force
(Get-ExecutionPolicy) | Should -Be 'RemoteSigned'
Remove-ItemProperty -Path $KeyPath -Name ExecutionPolicy -Force
}
TestFeature -KeyPath $KeyRoot
Set-ItemProperty -Path $KeyRoot -Name UseWindowsPowerShellPolicySetting -Value 1 -Force
TestFeature -KeyPath $WinPSKeyRoot
}
It 'Module logging policy test' {
function TestFeature
{
param([string]$KeyPath)
$ModuleToLog = 'Microsoft.PowerShell.Utility'
$ModuleNamesKeyPath = Join-Path $KeyPath 'ModuleNames'
if (-not (Test-Path $ModuleNamesKeyPath)) {$null = New-Item $ModuleNamesKeyPath}
Remove-Module $ModuleToLog -ErrorAction SilentlyContinue
Import-Module $ModuleToLog
(Get-Module $ModuleToLog).LogPipelineExecutionDetails | Should -BeFalse # without GP logging for the module should be OFF
# enable GP
[string]$RareCommand = Get-Random
Set-ItemProperty -Path $KeyPath -Name EnableModuleLogging -Value 1 -Force
Set-ItemProperty -Path $ModuleNamesKeyPath -Name $ModuleToLog -Value $ModuleToLog -Force
Remove-Module $ModuleToLog -ErrorAction SilentlyContinue
Import-Module $ModuleToLog # this will read and start using GP setting
(Get-Module $ModuleToLog).LogPipelineExecutionDetails | Should -BeTrue # with GP logging for the module should be ON
Get-Alias $RareCommand -ErrorAction SilentlyContinue | Out-Null
(Get-Module $ModuleToLog).LogPipelineExecutionDetails = $false # turn off logging
Remove-ItemProperty -Path $KeyPath -Name EnableModuleLogging -Force # turn off GP setting
Remove-Item $ModuleNamesKeyPath -Recurse -Force
# usually event becomes visible in the log after ~500 ms
# set timeout for 5 seconds
Wait-UntilTrue -sb { Get-WinEvent -FilterHashtable @{ ProviderName="PowerShellCore"; Id = 4103 } -MaxEvents 5 | ? {$_.Message.Contains($RareCommand)} } -TimeoutInMilliseconds (5*1000) -IntervalInMilliseconds 100 | Should -BeTrue
}
$KeyPath = Join-Path $KeyRoot 'ModuleLogging'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
Set-ItemProperty -Path $KeyPath -Name UseWindowsPowerShellPolicySetting -Value 1 -Force
$WinKeyPath = Join-Path $WinPSKeyRoot 'ModuleLogging'
if (-not (Test-Path $WinKeyPath)) {$null = New-Item $WinKeyPath}
TestFeature -KeyPath $WinKeyPath
}
It 'ScriptBlock logging policy test' {
function TestFeature
{
param([string]$KeyPath)
[string]$RareCommand = Get-Random
Set-ItemProperty -Path $KeyPath -Name EnableScriptBlockLogging -Value 1 -Force
Set-ItemProperty -Path $KeyPath -Name EnableScriptBlockInvocationLogging -Value 1 -Force
Invoke-Expression "$RareCommand | Out-Null"
Remove-ItemProperty -Path $KeyPath -Name EnableScriptBlockLogging -Force
Remove-ItemProperty -Path $KeyPath -Name EnableScriptBlockInvocationLogging -Force
# usually event becomes visible in the log after ~500 ms
# set timeout for 5 seconds
Wait-UntilTrue -sb { $script:CreatingScriptblockEvent = Get-WinEvent -FilterHashtable @{ ProviderName="PowerShellCore"; Id = 4104 } -MaxEvents 5 | ? {$_.Message.Contains($RareCommand)}; $script:CreatingScriptblockEvent } -TimeoutInMilliseconds (5*1000) -IntervalInMilliseconds 100 | Should -BeTrue
$sbStringStart = $script:CreatingScriptblockEvent.Message.IndexOf('ScriptBlock ID:')
$sbStringEnd = $script:CreatingScriptblockEvent.Message.IndexOf(0x0D, $sbStringStart)
$sbString = $script:CreatingScriptblockEvent.Message.Substring($sbStringStart, $sbStringEnd - $sbStringStart)
$StartedScriptBlockInvocationEvent = Get-WinEvent -FilterHashtable @{ ProviderName="PowerShellCore"; Id = 4105 } -MaxEvents 5 | ? {$_.Message.Contains($sbString)}
$StartedScriptBlockInvocationEvent | Should -Not -BeNullOrEmpty
$CompletedScriptBlockInvocationEvent = Get-WinEvent -FilterHashtable @{ ProviderName="PowerShellCore"; Id = 4106 } -MaxEvents 5 | ? {$_.Message.Contains($sbString)}
$CompletedScriptBlockInvocationEvent | Should -Not -BeNullOrEmpty
}
$KeyPath = Join-Path $KeyRoot 'ScriptBlockLogging'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
Set-ItemProperty -Path $KeyPath -Name UseWindowsPowerShellPolicySetting -Value 1 -Force
$WinKeyPath = Join-Path $WinPSKeyRoot 'ScriptBlockLogging'
if (-not (Test-Path $WinKeyPath)) {$null = New-Item $WinKeyPath}
TestFeature -KeyPath $WinKeyPath
}
It 'Transcription policy test' {
function TestFeature
{
param([string]$KeyPath)
$OutputDirectory = Join-Path $([System.IO.Path]::GetTempPath()) $(Get-Random)
$null = New-Item -Type Directory -Path $OutputDirectory -Force
Set-ItemProperty -Path $KeyPath -Name EnableTranscripting -Value 1 -Force
Set-ItemProperty -Path $KeyPath -Name OutputDirectory -Value $OutputDirectory -Force
Set-ItemProperty -Path $KeyPath -Name EnableInvocationHeader -Value 1 -Force
$number = Get-Random
$null = & "$PSHOME/pwsh" -NoProfile -NonInteractive -c "$number"
Remove-ItemProperty -Path $KeyPath -Name OutputDirectory -Force
Remove-ItemProperty -Path $KeyPath -Name EnableInvocationHeader -Force
$LogPath = (gci -Path $OutputDirectory -Filter "PowerShell_transcript*.txt" -Recurse).FullName
$Log = Get-Content $LogPath -Raw
$Log.Contains("$number") | Should -BeTrue # verifies that Transcription policy works
$Log.Contains("Command start time:") | Should -BeTrue # verifies that EnableInvocationHeader works
Remove-Item -Path $OutputDirectory -Recurse -Force
}
$KeyPath = Join-Path $KeyRoot 'Transcription'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
Set-ItemProperty -Path $KeyPath -Name UseWindowsPowerShellPolicySetting -Value 1 -Force
$WinKeyPath = Join-Path $WinPSKeyRoot 'Transcription'
if (-not (Test-Path $WinKeyPath)) {$null = New-Item $WinKeyPath}
TestFeature -KeyPath $WinKeyPath
}
It 'Default SourcePath on Update-Help policy test' {
function TestFeature
{
param([string]$KeyPath)
$HelpPath = Join-Path 'TestDrive:\' $(Get-Random)
$null = New-Item -Type Directory -Path $HelpPath -ErrorAction SilentlyContinue
$ModuleName = 'Microsoft.PowerShell.Utility'
Save-Help -Module $ModuleName -DestinationPath $HelpPath -Force
Set-ItemProperty -Path $KeyPath -Name EnableUpdateHelpDefaultSourcePath -Value 1 -Force
Set-ItemProperty -Path $KeyPath -Name DefaultSourcePath -Value $HelpPath -Force
# this should throw error cause we didn't save the help for this module locally;
# this ensures that Update-Help is not going to Internet to download help
{ Update-Help -Module Microsoft.PowerShell.Management -Force -ErrorAction Stop } | Should -Throw -ErrorId "UnableToRetrieveHelpInfoXml,Microsoft.PowerShell.Commands.UpdateHelpCommand"
# this should use saved help in location specified in the policy and should NOT throw error
Update-Help -Module Microsoft.PowerShell.Utility -Force
}
$HKLM_KeyRoot = 'HKLM:\Software\Policies\Microsoft\PowerShellCore'
if (-not (Test-Path $HKLM_KeyRoot)) {$null = New-Item $HKLM_KeyRoot}
$KeyPath = Join-Path $HKLM_KeyRoot 'UpdatableHelp'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
Set-ItemProperty -Path $KeyPath -Name UseWindowsPowerShellPolicySetting -Value 1 -Force
$HKLM_WinPSKeyRoot = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell'
if (-not (Test-Path $HKLM_WinPSKeyRoot)) {$null = New-Item $HKLM_WinPSKeyRoot}
$WinKeyPath = Join-Path $HKLM_WinPSKeyRoot 'UpdatableHelp'
if (-not (Test-Path $WinKeyPath)) {$null = New-Item $WinKeyPath}
TestFeature -KeyPath $WinKeyPath
Remove-Item $HKLM_KeyRoot -Recurse -Force
Remove-Item $HKLM_WinPSKeyRoot -Recurse -Force
}
It 'Session configuration policy test' {
function TestFeature
{
param([string]$KeyPath)
# set policy to use unique non-existing configuration session name
$SessionName = "TestSessionConfiguration-$(Get-Random)"
Set-ItemProperty -Path $KeyPath -Name EnableConsoleSessionConfiguration -Value 1 -Force
Set-ItemProperty -Path $KeyPath -Name ConsoleSessionConfigurationName -Value $SessionName -Force
$LogPath = (New-TemporaryFile).FullName
& "$PSHOME/pwsh" -NoProfile -NonInteractive -c "1" *> $LogPath # this implicitly uses SessionConfiguration from the policy
# Log should have an error that has our configuration session name; e.g.:
# 'The shell cannot be started. A failure occurred during initialization:
# Cannot create or open the configuration session 116337267.'
$Log = Get-Content $LogPath -Raw
$Log.Contains("$SessionName") | Should -BeTrue
Remove-Item -Path $LogPath -Force
}
$KeyPath = Join-Path $KeyRoot 'ConsoleSessionConfiguration'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
}
}
}