Browse Source

fix

pull/378/head
Dmitry Nefedov 2 years ago
parent
commit
65875141a8
  1. 555
      Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/Commands.ps1
  2. 704
      Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/Common.ps1
  3. BIN
      Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolFileEditor.dll
  4. 43
      Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolicyFileEditor.psd1
  5. 10
      Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolicyFileEditor.psm1

555
Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/Commands.ps1

@ -1,555 +0,0 @@
#requires -Version 2.0
$scriptRoot = Split-Path $MyInvocation.MyCommand.Path
. "$scriptRoot\Common.ps1"
<#
.SYNOPSIS
Creates or modifies a value in a .pol file.
.DESCRIPTION
Creates or modifies a value in a .pol file. By default, also updates the version number in the policy's gpt.ini file.
.PARAMETER Path
Path to the .pol file that is to be modified.
.PARAMETER Key
The registry key inside the .pol file that you want to modify.
.PARAMETER ValueName
The name of the registry value. May be set to an empty string to modify the default value of a key.
.PARAMETER Data
The new value to assign to the registry key / value. Cannot be $null, but can be set to an empty string or empty array.
.PARAMETER Type
The type of registry value to set in the policy file. Cannot be set to Unknown or None, but all other values of the RegistryValueKind enum are legal.
.PARAMETER NoGptIniUpdate
When this switch is used, the command will not attempt to update the version number in the gpt.ini file
.EXAMPLE
Set-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -Key Software\Policies\Something -ValueName SomeValue -Data 'Hello, World!' -Type String
Assigns a value of 'Hello, World!' to the String value Software\Policies\Something\SomeValue in the local computer Machine GPO. Updates the Machine version counter in $env:systemroot\system32\GroupPolicy\gpt.ini
.EXAMPLE
Set-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -Key Software\Policies\Something -ValueName SomeValue -Data 'Hello, World!' -Type String -NoGptIniUpdate
Same as example 1, except this one does not update gpt.ini right away. This can be useful if you want to set multiple
values in the policy file and only trigger a single Group Policy refresh.
.EXAMPLE
Set-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -Key Software\Policies\Something -ValueName SomeValue -Data '0x12345' -Type DWord
Example demonstrating that strings with valid numeric data (including hexadecimal strings beginning with 0x) can be assigned to the numeric types DWord, QWord and Binary.
.EXAMPLE
$entries = @(
New-Object psobject -Property @{ ValueName = 'MaxXResolution'; Data = 1680 }
New-Object psobject -Property @{ ValueName = 'MaxYResolution'; Data = 1050 }
)
$entries | Set-PolicyFileEntry -Path $env:SystemRoot\system32\GroupPolicy\Machine\registry.pol `
-Key 'SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' `
-Type DWord
Example of using pipeline input to set multiple values at once. The advantage to this approach is that the
.pol file on disk (and the GPT.ini file) will be updated if _any_ of the specified settings had to be modified,
and will be left alone if the file already contained all of the correct values.
The Key and Type properties could have also been specified via the pipeline objects instead of on the command line,
but since both values shared the same Key and Type, this example shows that you can pass the values in either way.
.INPUTS
The Key, ValueName, Data, and Type properties may be bound via the pipeline by property name.
.OUTPUTS
None. This command does not generate output.
.NOTES
If the specified policy file already contains the correct value, the file will not be modified, and the gpt.ini file will not be updated.
.LINK
Get-PolicyFileEntry
.LINK
Remove-PolicyFileEntry
.LINK
Update-GptIniVersion
.LINK
about_RegistryValuesForAdminTemplates
#>
function Set-PolicyFileEntry
{
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string] $Path,
[Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
[string] $Key,
[Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[string] $ValueName,
[Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)]
[AllowEmptyString()]
[AllowEmptyCollection()]
[object] $Data,
[Parameter(ValueFromPipelineByPropertyName = $true)]
[ValidateScript({
if ($_ -eq [Microsoft.Win32.RegistryValueKind]::Unknown)
{
throw 'Unknown is not a valid value for the Type parameter'
}
if ($_ -eq [Microsoft.Win32.RegistryValueKind]::None)
{
throw 'None is not a valid value for the Type parameter'
}
return $true
})]
[Microsoft.Win32.RegistryValueKind] $Type = [Microsoft.Win32.RegistryValueKind]::String,
[switch] $NoGptIniUpdate
)
begin
{
if (Get-Command [G]et-CallerPreference -CommandType Function -Module PreferenceVariables)
{
Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
}
$dirty = $false
try
{
$policyFile = OpenPolicyFile -Path $Path -ErrorAction Stop
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}
process
{
$existingEntry = $policyFile.GetValue($Key, $ValueName)
if ($null -ne $existingEntry -and $Type -eq (PolEntryTypeToRegistryValueKind $existingEntry.Type))
{
$existingData = GetEntryData -Entry $existingEntry -Type $Type
if (DataIsEqual $Data $existingData -Type $Type)
{
Write-Verbose "Policy setting '$Key\$ValueName' is already set to '$Data' of type '$Type'."
return
}
}
Write-Verbose "Configuring '$Key\$ValueName' to value '$Data' of type '$Type'."
try
{
switch ($Type)
{
([Microsoft.Win32.RegistryValueKind]::Binary)
{
$bytes = $Data -as [byte[]]
if ($null -eq $bytes)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'When -Type is set to Binary, -Data must be passed a Byte[] array.'
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$policyFile.SetBinaryValue($Key, $ValueName, $bytes)
}
break
}
([Microsoft.Win32.RegistryValueKind]::String)
{
$array = @($Data)
if ($array.Count -ne 1)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'When -Type is set to String, -Data must be passed a scalar value or single-element array.'
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$policyFile.SetStringValue($Key, $ValueName, $array[0].ToString())
}
break
}
([Microsoft.Win32.RegistryValueKind]::ExpandString)
{
$array = @($Data)
if ($array.Count -ne 1)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'When -Type is set to ExpandString, -Data must be passed a scalar value or single-element array.'
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$policyFile.SetStringValue($Key, $ValueName, $array[0].ToString(), $true)
}
break
}
([Microsoft.Win32.RegistryValueKind]::DWord)
{
$array = @($Data)
$dword = ($array | Select-Object -First 1) -as [UInt32]
if ($null -eq $dword -or $array.Count -ne 1)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'When -Type is set to DWord, -Data must be passed a valid UInt32 value.'
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$policyFile.SetDWORDValue($key, $ValueName, $dword)
}
break
}
([Microsoft.Win32.RegistryValueKind]::QWord)
{
$array = @($Data)
$qword = ($array | Select-Object -First 1) -as [UInt64]
if ($null -eq $qword -or $array.Count -ne 1)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'When -Type is set to QWord, -Data must be passed a valid UInt64 value.'
$PSCmdlet.ThrowTerminatingError($errorRecord)
}
else
{
$policyFile.SetQWORDValue($key, $ValueName, $qword)
}
break
}
([Microsoft.Win32.RegistryValueKind]::MultiString)
{
$strings = [string[]] @(
foreach ($item in @($Data))
{
$item.ToString()
}
)
$policyFile.SetMultiStringValue($Key, $ValueName, $strings)
break
}
} # switch ($Type)
$dirty = $true
}
catch
{
throw
}
}
end
{
if ($dirty)
{
$doUpdateGptIni = -not $NoGptIniUpdate
try
{
# SavePolicyFile contains the calls to $PSCmdlet.ShouldProcess, and will inherit our
# WhatIfPreference / ConfirmPreference values from here.
SavePolicyFile -PolicyFile $policyFile -UpdateGptIni:$doUpdateGptIni -ErrorAction Stop
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}
}
}
<#
.SYNOPSIS
Retrieves the current setting(s) from a .pol file.
.DESCRIPTION
Retrieves the current setting(s) from a .pol file.
.PARAMETER Path
Path to the .pol file that is to be read.
.PARAMETER Key
The registry key inside the .pol file that you want to read.
.PARAMETER ValueName
The name of the registry value. May be set to an empty string to read the default value of a key.
.PARAMETER All
Switch indicating that all entries from the specified .pol file should be output, instead of searching for a specific key / ValueName pair.
.EXAMPLE
Get-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -Key Software\Policies\Something -ValueName SomeValue
Reads the value of Software\Policies\Something\SomeValue from the Machine admin templates of the local GPO.
Either returns an object with the data and type of this registry value (if present), or returns nothing, if not found.
.EXAMPLE
Get-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -All
Outputs all of the registry values from the local machine Administrative Templates
.INPUTS
None. This command does not accept pipeline input.
.OUTPUTS
If the specified registry value is found, the function outputs a PSCustomObject with the following properties:
ValueName: The same value that was passed to the -ValueName parameter
Key: The same value that was passed to the -Key parameter
Data: The current value assigned to the specified Key / ValueName in the .pol file.
Type: The RegistryValueKind type of the specified Key / ValueName in the .pol file.
If the specified registry value is not found in the .pol file, the command returns nothing. No error is produced.
.LINK
Set-PolicyFileEntry
.LINK
Remove-PolicyFileEntry
.LINK
Update-GptIniVersion
.LINK
about_RegistryValuesForAdminTemplates
#>
function Get-PolicyFileEntry
{
[CmdletBinding(DefaultParameterSetName = 'ByKeyAndValue')]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string] $Path,
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = 'ByKeyAndValue')]
[string] $Key,
[Parameter(Mandatory = $true, Position = 2, ParameterSetName = 'ByKeyAndValue')]
[string] $ValueName,
[Parameter(Mandatory = $true, ParameterSetName = 'All')]
[switch] $All
)
if (Get-Command [G]et-CallerPreference -CommandType Function -Module PreferenceVariables)
{
Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
}
try
{
$policyFile = OpenPolicyFile -Path $Path -ErrorAction Stop
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
if ($PSCmdlet.ParameterSetName -eq 'ByKeyAndValue')
{
$entry = $policyFile.GetValue($Key, $ValueName)
if ($null -ne $entry)
{
PolEntryToPsObject -PolEntry $entry
}
}
else
{
foreach ($entry in $policyFile.Entries)
{
PolEntryToPsObject -PolEntry $entry
}
}
}
<#
.SYNOPSIS
Removes a value from a .pol file.
.DESCRIPTION
Removes a value from a .pol file. By default, also updates the version number in the policy's gpt.ini file.
.PARAMETER Path
Path to the .pol file that is to be modified.
.PARAMETER Key
The registry key inside the .pol file from which you want to remove a value.
.PARAMETER ValueName
The name of the registry value to be removed. May be set to an empty string to remove the default value of a key.
.PARAMETER NoGptIniUpdate
When this switch is used, the command will not attempt to update the version number in the gpt.ini file
.EXAMPLE
Remove-PolicyFileEntry -Path $env:systemroot\system32\GroupPolicy\Machine\registry.pol -Key Software\Policies\Something -ValueName SomeValue
Removes the value Software\Policies\Something\SomeValue from the local computer Machine GPO, if present. Updates the Machine version counter in $env:systemroot\system32\GroupPolicy\gpt.ini
.EXAMPLE
$entries = @(
New-Object psobject -Property @{ ValueName = 'MaxXResolution'; Data = 1680 }
New-Object psobject -Property @{ ValueName = 'MaxYResolution'; Data = 1050 }
)
$entries | Remove-PolicyFileEntry -Path $env:SystemRoot\system32\GroupPolicy\Machine\registry.pol `
-Key 'SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services'
Example of using pipeline input to remove multiple values at once. The advantage to this approach is that the
.pol file on disk (and the GPT.ini file) will be updated if _any_ of the specified settings had to be removed,
and will be left alone if the file already did not contain any of those values.
The Key property could have also been specified via the pipeline objects instead of on the command line, but
since both values shared the same Key, this example shows that you can pass the value in either way.
.INPUTS
The Key and ValueName properties may be bound via the pipeline by property name.
.OUTPUTS
None. This command does not generate output.
.NOTES
If the specified policy file is already not present in the .pol file, the file will not be modified, and the gpt.ini file will not be updated.
.LINK
Get-PolicyFileEntry
.LINK
Set-PolicyFileEntry
.LINK
Update-GptIniVersion
.LINK
about_RegistryValuesForAdminTemplates
#>
function Remove-PolicyFileEntry
{
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $true, Position = 0)]
[string] $Path,
[Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
[string] $Key,
[Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
[string] $ValueName,
[switch] $NoGptIniUpdate
)
begin
{
if (Get-Command [G]et-CallerPreference -CommandType Function -Module PreferenceVariables)
{
Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
}
$dirty = $false
try
{
$policyFile = OpenPolicyFile -Path $Path -ErrorAction Stop
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}
process
{
$entry = $policyFile.GetValue($Key, $ValueName)
if ($null -eq $entry)
{
Write-Verbose "Entry '$Key\$ValueName' is already not present in file '$Path'."
return
}
Write-Verbose "Removing entry '$Key\$ValueName' from file '$Path'"
$policyFile.DeleteValue($Key, $ValueName)
$dirty = $true
}
end
{
if ($dirty)
{
$doUpdateGptIni = -not $NoGptIniUpdate
try
{
# SavePolicyFile contains the calls to $PSCmdlet.ShouldProcess, and will inherit our
# WhatIfPreference / ConfirmPreference values from here.
SavePolicyFile -PolicyFile $policyFile -UpdateGptIni:$doUpdateGptIni -ErrorAction Stop
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}
}
}
<#
.SYNOPSIS
Increments the version counter in a gpt.ini file.
.DESCRIPTION
Increments the version counter in a gpt.ini file.
.PARAMETER Path
Path to the gpt.ini file that is to be modified.
.PARAMETER PolicyType
Can be set to either 'Machine', 'User', or both. This affects how the value of the Version number in the ini file is changed.
.EXAMPLE
Update-GptIniVersion -Path $env:SystemRoot\system32\GroupPolicy\gpt.ini -PolicyType Machine
Increments the Machine version counter of the local GPO.
.EXAMPLE
Update-GptIniVersion -Path $env:SystemRoot\system32\GroupPolicy\gpt.ini -PolicyType User
Increments the User version counter of the local GPO.
.EXAMPLE
Update-GptIniVersion -Path $env:SystemRoot\system32\GroupPolicy\gpt.ini -PolicyType Machine,User
Increments both the Machine and User version counters of the local GPO.
.INPUTS
None. This command does not accept pipeline input.
.OUTPUTS
None. This command does not generate output.
.NOTES
A gpt.ini file contains only a single Version value. However, this represents two separate counters, for machine and user versions.
The high 16 bits of the value are the User counter, and the low 16 bits are the Machine counter. For example (on PowerShell 3.0
and later), the Version value when the Machine counter is set to 3 and the User counter is set to 5 can be found by evaluating this
expression: (5 -shl 16) -bor 3 , which will show up as decimal value 327683 in the INI file.
.LINK
Get-PolicyFileEntry
.LINK
Set-PolicyFileEntry
.LINK
Remove-PolicyFileEntry
.LINK
about_RegistryValuesForAdminTemplates
#>
function Update-GptIniVersion
{
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({
if (Test-Path -LiteralPath $_ -PathType Leaf)
{
return $true
}
throw "Path '$_' does not exist."
})]
[string] $Path,
[Parameter(Mandatory = $true)]
[ValidateSet('Machine', 'User')]
[string[]] $PolicyType
)
if (Get-Command [G]et-CallerPreference -CommandType Function -Module PreferenceVariables)
{
Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
}
try
{
IncrementGptIniVersion @PSBoundParameters
}
catch
{
$PSCmdlet.ThrowTerminatingError($_)
}
}

704
Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/Common.ps1

@ -1,704 +0,0 @@
#requires -Version 2.0
$script:MachineExtensionGuids = '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]'
$script:UserExtensionGuids = '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]'
function OpenPolicyFile
{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $Path
)
$policyFile = New-Object TJX.PolFileEditor.PolFile
$policyFile.FileName = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
if (Test-Path -LiteralPath $policyFile.FileName)
{
try
{
$policyFile.LoadFile()
}
catch [TJX.PolFileEditor.FileFormatException]
{
$message = "File '$Path' is not a valid POL file."
$exception = New-Object System.Exception($message)
$errorRecord = New-Object System.Management.Automation.ErrorRecord(
$exception, 'InvalidPolFileContents', [System.Management.Automation.ErrorCategory]::InvalidData, $Path
)
throw $errorRecord
}
catch
{
$errorRecord = $_
$message = "Error loading policy file at path '$Path': $($errorRecord.Exception.Message)"
$exception = New-Object System.Exception($message, $errorRecord.Exception)
$newErrorRecord = New-Object System.Management.Automation.ErrorRecord(
$exception, 'FailedToOpenPolicyFile', [System.Management.Automation.ErrorCategory]::OperationStopped, $Path
)
throw $newErrorRecord
}
}
return $policyFile
}
function PolEntryToPsObject
{
param (
[TJX.PolFileEditor.PolEntry] $PolEntry
)
$type = PolEntryTypeToRegistryValueKind $PolEntry.Type
$data = GetEntryData -Entry $PolEntry -Type $type
return New-Object psobject -Property @{
Key = $PolEntry.KeyName
ValueName = $PolEntry.ValueName
Type = $type
Data = $data
}
}
function GetEntryData
{
param (
[TJX.PolFileEditor.PolEntry] $Entry,
[Microsoft.Win32.RegistryValueKind] $Type
)
switch ($type)
{
([Microsoft.Win32.RegistryValueKind]::Binary)
{
return $Entry.BinaryValue
}
([Microsoft.Win32.RegistryValueKind]::DWord)
{
return $Entry.DWORDValue
}
([Microsoft.Win32.RegistryValueKind]::ExpandString)
{
return $Entry.StringValue
}
([Microsoft.Win32.RegistryValueKind]::MultiString)
{
return $Entry.MultiStringValue
}
([Microsoft.Win32.RegistryValueKind]::QWord)
{
return $Entry.QWORDValue
}
([Microsoft.Win32.RegistryValueKind]::String)
{
return $Entry.StringValue
}
}
}
function SavePolicyFile
{
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(Mandatory = $true)]
[TJX.PolFileEditor.PolFile] $PolicyFile,
[switch] $UpdateGptIni
)
if ($PSCmdlet.ShouldProcess($PolicyFile.FileName, 'Save new settings'))
{
$parentPath = Split-Path $PolicyFile.FileName -Parent
if (-not (Test-Path -LiteralPath $parentPath -PathType Container))
{
try
{
$null = New-Item -Path $parentPath -ItemType Directory -ErrorAction Stop -Confirm:$false -WhatIf:$false
}
catch
{
$errorRecord = $_
$message = "Error creating parent folder of path '$Path': $($errorRecord.Exception.Message)"
$exception = New-Object System.Exception($message, $errorRecord.Exception)
$newErrorRecord = New-Object System.Management.Automation.ErrorRecord(
$exception, 'CreateParentFolderError', $errorRecord.CategoryInfo.Category, $Path
)
throw $newErrorRecord
}
}
try
{
$PolicyFile.SaveFile()
}
catch
{
$errorRecord = $_
$message = "Error saving policy file to path '$($PolicyFile.FileName)': $($errorRecord.Exception.Message)"
$exception = New-Object System.Exception($message, $errorRecord.Exception)
$newErrorRecord = New-Object System.Management.Automation.ErrorRecord(
$exception, 'FailedToSavePolicyFile', [System.Management.Automation.ErrorCategory]::OperationStopped, $PolicyFile
)
throw $newErrorRecord
}
}
if ($UpdateGptIni)
{
if ($policyFile.FileName -match '^(.*)\\+([^\\]+)\\+[^\\]+$' -and
$Matches[2] -eq 'User' -or $Matches[2] -eq 'Machine')
{
$iniPath = Join-Path $Matches[1] GPT.ini
if (Test-Path -LiteralPath $iniPath -PathType Leaf)
{
if ($PSCmdlet.ShouldProcess($iniPath, 'Increment version number in INI file'))
{
IncrementGptIniVersion -Path $iniPath -PolicyType $Matches[2] -Confirm:$false -WhatIf:$false
}
}
else
{
if ($PSCmdlet.ShouldProcess($iniPath, 'Create new gpt.ini file'))
{
NewGptIni -Path $iniPath -PolicyType $Matches[2]
}
}
}
}
}
function NewGptIni
{
param (
[string] $Path,
[string[]] $PolicyType
)
$parent = Split-Path $Path -Parent
if (-not (Test-Path $parent -PathType Container))
{
$null = New-Item -Path $parent -ItemType Directory -ErrorAction Stop
}
$version = GetNewVersionNumber -Version 0 -PolicyType $PolicyType
Set-Content -Path $Path -Encoding Ascii -Value @"
[General]
gPCMachineExtensionNames=$script:MachineExtensionGuids
Version=$version
gPCUserExtensionNames=$script:UserExtensionGuids
"@
}
function IncrementGptIniVersion
{
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[string] $Path,
[string[]] $PolicyType
)
$foundVersionLine = $false
$section = ''
$newContents = @(
foreach ($line in Get-Content $Path)
{
# This might not be the most unreadable regex ever, but it's trying hard to be!
# It's looking for section lines: [SectionName]
if ($line -match '^\s*\[([^\]]+)\]\s*$')
{
if ($section -eq 'General')
{
if (-not $foundVersionLine)
{
$foundVersionLine = $true
$newVersion = GetNewVersionNumber -Version 0 -PolicyType $PolicyType
"Version=$newVersion"
}
if (-not $foundMachineExtensionLine)
{
$foundMachineExtensionLine = $true
"gPCMachineExtensionNames=$script:MachineExtensionGuids"
}
if (-not $foundUserExtensionLine)
{
$foundUserExtensionLine = $true
"gPCUserExtensionNames=$script:UserExtensionGuids"
}
}
$section = $matches[1]
}
elseif ($section -eq 'General' -and
$line -match '^\s*Version\s*=\s*(\d+)\s*$' -and
$null -ne ($version = $matches[1] -as [uint32]))
{
$foundVersionLine = $true
$newVersion = GetNewVersionNumber -Version $version -PolicyType $PolicyType
$line = "Version=$newVersion"
}
elseif ($section -eq 'General' -and $line -match '^\s*gPC(Machine|User)ExtensionNames\s*=')
{
if ($matches[1] -eq 'Machine')
{
$foundMachineExtensionLine = $true
}
else
{
$foundUserExtensionLine = $true
}
$line = EnsureAdminTemplateCseGuidsArePresent $line
}
$line
}
if ($section -eq 'General')
{
if (-not $foundVersionLine)
{
$foundVersionLine = $true
$newVersion = GetNewVersionNumber -Version 0 -PolicyType $PolicyType
"Version=$newVersion"
}
if (-not $foundMachineExtensionLine)
{
$foundMachineExtensionLine = $true
"gPCMachineExtensionNames=$script:MachineExtensionGuids"
}
if (-not $foundUserExtensionLine)
{
$foundUserExtensionLine = $true
"gPCUserExtensionNames=$script:MachineExtensionGuids"
}
}
)
if ($PSCmdlet.ShouldProcess($Path, 'Increment Version number'))
{
Set-Content -Path $Path -Value $newContents -Encoding Ascii -Confirm:$false -WhatIf:$false
}
}
function EnsureAdminTemplateCseGuidsArePresent
{
param ([string] $Line)
# These lines contain pairs of GUIDs in "registry" format (with the curly braces), separated by nothing, with
# each pair of GUIDs wrapped in square brackets. Example:
# gPCMachineExtensionNames=[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]
# Per Darren Mar-Elia, these GUIDs must be in alphabetical order, or GP processing will have problems.
if ($Line -notmatch '\s*(gPC(?:Machine|User)ExtensionNames)\s*=\s*(.*)$')
{
throw "Malformed gpt.ini line: $Line"
}
$valueName = $matches[1]
$guidStrings = @($matches[2] -split '(?<=\])(?=\[)')
if ($matches[1] -eq 'gPCMachineExtensionNames')
{
$toolExtensionGuid = $script:MachineExtensionGuids
}
else
{
$toolExtensionGuid = $script:UserExtensionGuids
}
$guidList = @(
$guidStrings
$toolExtensionGuid
)
$newGuidString = ($guidList | Sort-Object -Unique) -join ''
return "$valueName=$newGuidString"
}
function GetNewVersionNumber
{
param (
[UInt32] $Version,
[string[]] $PolicyType
)
# User version is the high 16 bits, Machine version is the low 16 bits.
# Reference: http://blogs.technet.com/b/grouppolicy/archive/2007/12/14/understanding-the-gpo-version-number.aspx
$pair = UInt32ToUInt16Pair -UInt32 $version
if ($PolicyType -contains 'User')
{
$pair.HighPart++
}
if ($PolicyType -contains 'Machine')
{
$pair.LowPart++
}
return UInt16PairToUInt32 -UInt16Pair $pair
}
function UInt32ToUInt16Pair
{
param ([UInt32] $UInt32)
# Deliberately avoiding bitwise shift operators here, for PowerShell v2 compatibility.
$lowPart = $UInt32 -band 0xFFFF
$highPart = ($UInt32 - $lowPart) / 0x10000
return New-Object psobject -Property @{
LowPart = [UInt16] $lowPart
HighPart = [UInt16] $highPart
}
}
function UInt16PairToUInt32
{
param ([object] $UInt16Pair)
# Deliberately avoiding bitwise shift operators here, for PowerShell v2 compatibility.
return ([UInt32] $UInt16Pair.HighPart) * 0x10000 + $UInt16Pair.LowPart
}
function PolEntryTypeToRegistryValueKind
{
param ([TJX.PolFileEditor.PolEntryType] $PolEntryType)
switch ($PolEntryType)
{
([TJX.PolFileEditor.PolEntryType]::REG_NONE)
{
return [Microsoft.Win32.RegistryValueKind]::None
}
([TJX.PolFileEditor.PolEntryType]::REG_DWORD)
{
return [Microsoft.Win32.RegistryValueKind]::DWord
}
([TJX.PolFileEditor.PolEntryType]::REG_DWORD_BIG_ENDIAN)
{
return [Microsoft.Win32.RegistryValueKind]::DWord
}
([TJX.PolFileEditor.PolEntryType]::REG_BINARY)
{
return [Microsoft.Win32.RegistryValueKind]::Binary
}
([TJX.PolFileEditor.PolEntryType]::REG_EXPAND_SZ)
{
return [Microsoft.Win32.RegistryValueKind]::ExpandString
}
([TJX.PolFileEditor.PolEntryType]::REG_MULTI_SZ)
{
return [Microsoft.Win32.RegistryValueKind]::MultiString
}
([TJX.PolFileEditor.PolEntryType]::REG_QWORD)
{
return [Microsoft.Win32.RegistryValueKind]::QWord
}
([TJX.PolFileEditor.PolEntryType]::REG_SZ)
{
return [Microsoft.Win32.RegistryValueKind]::String
}
}
}
function GetPolFilePath
{
param (
[Parameter(Mandatory = $true, ParameterSetName = 'PolicyType')]
[string] $PolicyType,
[Parameter(Mandatory = $true, ParameterSetName = 'Account')]
[string] $Account
)
if ($PolicyType)
{
switch ($PolicyType)
{
'Machine'
{
return Join-Path $env:SystemRoot System32\GroupPolicy\Machine\registry.pol
}
'User'
{
return Join-Path $env:SystemRoot System32\GroupPolicy\User\registry.pol
}
'Administrators'
{
# BUILTIN\Administrators well-known SID
return Join-Path $env:SystemRoot System32\GroupPolicyUsers\S-1-5-32-544\User\registry.pol
}
'NonAdministrators'
{
# BUILTIN\Users well-known SID
return Join-Path $env:SystemRoot System32\GroupPolicyUsers\S-1-5-32-545\User\registry.pol
}
}
}
else
{
try
{
$sid = $Account -as [System.Security.Principal.SecurityIdentifier]
if ($null -eq $sid)
{
$sid = GetSidForAccount $Account
}
return Join-Path $env:SystemRoot "System32\GroupPolicyUsers\$($sid.Value)\User\registry.pol"
}
catch
{
throw
}
}
}
function GetSidForAccount($Account)
{
$acc = $Account
if ($acc -notlike '*\*') { $acc = "$env:COMPUTERNAME\$acc" }
try
{
$ntAccount = [System.Security.Principal.NTAccount]$acc
return $ntAccount.Translate([System.Security.Principal.SecurityIdentifier])
}
catch
{
$message = "Could not translate account '$acc' to a security identifier."
$exception = New-Object System.Exception($message, $_.Exception)
$errorRecord = New-Object System.Management.Automation.ErrorRecord(
$exception,
'CouldNotGetSidForAccount',
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
$Acc
)
throw $errorRecord
}
}
function DataIsEqual
{
param (
[object] $First,
[object] $Second,
[Microsoft.Win32.RegistryValueKind] $Type
)
if ($Type -eq [Microsoft.Win32.RegistryValueKind]::String -or
$Type -eq [Microsoft.Win32.RegistryValueKind]::ExpandString -or
$Type -eq [Microsoft.Win32.RegistryValueKind]::DWord -or
$Type -eq [Microsoft.Win32.RegistryValueKind]::QWord)
{
return @($First)[0] -ceq @($Second)[0]
}
# If we get here, $Type is either MultiString or Binary, both of which need to compare arrays.
# The PolicyFileEditor module never returns type Unknown or None.
$First = @($First)
$Second = @($Second)
if ($First.Count -ne $Second.Count) { return $false }
$count = $First.Count
for ($i = 0; $i -lt $count; $i++)
{
if ($First[$i] -cne $Second[$i]) { return $false }
}
return $true
}
function ParseKeyValueName
{
param ([string] $KeyValueName)
$key = $KeyValueName -replace '^\\+|\\+$'
$valueName = ''
if ($KeyValueName -match '^\\*(?<Key>.+?)\\+(?<ValueName>[^\\]*)$')
{
$key = $matches['Key'] -replace '\\{2,}', '\'
$valueName = $matches['ValueName']
}
return $key, $valueName
}
function GetTargetResourceCommon
{
param (
[string] $Path,
[string] $KeyValueName
)
$configuration = @{
KeyValueName = $KeyValueName
Ensure = 'Absent'
Data = $null
Type = [Microsoft.Win32.RegistryValueKind]::Unknown
}
if (Test-Path -LiteralPath $path -PathType Leaf)
{
$key, $valueName = ParseKeyValueName $KeyValueName
$entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName
if ($entry)
{
$configuration['Ensure'] = 'Present'
$configuration['Type'] = $entry.Type
$configuration['Data'] = @($entry.Data)
}
}
return $configuration
}
function SetTargetResourceCommon
{
param (
[string] $Path,
[string] $KeyValueName,
[string] $Ensure,
[string[]] $Data,
[Microsoft.Win32.RegistryValueKind] $Type
)
if ($null -eq $Data) { $Data = @() }
try
{
Assert-ValidDataAndType -Data $Data -Type $Type
}
catch
{
Write-Error -ErrorRecord $_
return
}
$key, $valueName = ParseKeyValueName $KeyValueName
if ($Ensure -eq 'Present')
{
Set-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName -Data $Data -Type $Type
}
else
{
Remove-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName
}
}
function TestTargetResourceCommon
{
[OutputType([bool])]
param (
[string] $Path,
[string] $KeyValueName,
[string] $Ensure,
[string[]] $Data,
[Microsoft.Win32.RegistryValueKind] $Type
)
if ($null -eq $Data) { $Data = @() }
try
{
Assert-ValidDataAndType -Data $Data -Type $Type
}
catch
{
Write-Error -ErrorRecord $_
return $false
}
$key, $valueName = ParseKeyValueName $KeyValueName
$fileExists = Test-Path -LiteralPath $Path -PathType Leaf
if ($Ensure -eq 'Present')
{
if (-not $fileExists) { return $false }
$entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName
return $null -ne $entry -and $Type -eq $entry.Type -and (DataIsEqual $entry.Data $Data -Type $Type)
}
else # Ensure is 'Absent'
{
if (-not $fileExists) { return $true }
$entry = Get-PolicyFileEntry -Path $Path -Key $key -ValueName $valueName
return $null -eq $entry
}
}
function Assert-ValidDataAndType
{
param (
[string[]] $Data,
[Microsoft.Win32.RegistryValueKind] $Type
)
if ($Type -ne [Microsoft.Win32.RegistryValueKind]::MultiString -and
$Type -ne [Microsoft.Win32.RegistryValueKind]::Binary -and
$Data.Count -gt 1)
{
$errorRecord = InvalidDataTypeCombinationErrorRecord -Message 'Do not pass arrays with multiple values to the -Data parameter when -Type is not set to either Binary or MultiString.'
throw $errorRecord
}
}
function InvalidDataTypeCombinationErrorRecord($Message)
{
$exception = New-Object System.Exception($Message)
return New-Object System.Management.Automation.ErrorRecord(
$exception, 'InvalidDataTypeCombination', [System.Management.Automation.ErrorCategory]::InvalidArgument, $null
)
}

BIN
Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolFileEditor.dll

Binary file not shown.

43
Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolicyFileEditor.psd1

@ -1,43 +0,0 @@
@{
ModuleToProcess = 'PolicyFileEditor.psm1'
ModuleVersion = '3.0.1'
GUID = '110a2398-3053-4ffc-89d1-1b6a38a2dc86'
Author = 'Dave Wyatt'
CompanyName = 'Home'
Copyright = '(c) 2015 Dave Wyatt. All rights reserved.'
Description = 'Commands and DSC resource for modifying Administrative Templates settings in local GPO registry.pol files.'
PowerShellVersion = '2.0'
# PowerShellHostName = ''
# PowerShellHostVersion = ''
DotNetFrameworkVersion = '2.0'
# CLRVersion = ''
# ProcessorArchitecture = ''
# RequiredModules = @()
# RequiredAssemblies = @()
# ScriptsToProcess = @()
# TypesToProcess = @()
# FormatsToProcess = @()
# NestedModules = @()
FunctionsToExport = @('Set-PolicyFileEntry', 'Remove-PolicyFileEntry', 'Get-PolicyFileEntry', 'Update-GptIniVersion')
# CmdletsToExport = '*'
# VariablesToExport = '*'
# AliasesToExport = '*'
# DscResourcesToExport = @()
# ModuleList = @()
# FileList = @()
# HelpInfoURI = ''
# DefaultCommandPrefix = ''
PrivateData = @{
PSData = @{
# Tags = @()
LicenseUri = 'https://www.apache.org/licenses/LICENSE-2.0.html'
ProjectUri = 'https://github.com/dlwyatt/PolicyFileEditor'
# IconUri = ''
ReleaseNotes = 'Updated resource schemas to be more friendly with Invoke-DscResource.'
}
}
}

10
Sophia Script/Sophia Script for Windows 11/bin/PolicyFileEditor/PolicyFileEditor.psm1

@ -1,10 +0,0 @@
#requires -Version 2.0
$scriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
$dllPath = Join-Path $scriptRoot PolFileEditor.dll
Add-Type -Path $dllPath -ErrorAction Stop
$commandsFile = Join-Path $scriptRoot Commands.ps1
. $commandsFile
Loading…
Cancel
Save