Adding PSCore group policy definitions (#10468)

This commit is contained in:
Andrew
2019-09-12 13:41:59 -07:00
committed by Travis Plunk
parent 2aae04a659
commit ed29ad1506
8 changed files with 666 additions and 27 deletions
@@ -0,0 +1,88 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
<#
.Synopsis
Group Policy tools use administrative template files (.admx, .adml) to populate policy settings in the user interface.
This allows administrators to manage registry-based policy settings.
This script installes PowerShell Core Administrative Templates for Windows.
.Notes
The PowerShellCoreExecutionPolicy.admx and PowerShellCoreExecutionPolicy.adml files are
expected to be at the location specified by the Path parameter with default value of the location of this script.
#>
[CmdletBinding()]
param
(
[ValidateNotNullOrEmpty()]
[string] $Path = $PSScriptRoot
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Test-Elevated
{
[CmdletBinding()]
[OutputType([bool])]
Param()
# if the current Powershell session was called with administrator privileges,
# the Administrator Group's well-known SID will show up in the Groups for the current identity.
# Note that the SID won't show up unless the process is elevated.
return (([Security.Principal.WindowsIdentity]::GetCurrent()).Groups -contains "S-1-5-32-544")
}
$IsWindowsOs = $PSHOME.EndsWith('\WindowsPowerShell\v1.0', [System.StringComparison]::OrdinalIgnoreCase) -or $IsWindows
if (-not $IsWindowsOs)
{
throw 'This script must be run on Windows.'
}
if (-not (Test-Elevated))
{
throw 'This script must be run from an elevated process.'
}
if ([System.Management.Automation.Platform]::IsNanoServer)
{
throw 'Group policy definitions are not supported on Nano Server.'
}
$admxName = 'PowerShellCoreExecutionPolicy.admx'
$admlName = 'PowerShellCoreExecutionPolicy.adml'
$admx = Get-Item -Path (Join-Path -Path $Path -ChildPath $admxName)
$adml = Get-Item -Path (Join-Path -Path $Path -ChildPath $admlName)
$admxTargetPath = Join-Path -Path $env:WINDIR -ChildPath "PolicyDefinitions"
$admlTargetPath = Join-Path -Path $admxTargetPath -ChildPath "en-US"
$files = @($admx, $adml)
foreach ($file in $files)
{
if (-not (Test-Path -Path $file))
{
throw "Could not find $($file.Name) at $Path"
}
}
Write-Verbose "Copying $admx to $admxTargetPath"
Copy-Item -Path $admx -Destination $admxTargetPath -Force
$admxTargetFullPath = Join-Path -Path $admxTargetPath -ChildPath $admxName
if (Test-Path -Path $admxTargetFullPath)
{
Write-Verbose "$admxName was installed successfully"
}
else
{
Write-Error "Could not install $admxName"
}
Write-Verbose "Copying $adml to $admlTargetPath"
Copy-Item -Path $adml -Destination $admlTargetPath -Force
$admlTargetFullPath = Join-Path -Path $admlTargetPath -ChildPath $admlName
if (Test-Path -Path $admlTargetFullPath)
{
Write-Verbose "$admlName was installed successfully"
}
else
{
Write-Error "Could not install $admlName"
}
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?>
<policyDefinitionResources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" revision="1.0" schemaVersion="1.0" xmlns="http://www.microsoft.com/GroupPolicy/PolicyDefinitions">
<displayName>PowerShell Core</displayName>
<description>This file contains the configuration options for PowerShell Core</description>
<resources>
<stringTable>
<string id="AllScripts">Allow all scripts</string>
<string id="AllScriptsSigned">Allow only signed scripts</string>
<string id="EnableScripts">Turn on Script Execution</string>
<string id="EnableScripts_Explain">This policy setting lets you configure the script execution policy, controlling which scripts are allowed to run.
If you enable this policy setting, the scripts selected in the drop-down list are allowed to run.
The "Allow only signed scripts" policy setting allows scripts to execute only if they are signed by a trusted publisher.
The "Allow local scripts and remote signed scripts" policy setting allows any local scrips to run; scripts that originate from the internet must be signed by a trusted publisher.
The "Allow all scripts" policy setting allows all scripts to run.
If you disable this policy setting, no scripts are allowed to run.
Note: This policy setting exists under both "Computer Configuration" and "User Configuration" in the Local Group Policy Editor. The "Computer Configuration" has precedence over "User Configuration."
If you disable or do not configure this policy setting, it reverts to a per-machine preference setting; the default if that is not configured is "Allow local scripts and remote signed scripts."</string>
<string id="PowerShell">PowerShell Core</string>
<string id="RemoteSignedScripts">Allow local scripts and remote signed scripts</string>
<string id="SUPPORTED_WIN7">At least Microsoft Windows 7 or Windows Server 2008 family</string>
<string id="EnableModuleLogging">Turn on Module Logging</string>
<string id="EnableModuleLogging_Explain">
This policy setting allows you to turn on logging for PowerShell Core modules.
If you enable this policy setting, pipeline execution events for members of the specified modules are recorded in the PowerShell Core log in Event Viewer. Enabling this policy setting for a module is equivalent to setting the LogPipelineExecutionDetails property of the module to True.
If you disable this policy setting, logging of execution events is disabled for all PowerShell Core modules. Disabling this policy setting for a module is equivalent to setting the LogPipelineExecutionDetails property of the module to False.
If this policy setting is not configured, the LogPipelineExecutionDetails property of a module determines whether the execution events of a module are logged. By default, the LogPipelineExecutionDetails property of all modules is set to False.
To add modules to the policy setting list, click Show, and then type the module names in the list. The modules in the list must be installed on the computer.
Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting.
</string>
<string id="EnableTranscripting">Turn on PowerShell Transcription</string>
<string id="EnableTranscripting_Explain">
This policy setting lets you capture the input and output of PowerShell Core commands into text-based transcripts.
If you enable this policy setting, PowerShell Core will enable transcription logging for PowerShell Core and any other
applications that leverage the PowerShell Core engine. By default, PowerShell Core will record transcript output to each users' My Documents
directory, with a file name that includes 'PowerShell_transcript', along with the computer name and time started. Enabling this policy is equivalent
to calling the Start-Transcript cmdlet on each PowerShell Core session.
If you disable this policy setting, transcription logging of PowerShell-based applications is disabled by default, although transcripting can still be enabled
through the Start-Transcript cmdlet.
If you use the OutputDirectory setting to enable transcription logging to a shared location, be sure to limit access to that directory to prevent users
from viewing the transcripts of other users or computers.
Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting.
</string>
<string id="EnableScriptBlockLogging">Turn on PowerShell Script Block Logging</string>
<string id="EnableScriptBlockLogging_Explain">
This policy setting enables logging of all PowerShell script input to the Microsoft-Windows-PowerShell/Operational event log. If you enable this policy setting,
PowerShell Core will log the processing of commands, script blocks, functions, and scripts - whether invoked interactively, or through automation.
If you disable this policy setting, logging of PowerShell script input is disabled.
If you enable the Script Block Invocation Logging, PowerShell additionally logs events when invocation of a command, script block, function, or script
starts or stops. Enabling Invocation Logging generates a high volume of event logs.
Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting.
</string>
<string id="EnableUpdateHelpDefaultSourcePath">Set the default source path for Update-Help</string>
<string id="EnableUpdateHelpDefaultSourcePath_Explain">This policy setting allows you to set the default value of the SourcePath parameter on the Update-Help cmdlet.
If you enable this policy setting, the Update-Help cmdlet will use the specified value as the default value for the SourcePath parameter. This default value can be overridden by specifying a different value with the SourcePath parameter on the Update-Help cmdlet.
If this policy setting is disabled or not configured, this policy setting does not set a default value for the SourcePath parameter of the Update-Help cmdlet.
Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting.
</string>
<string id="ConsoleSessionConfiguration">Console session configuration</string>
<string id="ConsoleSessionConfiguration_Explain">Specifies a configuration endpoint in which PowerShell is run. This can be any endpoint registered on the local machine including the default PowerShell remoting endpoints or a custom endpoint having specific user role capabilities.</string>
<!--<string id="PowerShell">PowerShell Core</string>-->
</stringTable>
<presentationTable>
<presentation id="EnableScripts">
<checkBox refId="UseWindowsPowerShellPolicySetting">Use Windows PowerShell Policy setting.</checkBox>
<dropdownList refId="ExecutionPolicy" noSort="true">Execution Policy</dropdownList>
</presentation>
<presentation id="EnableModuleLogging">
<checkBox refId="UseWindowsPowerShellPolicySetting">Use Windows PowerShell Policy setting.</checkBox>
<text>To turn on logging for one or more modules, click Show, and then type the module names in the list. Wildcards are supported.</text>
<listBox refId="Listbox_ModuleNames" required="false">Module Names</listBox>
<text>To turn on logging for the PowerShell Core core modules, type the following module names in the list:</text>
<text>Microsoft.PowerShell.*</text>
<text>Microsoft.WSMan.Management</text>
</presentation>
<presentation id="EnableTranscripting">
<checkBox refId="UseWindowsPowerShellPolicySetting">Use Windows PowerShell Policy setting.</checkBox>
<textBox refId="OutputDirectory"><label>Transcript output directory</label></textBox>
<checkBox refId="EnableInvocationHeader">Include invocation headers:</checkBox>
</presentation>
<presentation id="EnableScriptBlockLogging">
<checkBox refId="UseWindowsPowerShellPolicySetting">Use Windows PowerShell Policy setting.</checkBox>
<checkBox refId="EnableScriptBlockInvocationLogging">Log script block invocation start / stop events:</checkBox>
</presentation>
<presentation id="EnableUpdateHelpDefaultSourcePath">
<checkBox refId="UseWindowsPowerShellPolicySetting">Use Windows PowerShell Policy setting.</checkBox>
<textBox refId="SourcePathForUpdateHelp">
<label>Default Source Path</label>
</textBox>
</presentation>
<presentation id="ConsoleSessionConfiguration">
<textBox refId="ConsoleSessionConfigurationName">
<label>ConsoleSessionConfigurationName</label>
</textBox>
</presentation>
</presentationTable>
</resources>
</policyDefinitionResources>
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<policyDefinitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" revision="1.0" schemaVersion="1.0" xmlns="http://www.microsoft.com/GroupPolicy/PolicyDefinitions">
<policyNamespaces>
<target prefix="powershellexecutionpolicy" namespace="Microsoft.Policies.PowerShellCore" />
<using prefix="windows" namespace="Microsoft.Policies.Windows" />
</policyNamespaces>
<resources minRequiredRevision="1.0" />
<supportedOn>
<definitions>
<definition name="SUPPORTED_WIN7" displayName="$(string.SUPPORTED_WIN7)" />
</definitions>
</supportedOn>
<categories>
<category name="PowerShell" displayName="$(string.PowerShell)">
</category>
</categories>
<policies>
<policy name="EnableScripts" class="Both" displayName="$(string.EnableScripts)" explainText="$(string.EnableScripts_Explain)" presentation="$(presentation.EnableScripts)" key="Software\Policies\Microsoft\PowerShellCore" valueName="EnableScripts">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<boolean id="UseWindowsPowerShellPolicySetting" valueName="UseWindowsPowerShellPolicySetting" />
<enum id="ExecutionPolicy" valueName="ExecutionPolicy" required="true">
<item displayName="$(string.AllScriptsSigned)">
<value>
<string>AllSigned</string>
</value>
</item>
<item displayName="$(string.RemoteSignedScripts)">
<value>
<string>RemoteSigned</string>
</value>
</item>
<item displayName="$(string.AllScripts)">
<value>
<string>Unrestricted</string>
</value>
</item>
</enum>
</elements>
</policy>
<policy name="EnableModuleLogging" class="Both" displayName="$(string.EnableModuleLogging)" explainText="$(string.EnableModuleLogging_Explain)" presentation="$(presentation.EnableModuleLogging)" key="Software\Policies\Microsoft\PowerShellCore\ModuleLogging" valueName="EnableModuleLogging">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<boolean id="UseWindowsPowerShellPolicySetting" valueName="UseWindowsPowerShellPolicySetting" />
<list id="Listbox_ModuleNames" key="Software\Policies\Microsoft\PowerShellCore\ModuleLogging\ModuleNames" />
</elements>
</policy>
<policy name="EnableTranscripting" class="Both" displayName="$(string.EnableTranscripting)" explainText="$(string.EnableTranscripting_Explain)" presentation="$(presentation.EnableTranscripting)" key="Software\Policies\Microsoft\PowerShellCore\Transcription" valueName="EnableTranscripting">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<boolean id="UseWindowsPowerShellPolicySetting" valueName="UseWindowsPowerShellPolicySetting" />
<text id="OutputDirectory" valueName="OutputDirectory" />
<boolean id="EnableInvocationHeader" valueName="EnableInvocationHeader" />
</elements>
</policy>
<policy name="EnableScriptBlockLogging" class="Both" displayName="$(string.EnableScriptBlockLogging)" explainText="$(string.EnableScriptBlockLogging_Explain)" presentation="$(presentation.EnableScriptBlockLogging)" key="Software\Policies\Microsoft\PowerShellCore\ScriptBlockLogging" valueName="EnableScriptBlockLogging">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<boolean id="UseWindowsPowerShellPolicySetting" valueName="UseWindowsPowerShellPolicySetting" />
<boolean id="EnableScriptBlockInvocationLogging" valueName="EnableScriptBlockInvocationLogging" />
</elements>
</policy>
<policy name="EnableUpdateHelpDefaultSourcePath" class="Both" displayName="$(string.EnableUpdateHelpDefaultSourcePath)" explainText="$(string.EnableUpdateHelpDefaultSourcePath_Explain)" presentation="$(presentation.EnableUpdateHelpDefaultSourcePath)" key="Software\Policies\Microsoft\PowerShellCore\UpdatableHelp" valueName="EnableUpdateHelpDefaultSourcePath">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<boolean id="UseWindowsPowerShellPolicySetting" valueName="UseWindowsPowerShellPolicySetting" />
<text id="SourcePathForUpdateHelp" valueName="DefaultSourcePath" required="true"/>
</elements>
</policy>
<policy name="ConsoleSessionConfiguration" class="Both" displayName="$(string.ConsoleSessionConfiguration)" explainText="$(string.ConsoleSessionConfiguration_Explain)" presentation="$(presentation.ConsoleSessionConfiguration)" key="Software\Policies\Microsoft\PowerShellCore\ConsoleSessionConfiguration" valueName="EnableConsoleSessionConfiguration">
<parentCategory ref="PowerShell" />
<supportedOn ref="SUPPORTED_WIN7" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
<elements>
<text id="ConsoleSessionConfigurationName" valueName="ConsoleSessionConfigurationName" required="true"/>
</elements>
</policy>
</policies>
</policyDefinitions>
+12
View File
@@ -3051,6 +3051,15 @@
<Component Id="cmp712842BFA391403DA3F21B2A4C729ED3" Guid="{e141999c-71cc-4c7f-9f2b-11b1005bd5dc}">
<File Id="filDB5CFCFC242B419586E36CD5B281216A" KeyPath="yes" Source="$(env.ProductSourcePath)\mscordaccore_$(var.FileArchitecture)_$(var.FileArchitecture)_4.700.19.42102.dll" />
</Component>
<Component Id="cmpECBD8DFB18AD451AB3D81803DECC13BF" Guid="{c6e5a982-3e3d-4e16-82c3-34c564bec4fe}">
<File Id="fil639FFF2D706848E18758EE576F1B527D" KeyPath="yes" Source="$(env.ProductSourcePath)\PowerShellCoreExecutionPolicy.adml" />
</Component>
<Component Id="cmp01B3850D6E55468AA84B758FDE8CA59E" Guid="{a3d1978c-325b-4795-bb3a-cbfb8b29c855}">
<File Id="filFE64839ED9F24059906D693E9D52015A" KeyPath="yes" Source="$(env.ProductSourcePath)\PowerShellCoreExecutionPolicy.admx" />
</Component>
<Component Id="cmpF6FDA6202E9D4CEF9F21A23D32571BFC" Guid="{e0776aa4-b3aa-4a15-a497-697b510fba8c}">
<File Id="fil9120D0A4381F4B34B80AB37D2EFBCF7D" KeyPath="yes" Source="$(env.ProductSourcePath)\InstallPSCorePolicyDefinitions.ps1" />
</Component>
</DirectoryRef>
</Fragment>
<Fragment>
@@ -3888,6 +3897,9 @@
<ComponentRef Id="cmpD7A187ADE66347E5A7550B96BA10D493" />
<ComponentRef Id="cmp827B25EEC28D4699BB29042B273C8CBF" />
<ComponentRef Id="cmp712842BFA391403DA3F21B2A4C729ED3" />
<ComponentRef Id="cmpECBD8DFB18AD451AB3D81803DECC13BF" />
<ComponentRef Id="cmp01B3850D6E55468AA84B758FDE8CA59E" />
<ComponentRef Id="cmpF6FDA6202E9D4CEF9F21A23D32571BFC" />
</ComponentGroup>
</Fragment>
</Wix>
@@ -807,40 +807,43 @@ namespace System.Management.Automation
{nameof(ConsoleSessionConfiguration), @"Software\Policies\Microsoft\PowerShellCore\ConsoleSessionConfiguration"}
};
private static readonly Dictionary<string, string> WindowsPowershellGroupPolicyKeys = new Dictionary<string, string>
{
{nameof(ScriptExecution), @"Software\Policies\Microsoft\Windows\PowerShell"},
{nameof(ScriptBlockLogging), @"Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"},
{nameof(ModuleLogging), @"Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging"},
{nameof(Transcription), @"Software\Policies\Microsoft\Windows\PowerShell\Transcription"},
{nameof(UpdatableHelp), @"Software\Policies\Microsoft\Windows\PowerShell\UpdatableHelp"},
};
private const string PolicySettingFallbackKey = "UseWindowsPowerShellPolicySetting";
private static readonly ConcurrentDictionary<ConfigScope, ConcurrentDictionary<string, PolicyBase>> s_cachedPoliciesFromRegistry =
new ConcurrentDictionary<ConfigScope, ConcurrentDictionary<string, PolicyBase>>();
private static readonly Func<ConfigScope, ConcurrentDictionary<string, PolicyBase>> s_subCacheCreationDelegate =
key => new ConcurrentDictionary<string, PolicyBase>(StringComparer.OrdinalIgnoreCase);
key => new ConcurrentDictionary<string, PolicyBase>(StringComparer.Ordinal);
/// <summary>
/// The implementation of fetching a specific kind of policy setting from the given configuration scope.
/// Read policy settings from a registry key into a policy object.
/// </summary>
private static T GetPolicySettingFromGPOImpl<T>(ConfigScope scope) where T : PolicyBase, new()
/// <param name="instance">Policy object that will be filled with values from registry.</param>
/// <param name="instanceType">Type of policy object used.</param>
/// <param name="gpoKey">Registry key that has policy settings.</param>
/// <returns>True if any property was successfully set on the policy object.</returns>
private static bool TrySetPolicySettingsFromRegistryKey(object instance, Type instanceType, RegistryKey gpoKey)
{
Type tType = typeof(T);
// SystemWide scope means 'LocalMachine' root key when query from registry
RegistryKey rootKey = (scope == ConfigScope.AllUsers) ? Registry.LocalMachine : Registry.CurrentUser;
var properties = instanceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
bool isAnyPropertySet = false;
GroupPolicyKeys.TryGetValue(tType.Name, out string gpoKeyPath);
Diagnostics.Assert(gpoKeyPath != null, StringUtil.Format("The GPO registry key path should be pre-defined for {0}", tType.Name));
string[] valueNames = gpoKey.GetValueNames();
string[] subKeyNames = gpoKey.GetSubKeyNames();
var valueNameSet = valueNames.Length > 0 ? new HashSet<string>(valueNames, StringComparer.OrdinalIgnoreCase) : null;
var subKeyNameSet = subKeyNames.Length > 0 ? new HashSet<string>(subKeyNames, StringComparer.OrdinalIgnoreCase) : null;
using (RegistryKey gpoKey = rootKey.OpenSubKey(gpoKeyPath))
// If there are any values or subkeys in the registry key - read them into the policy instance object
if ((valueNameSet != null) || (subKeyNameSet != null))
{
// If the corresponding GPO key doesn't exist, return null
if (gpoKey == null) { return null; }
// The corresponding GPO key exists, then create an instance of T
// and populate its properties with the settings
object tInstance = Activator.CreateInstance(tType, nonPublic: true);
var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
bool isAnyPropertySet = false;
string[] valueNames = gpoKey.GetValueNames();
string[] subKeyNames = gpoKey.GetSubKeyNames();
var valueNameSet = valueNames.Length > 0 ? new HashSet<string>(valueNames, StringComparer.OrdinalIgnoreCase) : null;
var subKeyNameSet = subKeyNames.Length > 0 ? new HashSet<string>(subKeyNames, StringComparer.OrdinalIgnoreCase) : null;
foreach (var property in properties)
{
string settingName = property.Name;
@@ -895,18 +898,61 @@ namespace System.Management.Automation
break;
default:
Diagnostics.Assert(false, "Should be unreachable code. Update this switch block when properties of new types are added to PowerShell policy types.");
break;
throw System.Management.Automation.Interpreter.Assert.Unreachable;
}
// Set the property if the value is not null
if (propertyValue != null)
{
property.SetValue(tInstance, propertyValue);
property.SetValue(instance, propertyValue);
isAnyPropertySet = true;
}
}
}
}
return isAnyPropertySet;
}
/// <summary>
/// The implementation of fetching a specific kind of policy setting from the given configuration scope.
/// </summary>
private static T GetPolicySettingFromGPOImpl<T>(ConfigScope scope) where T : PolicyBase, new()
{
Type tType = typeof(T);
// SystemWide scope means 'LocalMachine' root key when query from registry
RegistryKey rootKey = (scope == ConfigScope.AllUsers) ? Registry.LocalMachine : Registry.CurrentUser;
GroupPolicyKeys.TryGetValue(tType.Name, out string gpoKeyPath);
Diagnostics.Assert(gpoKeyPath != null, StringUtil.Format("The GPO registry key path should be pre-defined for {0}", tType.Name));
using (RegistryKey gpoKey = rootKey.OpenSubKey(gpoKeyPath))
{
// If the corresponding GPO key doesn't exist, return null
if (gpoKey == null) { return null; }
// The corresponding GPO key exists, then create an instance of T
// and populate its properties with the settings
object tInstance = Activator.CreateInstance(tType, nonPublic: true);
bool isAnyPropertySet = false;
// if PolicySettingFallbackKey is Not set - use PowerShell Core policy reg key
if ((int)gpoKey.GetValue(PolicySettingFallbackKey, 0) == 0)
{
isAnyPropertySet = TrySetPolicySettingsFromRegistryKey(tInstance, tType, gpoKey);
}
else
{
// when PolicySettingFallbackKey flag is set (REG_DWORD "1") use Windows PS policy reg key
WindowsPowershellGroupPolicyKeys.TryGetValue(tType.Name, out string winPowershellGpoKeyPath);
Diagnostics.Assert(winPowershellGpoKeyPath != null, StringUtil.Format("The Windows PS GPO registry key path should be pre-defined for {0}", tType.Name));
using (RegistryKey winPowershellGpoKey = rootKey.OpenSubKey(winPowershellGpoKeyPath))
{
// If the corresponding Windows PS GPO key doesn't exist, return null
if (winPowershellGpoKey == null) { return null; }
isAnyPropertySet = TrySetPolicySettingsFromRegistryKey(tInstance, tType, winPowershellGpoKey);
}
}
// If no property is set, then we consider this policy as undefined
return isAnyPropertySet ? (T)tInstance : null;
@@ -35,7 +35,7 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="..\..\LICENSE.txt;..\..\ThirdPartyNotices.txt;..\powershell-native\Install-PowerShellRemoting.ps1;..\PowerShell.Core.Instrumentation\PowerShell.Core.Instrumentation.man;..\PowerShell.Core.Instrumentation\RegisterManifest.ps1">
<Content Include="..\..\LICENSE.txt;..\..\ThirdPartyNotices.txt;..\powershell-native\Install-PowerShellRemoting.ps1;..\PowerShell.Core.Instrumentation\PowerShell.Core.Instrumentation.man;..\PowerShell.Core.Instrumentation\RegisterManifest.ps1;..\..\assets\GroupPolicy\PowerShellCoreExecutionPolicy.admx;..\..\assets\GroupPolicy\PowerShellCoreExecutionPolicy.adml;..\..\assets\GroupPolicy\InstallPSCorePolicyDefinitions.ps1">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
@@ -0,0 +1,248 @@
# 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 -Be $False # 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 -Be $True # 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 = 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 be $True # verifies that Transcription policy works
$Log.Contains("Command start time:") | should be $True # 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
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 be $True
Remove-Item -Path $LogPath -Force
}
$KeyPath = Join-Path $KeyRoot 'ConsoleSessionConfiguration'
if (-not (Test-Path $KeyPath)) {$null = New-Item $KeyPath}
TestFeature -KeyPath $KeyPath
}
}
}
+1
View File
@@ -30,6 +30,7 @@
<file src="__INPATHROOT__\Install-PowerShellRemoting.ps1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\Install-PowerShellRemoting.ps1" />
<file src="__INPATHROOT__\RegisterManifest.ps1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\RegisterManifest.ps1" />
<file src="__INPATHROOT__\InstallPSCorePolicyDefinitions.ps1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\InstallPSCorePolicyDefinitions.ps1" />
<file src="__INPATHROOT__\Modules\CimCmdlets\CimCmdlets.psd1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\Modules\CimCmdlets\CimCmdlets.psd1" />
<file src="__INPATHROOT__\Modules\Microsoft.PowerShell.Diagnostics\Microsoft.PowerShell.Diagnostics.psd1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\Modules\Microsoft.PowerShell.Diagnostics\Microsoft.PowerShell.Diagnostics.psd1" />
<file src="__INPATHROOT__\Modules\Microsoft.PowerShell.Host\Microsoft.PowerShell.Host.psd1" signType="AuthenticodeFormer" dest="__OUTPATHROOT__\Modules\Microsoft.PowerShell.Host\Microsoft.PowerShell.Host.psd1" />