I’ve written the following piece of code to automate the creation of a Group Policy that would configure all the new PowerShell 5 settings – ScriptBlock Logging, Protected EventLog and Transcripts – that Lee Holmes mentioned in the PowerShell ♥ the Blue Team post.
It should be run from a Windows 10 computer joined to the domain where you’ve the Remote Server Administration Tools (RSAT) installed because of the New-SelfsignedCertificate cmdlet and the GroupPolicy module requirement. If you’ve a PKI, you don’t need the self-signed certificate and may prefer using a certificate issued by your PKI. In this case, you start by enrolling the certificate from your PKI and the demo code below will use that one instead.
Last warning, the C:\Transcripts folder should exist on the computer targeted by the GPO and the NTFS security should be adjusted before applying the GPO.
Unfortunately the GPO doesn’t handle that requirement.
#Requires -RunasAdministrator | |
#Requires -Modules ActiveDirectory,GroupPolicy | |
# Make sure we can reach the PDC | |
$PDC = (Get-ADDomainController -Service 1 -Discover -ErrorAction SilentlyContinue).Hostname | |
if ($PDC) { | |
# Get the domain name | |
$DomainName = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name | |
# Create the GPO | |
try { | |
$GPO = New-GPO -Name 'PowerShell 5 Settings' -Domain "$($DomainName)" -ErrorAction Stop | |
} catch { | |
Write-Warning -Message "Failed to create PS5 GPO because $($_.Exception.Message)" | |
} | |
if ($GPO) { | |
# Don't need user settings | |
$GPO.GpoStatus = [Microsoft.GroupPolicy.GpoStatus]::UserSettingsDisabled | |
if (-not(Get-ChildItem -Path Cert:\LocalMachine\My\ -DocumentEncryptionCert -EA SilentlyContinue) { | |
# Create a self signed certificate | |
$CertHT = @{ | |
Subject = "CN=ProtectedEventLog@$($DomainName)"; | |
KeyLength = 2048; | |
KeySpec = 'KeyExchange'; | |
HashAlgorithm = 'SHA1'; | |
KeyExportPolicy = 'Exportable'; | |
KeyUsage = 'KeyEncipherment','DataEncipherment' ; | |
NotAfter = (Get-Date).AddYears(1); | |
TextExtension = '2.5.29.37={text}1.3.6.1.4.1.311.80.1'; | |
} | |
try { | |
$SSCert = New-SelfSignedCertificate @CertHT -ErrorAction Stop | |
} catch { | |
Write-Warning -Message "Self signed certificate issue because $($_.Exception.Message)" | |
} | |
} else { | |
# Use the first ocumentEncryption capable certificate (the one enrolled by a PKI, if any) | |
$SSCert = Get-ChildItem -Path Cert:\LocalMachine\My\ -DocumentEncryptionCert | Select -First 1 | |
} | |
if ($SSCert) { | |
# Export the public and private key | |
try { | |
Export-Certificate -Type CERT -Cert $SSCert -FilePath "$($HOME)\Documents\publickeyGPO.cer" -ErrorAction Stop | |
Export-PfxCertificate -Cert $SSCert -Password (ConvertTo-SecureString -AsPlainText '12345678' -Force) -FilePath "$($HOME)\Documents\privatekeyGPO.pfx" -ErrorAction Stop | |
} catch { | |
Write-Warning -Message "Exporting certificate failed because $($_.Exception.Message)" | |
} | |
# Get a base-64 encoded blob | |
$B64CertString = @" | |
-----BEGIN CERTIFICATE----- | |
$( | |
[Convert]::ToBase64String( | |
($SSCert).Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert), | |
'InsertLineBreaks' | |
) | |
) | |
-----END CERTIFICATE----- | |
"@ | |
# Main hashtable | |
$HT = @{ GUID = ($GPO).Id ; ErrorAction = 'Stop' } | |
# Array that stores each setting as a hashtable | |
@( | |
# Script Block Logging | |
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' ; | |
ValueName = 'EnableScriptBlockLogging' ; Type = 'String' ; Value = '1' | |
}, | |
# Do not enable script block invocation logging | |
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging' ; | |
ValueName = 'EnableScriptBlockInvocationLogging' ; Type = 'String' ; Value = '0' | |
}, | |
# Enable Protected Eventlog | |
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging' ; | |
ValueName = 'EnableProtectedEventLogging' ;Type = 'String' ; Value = '1' | |
}, | |
# Set its base-64 public key | |
@{ Key = 'HKLM\Software\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging' ; | |
ValueName = 'EncryptionCertificate' ; Type = 'String' ; Value = $B64CertString | |
}, | |
# Transcript | |
@{ Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' ; | |
ValueName = 'EnableTranscripting' ; Type = 'DWORD'; Value = 1 | |
}, | |
# Set its path | |
@{ Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' ; | |
ValueName = 'OutputDirectory' ; Type = 'String' ; Value = 'C:\Transcripts' | |
}, | |
# Do not enable invocation header | |
@{ Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription' ; | |
ValueName = 'EnableInvocationHeader' ; Type = 'DWORD' ; Value = 0 | |
} | |
) | ForEach-Object { | |
$reg = $_ | |
try { | |
Set-GPRegistryValue @HT @reg | |
} catch { | |
Write-Warning -Message "Faile to set GPO setting because $($_.Exception.Message)" | |
} | |
} | |
} | |
} | |
} |
Pingback: Create a GPO for PowerShell 5.0 settings | Tim Bolton - MCITP - MCTS
Pingback: Dew Drop - July 29, 2016 (#2298) - Morning Dew