Commands.ps1
#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($_) } } # SIG # Begin signature block # MIIgTAYJKoZIhvcNAQcCoIIgPTCCIDkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAWcBLp5Go0Yq2e # WARWCHEIvOxdE5WCmcYIoSD6N4yxq6CCG1YwggO3MIICn6ADAgECAhAM5+DlF9hG # /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa # Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD # ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC # AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8 # tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf # 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1 # lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi # uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz # vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA # A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS # TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf # 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv # hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+ # S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD # +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFJDCCBAygAwIBAgIQCRrHC94asIH2W7lu # /i1eITANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln # aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhE # aWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE3MDYy # MTAwMDAwMFoXDTE4MDgyOTEyMDAwMFowYTELMAkGA1UEBhMCQ0ExCzAJBgNVBAgT # Ak9OMREwDwYDVQQHEwhCcmFtcHRvbjEYMBYGA1UEChMPRGF2aWQgTGVlIFd5YXR0 # MRgwFgYDVQQDEw9EYXZpZCBMZWUgV3lhdHQwggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDA/6cBpCOYJk6IY2cPgY23dVjs3xYcJKeSDIvnfvBl/SFqvEYX # tJkfbETnjp2XkZ9UFk2j5b5JpRg25GpkTo4a0MYMAUyn7tfotxG64sjxsNLXrrYN # nx3q2QUt4dpRjG11giMyFSjAFPjPO1JbM9976GN96ldiOUX3yH+UM4Ow6zS+0iGq # MSnd88iPs/CqphaVTDBN+ZmU864hnzGhjiMBlyjw4z/aLtJeopeLmuk8wyxN+N3v # P/wsFwmPpycxSU3hPlqa2GDDswYtiMuf5NOdG3CfEEX9Ntrd2NxpjXOPGDt6Ko0C # 0mYSd9qgzznxuweJPXXg4GR6jeVZrq4pQZFlAgMBAAGjggHFMIIBwTAfBgNVHSME # GDAWgBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUDkf1juW1Z6Tp40ck # YDz0SxGZDn8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcG # A1UdHwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFz # c3VyZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v # c2hhMi1hc3N1cmVkLWNzLWcxLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwDATAq # MCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeB # DAEEATCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k # aWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmluZ0NBLmNydDAMBgNV # HRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAqho5skBHIeG738khh3muTynOW # TWwHZ4fJI0oWgAVLK/g1VNHKCWY+fqoxdXZcRZt6jZtklmuYAPx3q86XtOniVqYk # EFIcJgBFJtBuAQDeYFyKyMWoidqGVQ4wIrzArUEWg6q/ba36S+u1TqnZr3GJ3+y0 # G9iNbc1nEzKoUPARo3rgWEirgARMjNCPSufDLHNsjPHCXwUNxbRtq+kiUxBgOIF7 # Rn4ADci9pCalNeby/V7I2poj5nQ22f2P7nwIMuIFszOsV7BBfI2ni9GzPhKBCwLy # K6s3wzPQyuUewKiG84dQcnOIZO9f/8aB30BVu6FSnNvMdMjyLIRtlPP0txxvMIIF # MDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0BAQsFADBlMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg # Q0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQswCQYDVQQGEwJV # UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu # Y29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWdu # aW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9n # dwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ1DcZ17aq8JyG # pdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS # 8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6scKKrzn/pfMuS # oeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEg # JSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg0A9kczyen6Yz # qf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B # Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUHAQEEbTBrMCQG # CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG # N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv # b3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9j # cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYD # VR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3 # LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl # 6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0G # CSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg # /cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5jDhNLrddfRHnz # NhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKc # FEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIysjaKJAL+L3J+ # HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6 # i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIGajCCBVKgAwIBAgIQ # AwGaAjr/WLFr1tXq5hfwZjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzEV # MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t # MSEwHwYDVQQDExhEaWdpQ2VydCBBc3N1cmVkIElEIENBLTEwHhcNMTQxMDIyMDAw # MDAwWhcNMjQxMDIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzERMA8GA1UEChMIRGln # aUNlcnQxJTAjBgNVBAMTHERpZ2lDZXJ0IFRpbWVzdGFtcCBSZXNwb25kZXIwggEi # MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjZF38fLPggjXg4PbGKuZJdTvM # buBTqZ8fZFnmfGt/a4ydVfiS457VWmNbAklQ2YPOb2bu3cuF6V+l+dSHdIhEOxnJ # 5fWRn8YUOawk6qhLLJGJzF4o9GS2ULf1ErNzlgpno75hn67z/RJ4dQ6mWxT9RSOO # hkRVfRiGBYxVh3lIRvfKDo2n3k5f4qi2LVkCYYhhchhoubh87ubnNC8xd4EwH7s2 # AY3vJ+P3mvBMMWSN4+v6GYeofs/sjAw2W3rBerh4x8kGLkYQyI3oBGDbvHN0+k7Y # /qpA8bLOcEaD6dpAoVk62RUJV5lWMJPzyWHM0AjMa+xiQpGsAsDvpPCJEY93AgMB # AAGjggM1MIIDMTAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUB # Af8EDDAKBggrBgEFBQcDCDCCAb8GA1UdIASCAbYwggGyMIIBoQYJYIZIAYb9bAcB # MIIBkjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzCC # AWQGCCsGAQUFBwICMIIBVh6CAVIAQQBuAHkAIAB1AHMAZQAgAG8AZgAgAHQAaABp # AHMAIABDAGUAcgB0AGkAZgBpAGMAYQB0AGUAIABjAG8AbgBzAHQAaQB0AHUAdABl # AHMAIABhAGMAYwBlAHAAdABhAG4AYwBlACAAbwBmACAAdABoAGUAIABEAGkAZwBp # AEMAZQByAHQAIABDAFAALwBDAFAAUwAgAGEAbgBkACAAdABoAGUAIABSAGUAbAB5 # AGkAbgBnACAAUABhAHIAdAB5ACAAQQBnAHIAZQBlAG0AZQBuAHQAIAB3AGgAaQBj # AGgAIABsAGkAbQBpAHQAIABsAGkAYQBiAGkAbABpAHQAeQAgAGEAbgBkACAAYQBy # AGUAIABpAG4AYwBvAHIAcABvAHIAYQB0AGUAZAAgAGgAZQByAGUAaQBuACAAYgB5 # ACAAcgBlAGYAZQByAGUAbgBjAGUALjALBglghkgBhv1sAxUwHwYDVR0jBBgwFoAU # FQASKxOYspkH7R7for5XDStnAs0wHQYDVR0OBBYEFGFaTSS2STKdSip5GoNL9B6J # wcp9MH0GA1UdHwR2MHQwOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMDigNqA0hjJodHRwOi8vY3JsNC5kaWdp # Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNybDB3BggrBgEFBQcBAQRr # MGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEF # BQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl # ZElEQ0EtMS5jcnQwDQYJKoZIhvcNAQEFBQADggEBAJ0lfhszTbImgVybhs4jIA+A # h+WI//+x1GosMe06FxlxF82pG7xaFjkAneNshORaQPveBgGMN/qbsZ0kfv4gpFet # W7easGAm6mlXIV00Lx9xsIOUGQVrNZAQoHuXx/Y/5+IRQaa9YtnwJz04HShvOlIJ # 8OxwYtNiS7Dgc6aSwNOOMdgv420XEwbu5AO2FKvzj0OncZ0h3RTKFV2SQdr5D4HR # mXQNJsQOfxu19aDxxncGKBXp2JPlVRbwuwqrHNtcSCdmyKOLChzlldquxC5ZoGHd # 2vNtomHpigtt7BIYvfdVVEADkitrwlHCCkivsNRu4PQUCjob4489yq9qjXvc2EQw # ggbNMIIFtaADAgECAhAG/fkDlgOt6gAK6z8nu7obMA0GCSqGSIb3DQEBBQUAMGUx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9v # dCBDQTAeFw0wNjExMTAwMDAwMDBaFw0yMTExMTAwMDAwMDBaMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTCCASIwDQYJ # KoZIhvcNAQEBBQADggEPADCCAQoCggEBAOiCLZn5ysJClaWAc0Bw0p5WVFypxNJB # Bo/JM/xNRZFcgZ/tLJz4FlnfnrUkFcKYubR3SdyJxArar8tea+2tsHEx6886QAxG # TZPsi3o2CAOrDDT+GEmC/sfHMUiAfB6iD5IOUMnGh+s2P9gww/+m9/uizW9zI/6s # VgWQ8DIhFonGcIj5BZd9o8dD3QLoOz3tsUGj7T++25VIxO4es/K8DCuZ0MZdEkKB # 4YNugnM/JksUkK5ZZgrEjb7SzgaurYRvSISbT0C58Uzyr5j79s5AXVz2qPEvr+yJ # IvJrGGWxwXOt1/HYzx4KdFxCuGh+t9V3CidWfA9ipD8yFGCV/QcEogkCAwEAAaOC # A3owggN2MA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAyBggrBgEFBQcDAQYIKwYB # BQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUHAwgwggHSBgNVHSAEggHJ # MIIBxTCCAbQGCmCGSAGG/WwAAQQwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 # LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH # AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy # AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj # AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg # AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ # AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt # AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj # AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl # AHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTASBgNVHRMBAf8ECDAGAQH/AgEAMHkG # CCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu # Y29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln # aUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRw # Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3Js # MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk # SURSb290Q0EuY3JsMB0GA1UdDgQWBBQVABIrE5iymQftHt+ivlcNK2cCzTAfBgNV # HSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEA # RlA+ybcoJKc4HbZbKa9Sz1LpMUerVlx71Q0LQbPv7HUfdDjyslxhopyVw1Dkgrkj # 0bo6hnKtOHisdV0XFzRyR4WUVtHruzaEd8wkpfMEGVWp5+Pnq2LN+4stkMLA0rWU # vV5PsQXSDj0aqRRbpoYxYqioM+SbOafE9c4deHaUJXPkKqvPnHZL7V/CSxbkS3BM # AIke/MV5vEwSV/5f4R68Al2o/vsHOE8Nxl2RuQ9nRc3Wg+3nkg2NsWmMT/tZ4CMP # 0qquAHzunEIOz5HXJ7cW7g/DvXwKoO4sCFWFIrjrGBpN/CohrUkxg0eVd3HcsRtL # SxwQnHcUwZ1PL1qVCCkQJjGCBEwwggRIAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUw # EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x # MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcg # Q0ECEAkaxwveGrCB9lu5bv4tXiEwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGC # NwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgeDlioT2f # GHcGNTfuRsD0IKaxlXd1hWdfaxBVWx50HuMwDQYJKoZIhvcNAQEBBQAEggEAUPAK # q7M3j9NMUPZulEA4Xw5ihJ034qItL2ZaAwGQ6ITLEXwqQp4o9sI5RJ4or9zCwKuI # CSz9OUPEnYj2hIVVJY7Qd4+4J4u5thFJzWqj4nDJV/IVSpR7j+QWc+M4774mNQv3 # kjTaHWaI4ZGSN7QGXSqJiagS8cnq7s8puJu0eS2tvCjHPbAZ01pgMWckCqXpnKhw # +cuSLQ69CFhAp9xL5oOWva0qrW/aL829MGfrmt3HtIOonLIip+p7E1pG/3L8N8oq # sWG9LmyXR147VVev4pa/pzI+gjEG4yLNaSiC4IeF9uuyJ+DI7d0fVeJVQUROkzx1 # ertJoAoGC4g9/s2ATaGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0Et # MQIQAwGaAjr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsG # CSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwMjEyMjExOTQzWjAjBgkqhkiG # 9w0BCQQxFgQUHWtK+HMhOICqm+NK2ov+B+/Ica8wDQYJKoZIhvcNAQEBBQAEggEA # lkoWbQS5N7Oi6fFD39t6V/wGuLkTj1a6MK4lLTcGu4KUTQeSGQpkWn8oeL2JdvgX # 23jgLe9D7kn6W7GP5xgYgwFF3LN+8sgt5mzjOYBkGoUkGzwRSJcHsxIaT5P8B8pC # ttYX/gD86GOT5OxT+DybiY6g6ejYy+a7qoAlawm9Vmth/Nx4MC85FFxgOFs6YJXy # IShe2CmmIZxUmlgWRoxpx19wCOgY0O34CgnkNVSuVPDJMjdmxdlr3rhHDwwyp7C4 # n0DTzclyQs/g3DqnMGyj1/fgnHk+p4kqpJAHaWFom/V1vxK4KgWl7fT0OibEQBoQ # q6k346yfNir+/qcW3OacgA== # SIG # End signature block |