Private/Deploy-Accelerator-Helpers/Request-ALZConfigurationValue.ps1
|
function Request-ALZConfigurationValue { <# .SYNOPSIS Parses configuration files and prompts the user for input values interactively. .DESCRIPTION This function reads the inputs.yaml file, loads the schema for descriptions and help links, and prompts the user for values. It prompts for all inputs in inputs.yaml. .PARAMETER ConfigFolderPath The path to the folder containing the configuration files. .PARAMETER IacType The Infrastructure as Code type (terraform or bicep). .PARAMETER VersionControl The version control system (github, azure-devops, or local). .PARAMETER AzureContextOutputDirectory The output directory to pass to Get-AzureContext for caching Azure context data. .PARAMETER AzureContextClearCache When set, clears the cached Azure context data before fetching. .PARAMETER SensitiveOnly When set, only prompts for sensitive inputs that are not already set (via environment variables or non-empty config values). .OUTPUTS Returns $true if configuration was updated, $false otherwise. #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true)] [string] $ConfigFolderPath, [Parameter(Mandatory = $true)] [string] $IacType, [Parameter(Mandatory = $true)] [string] $VersionControl, [Parameter(Mandatory = $false)] [string] $AzureContextOutputDirectory = "", [Parameter(Mandatory = $false)] [switch] $AzureContextClearCache, [Parameter(Mandatory = $false)] [switch] $SensitiveOnly ) # Helper function to get a property from schema info safely function Get-SchemaProperty { param($SchemaInfo, $PropertyName, $Default = $null) if ($null -ne $SchemaInfo -and $SchemaInfo.PSObject.Properties.Name -contains $PropertyName) { return $SchemaInfo.$PropertyName } return $Default } # Helper function to prompt for a single input value function Read-InputValue { param( $Key, $CurrentValue, $SchemaInfo, $Indent = "", $DefaultDescription = "No description available", $AzureContext ) # Use pre-fetched Azure context data from parent scope $description = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "description" -Default $DefaultDescription $helpLink = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "helpLink" $isSensitive = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "sensitive" -Default $false $schemaType = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "type" -Default "string" $isRequired = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "required" -Default $false $source = Get-SchemaProperty -SchemaInfo $SchemaInfo -PropertyName "source" # For sensitive inputs, check if value is set via environment variable $envVarValue = $null if ($isSensitive) { $envVarName = "TF_VAR_$Key" $envVarValue = [System.Environment]::GetEnvironmentVariable($envVarName) if (-not [string]::IsNullOrWhiteSpace($envVarValue)) { $CurrentValue = $envVarValue } } # Check if the current value is an array $isArray = $schemaType -eq "array" -or $CurrentValue -is [System.Collections.IList] # Check if the current value is a placeholder (surrounded by angle brackets) $isPlaceholder = $false $hasPlaceholderItems = $false if ($isArray) { # Check if array contains placeholder items if ($null -ne $CurrentValue -and $CurrentValue.Count -gt 0) { foreach ($item in $CurrentValue) { if ($item -is [string] -and $item -match '^\s*<.*>\s*$') { $hasPlaceholderItems = $true break } } } } elseif ($CurrentValue -is [string] -and $CurrentValue -match '^\s*<.*>\s*$') { $isPlaceholder = $true } # Determine effective default (don't use placeholders as defaults) $effectiveDefault = if ($isPlaceholder) { "" } elseif ($isArray -and $hasPlaceholderItems) { @() } else { $CurrentValue } # Build base parameters for Read-MenuSelection $menuParams = @{ Title = $Key HelpText = @($description, $helpLink) Options = @() DefaultValue = $effectiveDefault AllowManualEntry = $true ManualEntryPrompt = "Enter value (press enter to accept default)" Type = $schemaType IsRequired = $isRequired RequiredMessage = "This field is required. Please enter a value." IsSensitive = $isSensitive } # Customize parameters based on input type if ($isArray) { $menuParams.HelpText = @($description, $helpLink, "Format: Comma-separated list of values") $menuParams.ManualEntryPrompt = "Enter values (comma-separated)" $menuParams.RequiredMessage = "This field is required. Please enter values." } elseif ($source -eq "subscription") { $menuParams.OptionsTitle = "Available subscriptions:" $menuParams.Options = $AzureContext.Subscriptions $menuParams.ManualEntryPrompt = "Enter subscription ID" $menuParams.RequiredMessage = "This field is required. Please select a subscription." $menuParams.EmptyMessage = "No subscriptions found in Azure context." } elseif ($source -eq "managementGroup") { $menuParams.OptionsTitle = "Available management groups:" $menuParams.Options = $AzureContext.ManagementGroups $menuParams.ManualEntryPrompt = "Enter management group ID" $menuParams.RequiredMessage = "This field is required. Please select a management group." $menuParams.EmptyMessage = "No management groups found in Azure context." } elseif ($source -eq "azureRegion") { $menuParams.OptionsTitle = "Available regions (AZ = Availability Zone support):" $menuParams.Options = $AzureContext.Regions $menuParams.ManualEntryPrompt = "Enter region name (e.g., uksouth, eastus)" $menuParams.RequiredMessage = "This field is required. Please select a region." $menuParams.EmptyMessage = "No regions found in Azure context." } elseif ($schemaType -eq "boolean") { $menuParams.ManualEntryPrompt = "Enter value (true/false) (press enter to accept default)" $menuParams.DefaultValue = $effectiveDefault.ToString().ToLower() } $newValue = Read-MenuSelection @menuParams # Return value along with sensitivity info return @{ Value = $newValue IsSensitive = $isSensitive } } if ($PSCmdlet.ShouldProcess("Configuration files", "prompt for input values")) { $AzureContext = $null # Fetch Azure context once upfront if not in SensitiveOnly mode if (-not $SensitiveOnly.IsPresent) { if (-not [string]::IsNullOrWhiteSpace($AzureContextOutputDirectory)) { $AzureContext = Get-AzureContext -OutputDirectory $AzureContextOutputDirectory -ClearCache:$AzureContextClearCache.IsPresent } else { $AzureContext = Get-AzureContext -ClearCache:$AzureContextClearCache.IsPresent } } Write-Verbose (ConvertTo-Json $AzureContext) # Load the schema file $schemaPath = Join-Path $PSScriptRoot "AcceleratorInputSchema.json" if (-not (Test-Path $schemaPath)) { Write-Warning "Schema file not found at $schemaPath. Proceeding without descriptions." $schema = $null } else { $schema = Get-Content -Path $schemaPath -Raw | ConvertFrom-Json } # Define the configuration files to process $inputsYamlPath = Join-Path $ConfigFolderPath "inputs.yaml" $configUpdated = $false # Process inputs.yaml - prompt for ALL inputs if (Test-Path $inputsYamlPath) { Write-ToConsoleLog "=== Bootstrap Configuration (inputs.yaml) ===" Write-ToConsoleLog "For more information, see: https://aka.ms/alz/acc/phase0" # Read the raw content to preserve comments and ordering $inputsYamlContent = Get-Content -Path $inputsYamlPath -Raw $inputsConfig = $inputsYamlContent | ConvertFrom-Yaml -Ordered $inputsUpdated = $false # Track changes to apply to the raw content $changes = @{} # Get the appropriate schema sections based on version control $bootstrapSchema = $null $vcsSchema = $null if ($null -ne $schema) { $bootstrapSchema = $schema.inputs.bootstrap.properties if ($VersionControl -eq "github") { $vcsSchema = $schema.inputs.github.properties } elseif ($VersionControl -eq "azure-devops") { $vcsSchema = $schema.inputs.azure_devops.properties } elseif ($VersionControl -eq "local") { $vcsSchema = $schema.inputs.local.properties } } foreach ($key in @($inputsConfig.Keys)) { $currentValue = $inputsConfig[$key] # Handle nested subscription_ids object (always in schema) if ($key -eq "subscription_ids" -and $currentValue -is [System.Collections.IDictionary]) { # Skip subscription_ids in SensitiveOnly mode (subscription IDs are not sensitive) if ($SensitiveOnly.IsPresent) { continue } # Only process if subscription_ids is in the schema if ($null -eq $bootstrapSchema -or -not ($bootstrapSchema.PSObject.Properties.Name -contains "subscription_ids")) { continue } $subscriptionIdsSchema = $bootstrapSchema.subscription_ids.properties foreach ($subKey in @($currentValue.Keys)) { $subCurrentValue = $currentValue[$subKey] $subSchemaInfo = $null if ($null -ne $subscriptionIdsSchema -and $subscriptionIdsSchema.PSObject.Properties.Name -contains $subKey) { $subSchemaInfo = $subscriptionIdsSchema.$subKey } else { # Skip subscription IDs not in schema continue } $result = Read-InputValue -Key $subKey -CurrentValue $subCurrentValue -SchemaInfo $subSchemaInfo -DefaultDescription "Subscription ID for $subKey" -AzureContext $AzureContext $subNewValue = $result.Value $subIsSensitive = $result.IsSensitive if ($subNewValue -ne $subCurrentValue -or $subIsSensitive) { $currentValue[$subKey] = $subNewValue $changes["subscription_ids.$subKey"] = @{ OldValue = $subCurrentValue NewValue = $subNewValue Key = $subKey IsNested = $true IsSensitive = $subIsSensitive } $inputsUpdated = $true } } continue } # Skip inputs that are not in the schema $schemaInfo = $null $isInBootstrapSchema = $null -ne $bootstrapSchema -and $bootstrapSchema.PSObject.Properties.Name -contains $key $isInVcsSchema = $null -ne $vcsSchema -and $vcsSchema.PSObject.Properties.Name -contains $key if (-not $isInBootstrapSchema -and -not $isInVcsSchema) { # This input is not in the schema, skip it continue } # Look up schema info from bootstrap or VCS-specific schema if ($isInBootstrapSchema) { $schemaInfo = $bootstrapSchema.$key } elseif ($isInVcsSchema) { $schemaInfo = $vcsSchema.$key } # Check if this is a sensitive input $isSensitiveField = Get-SchemaProperty -SchemaInfo $schemaInfo -PropertyName "sensitive" -Default $false # In SensitiveOnly mode, skip non-sensitive inputs if ($SensitiveOnly.IsPresent -and -not $isSensitiveField) { continue } # In SensitiveOnly mode, check if sensitive value is already set if ($SensitiveOnly.IsPresent -and $isSensitiveField) { # Check environment variable first $envVarName = "TF_VAR_$key" $envVarValue = [System.Environment]::GetEnvironmentVariable($envVarName) if (-not [string]::IsNullOrWhiteSpace($envVarValue)) { Write-ToConsoleLog "[$key] - Already set via environment variable $envVarName" continue } # Check if config value is a real value (not empty, not a placeholder) $isPlaceholderValue = $currentValue -is [string] -and $currentValue -match '^\s*<.*>\s*$' $isSetViaEnvVarPlaceholder = $currentValue -is [string] -and $currentValue -like "Set via environment variable*" if (-not [string]::IsNullOrWhiteSpace($currentValue) -and -not $isPlaceholderValue -and -not $isSetViaEnvVarPlaceholder) { Write-ToConsoleLog "[$key] - Already set in configuration" continue } } $result = Read-InputValue -Key $key -CurrentValue $currentValue -SchemaInfo $schemaInfo -AzureContext $AzureContext $newValue = $result.Value $isSensitive = $result.IsSensitive # Update if changed (handle array comparison) or if sensitive (always track sensitive values) $hasChanged = $false if ($currentValue -is [System.Collections.IList] -or $newValue -is [System.Collections.IList]) { # Compare arrays $currentArray = @($currentValue) $newArray = @($newValue) if ($currentArray.Count -ne $newArray.Count) { $hasChanged = $true } else { for ($i = 0; $i -lt $currentArray.Count; $i++) { if ($currentArray[$i] -ne $newArray[$i]) { $hasChanged = $true break } } } } else { $hasChanged = $newValue -ne $currentValue } if ($hasChanged -or $isSensitive) { $inputsConfig[$key] = $newValue $changes[$key] = @{ OldValue = $currentValue NewValue = $newValue Key = $key IsNested = $false IsArray = $newValue -is [System.Collections.IList] IsBoolean = $newValue -is [bool] IsNumber = $newValue -is [int] -or $newValue -is [long] -or $newValue -is [double] IsSensitive = $isSensitive } $inputsUpdated = $true } } # Save updated inputs.yaml preserving comments and ordering if ($inputsUpdated) { $updatedContent = $inputsYamlContent $sensitiveEnvVars = @{} foreach ($changeKey in $changes.Keys) { $change = $changes[$changeKey] $key = $change.Key $oldValue = $change.OldValue $newValue = $change.NewValue $isArray = if ($change.ContainsKey('IsArray')) { $change.IsArray } else { $false } $isBoolean = if ($change.ContainsKey('IsBoolean')) { $change.IsBoolean } else { $false } $isNumber = if ($change.ContainsKey('IsNumber')) { $change.IsNumber } else { $false } $isSensitive = if ($change.ContainsKey('IsSensitive')) { $change.IsSensitive } else { $false } # Handle sensitive values - set as environment variable instead of in file if ($isSensitive -and -not [string]::IsNullOrWhiteSpace($newValue)) { $envVarName = "TF_VAR_$key" [System.Environment]::SetEnvironmentVariable($envVarName, $newValue) $sensitiveEnvVars[$key] = $envVarName # Update the config file to indicate it's set as an env var $envVarPlaceholder = "Set via environment variable $envVarName" $escapedOldValue = if ([string]::IsNullOrWhiteSpace($oldValue)) { "" } else { [regex]::Escape($oldValue) } if ([string]::IsNullOrWhiteSpace($escapedOldValue)) { $pattern = "(?m)^(\s*${key}:\s*)`"?`"?(\s*)(#.*)?$" } else { $pattern = "(?m)^(\s*${key}:\s*)`"?${escapedOldValue}`"?(\s*)(#.*)?$" } $replacement = "`${1}`"$envVarPlaceholder`"`${2}`${3}" $updatedContent = $updatedContent -replace $pattern, $replacement continue } if ($isArray) { # Handle array values - convert to YAML inline array format $yamlArrayValue = "[" + (($newValue | ForEach-Object { "`"$_`"" }) -join ", ") + "]" # Check if old value is already in array format or a different format (string/placeholder) $oldValueIsArray = $oldValue -is [System.Collections.IList] if ($oldValueIsArray) { # Match the existing array - greedy match within brackets $pattern = "(?m)^(\s*${key}:\s*)\[[^\]]*\](\s*)(#.*)?$" } else { # Old value was a string/placeholder, match quoted or unquoted value $escapedOldValue = if ([string]::IsNullOrWhiteSpace($oldValue)) { "" } else { [regex]::Escape($oldValue.ToString()) } if ([string]::IsNullOrWhiteSpace($escapedOldValue)) { $pattern = "(?m)^(\s*${key}:\s*)`"?`"?(\s*)(#.*)?$" } else { $pattern = "(?m)^(\s*${key}:\s*)`"?${escapedOldValue}`"?(\s*)(#.*)?$" } } $replacement = "`${1}$yamlArrayValue`${2}`${3}" } elseif ($isBoolean) { # Handle boolean values - no quotes, lowercase true/false $yamlBoolValue = if ($newValue) { "true" } else { "false" } # Match any boolean-like value (true/false/True/False/yes/no) case-insensitively $pattern = "(?mi)^(\s*${key}:\s*)`"?(true|false)`"?(\s*)(#.*)?$" $replacement = "`${1}$yamlBoolValue`${3}`${4}" } elseif ($isNumber) { # Handle numeric values - no quotes $yamlNumValue = $newValue.ToString() $escapedOldValue = [regex]::Escape($oldValue.ToString()) $pattern = "(?m)^(\s*${key}:\s*)`"?${escapedOldValue}`"?(\s*)(#.*)?$" $replacement = "`${1}$yamlNumValue`${2}`${3}" } else { # Handle string values # Escape special regex characters in the old value $escapedOldValue = [regex]::Escape($oldValue) # Build regex pattern to match the key-value pair # This handles both quoted and unquoted values if ([string]::IsNullOrWhiteSpace($oldValue)) { # Empty value - match key followed by colon and optional whitespace/quotes $pattern = "(?m)^(\s*${key}:\s*)`"?`"?(\s*)(#.*)?$" $replacement = "`${1}`"$newValue`"`${2}`${3}" } else { # Non-empty value - match the specific value $pattern = "(?m)^(\s*${key}:\s*)`"?${escapedOldValue}`"?(\s*)(#.*)?$" $replacement = "`${1}`"$newValue`"`${2}`${3}" } } $updatedContent = $updatedContent -replace $pattern, $replacement } $updatedContent | Set-Content -Path $inputsYamlPath -Force -NoNewline Write-ToConsoleLog "Updated inputs.yaml" -IsSuccess # Display summary of sensitive environment variables if ($sensitiveEnvVars.Count -gt 0) { Write-ToConsoleLog "Sensitive values have been set as environment variables:" -IsWarning foreach ($varKey in $sensitiveEnvVars.Keys) { Write-ToConsoleLog "$varKey -> $($sensitiveEnvVars[$varKey])" -IsSelection -IndentLevel 1 } Write-ToConsoleLog "These environment variables are set for the current process only." Write-ToConsoleLog "The config file contains placeholders indicating the values are set via environment variables." } $configUpdated = $true } } return $configUpdated } } # SIG # Begin signature block # MIIoLAYJKoZIhvcNAQcCoIIoHTCCKBkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAwL1pOZ8zcrcus # 3HvD2FUP6heFFfr3U8MmgpRdkpmM2aCCDXYwggX0MIID3KADAgECAhMzAAAEhV6Z # 7A5ZL83XAAAAAASFMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM3WhcNMjYwNjE3MTgyMTM3WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDASkh1cpvuUqfbqxele7LCSHEamVNBfFE4uY1FkGsAdUF/vnjpE1dnAD9vMOqy # 5ZO49ILhP4jiP/P2Pn9ao+5TDtKmcQ+pZdzbG7t43yRXJC3nXvTGQroodPi9USQi # 9rI+0gwuXRKBII7L+k3kMkKLmFrsWUjzgXVCLYa6ZH7BCALAcJWZTwWPoiT4HpqQ # hJcYLB7pfetAVCeBEVZD8itKQ6QA5/LQR+9X6dlSj4Vxta4JnpxvgSrkjXCz+tlJ # 67ABZ551lw23RWU1uyfgCfEFhBfiyPR2WSjskPl9ap6qrf8fNQ1sGYun2p4JdXxe # UAKf1hVa/3TQXjvPTiRXCnJPAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUuCZyGiCuLYE0aU7j5TFqY05kko0w # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwNTM1OTAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBACjmqAp2Ci4sTHZci+qk # tEAKsFk5HNVGKyWR2rFGXsd7cggZ04H5U4SV0fAL6fOE9dLvt4I7HBHLhpGdE5Uj # Ly4NxLTG2bDAkeAVmxmd2uKWVGKym1aarDxXfv3GCN4mRX+Pn4c+py3S/6Kkt5eS # DAIIsrzKw3Kh2SW1hCwXX/k1v4b+NH1Fjl+i/xPJspXCFuZB4aC5FLT5fgbRKqns # WeAdn8DsrYQhT3QXLt6Nv3/dMzv7G/Cdpbdcoul8FYl+t3dmXM+SIClC3l2ae0wO # lNrQ42yQEycuPU5OoqLT85jsZ7+4CaScfFINlO7l7Y7r/xauqHbSPQ1r3oIC+e71 # 5s2G3ClZa3y99aYx2lnXYe1srcrIx8NAXTViiypXVn9ZGmEkfNcfDiqGQwkml5z9 # nm3pWiBZ69adaBBbAFEjyJG4y0a76bel/4sDCVvaZzLM3TFbxVO9BQrjZRtbJZbk # C3XArpLqZSfx53SuYdddxPX8pvcqFuEu8wcUeD05t9xNbJ4TtdAECJlEi0vvBxlm # M5tzFXy2qZeqPMXHSQYqPgZ9jvScZ6NwznFD0+33kbzyhOSz/WuGbAu4cHZG8gKn # lQVT4uA2Diex9DMs2WHiokNknYlLoUeWXW1QrJLpqO82TLyKTbBM/oZHAdIc0kzo # STro9b3+vjn2809D0+SOOCVZMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGgwwghoIAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAASFXpnsDlkvzdcAAAAABIUwDQYJYIZIAWUDBAIB # BQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFhpMa8y6pnIJWekeKVDD2D8 # S8S7IP2Fy7uqkkXT6472MEQGCisGAQQBgjcCAQwxNjA0oBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEcgBpodHRwczovL3d3dy5taWNyb3NvZnQuY29tIDANBgkqhkiG9w0B # AQEFAASCAQBKbVaicNF7TIjtUYhs1Os9LJisTpt4S2KqbDGUyy9W70ffl2oeJGLI # wbN1nhuo3yGcbQRkqN2DtbcNPtt7/JDaiz8AgUqyfomb+wR+nbAokyOXiR/W4TZS # b5bpbmnqzCwsgtMFrYJ1RVT3MTk6sNEy5YEya8xd06kwtu7JyrYRPbYVqOgCVLgA # x3MOPBtsjBEurcJQcjgRTkiB3wEwF4nencXcdAF5IY3FwiRAUmkRhEMPWUNIVkRM # Xi9FFbCbjvMgErKG+S5h7KUHGpYbJg4Xx8d5w23FZ1c/UFVrJKOOnXCMfUbe+e5q # ZSrK0IpU++p71WyDggMLQhUv6HagzUHioYIXlDCCF5AGCisGAQQBgjcDAwExgheA # MIIXfAYJKoZIhvcNAQcCoIIXbTCCF2kCAQMxDzANBglghkgBZQMEAgEFADCCAVIG # CyqGSIb3DQEJEAEEoIIBQQSCAT0wggE5AgEBBgorBgEEAYRZCgMBMDEwDQYJYIZI # AWUDBAIBBQAEII5LcAOxflv+HkVaqw2Psp+w+n76yRRlSA7f+EGKcwlOAgZpaOKG # XosYEzIwMjYwMTMwMjAwNjMwLjEyOFowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBB # bWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo4NjAz # LTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZaCCEeowggcgMIIFCKADAgECAhMzAAACBywROYnNhfvFAAEAAAIHMA0GCSqGSIb3 # DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDEzMDE5 # NDI1MloXDTI2MDQyMjE5NDI1MlowgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlv # bnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo4NjAzLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAMU//3p0+Zx+A4N7f+e4W964Gy38mZLFKQ6fz1kX # K0dCbfjiIug+qRXCz4KJR6NBpsp/79zspTWerACaa2I+cbzObhKX35EllpDgPHeq # 0D2Z1B1LsKF/phRs/hn77yVo1tNCKAmhcKbOVXfi+YLjOkWsRPgoABONdI8rSxC4 # WEqvuW01owUZyVdKciFydJyP1BQNUtCkCwm2wofIc3tw3vhoRcukUZzUj5ZgVHFp # OCpI+oZF8R+5DbIasBtaMlg5e555MDUxUqFbzPNISl+Mp4r+3Ze4rKSkJRoqfmzy # yo1sjdse3+sT+k3PBacArP484FFsnEiSYv6f8QxWKvm7y7JY+XW3zwwrnnUAZWH7 # YfjOJHXhgPHPIIb3biBqicqOJxidZQE61euc8roBL8s3pj7wrGHbprq8psVvNqpZ # cCPMSJDwRj0r2lgj8oLKCLGMPAd9SBVJYLJPwrDuYYHJRmZE8/Fc42W4x78/wK0E # kym6HwIFbKO8V8WY5I1ErwRORSaVNQBHUIg5p4GosbCxxKEV/K8NCtsKGaFeJvid # ExflT1iv13tVxgefp5kmyDLOHlAqUhsJAL9i+EUrjZx4IEMxtz463lHpP8zBx7mN # XJUKapdXFY5pBzisDadXuicw5kLpS8IbwsYVJkGePWWgMMtaj8j5G5GiTaP9DjNw # yfCRAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUcrVSYsK9etAK9H3wkGrXz/jOjR4w # HwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKg # UIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAw # XjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # ZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQw # DAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E # BAMCB4AwDQYJKoZIhvcNAQELBQADggIBAOO7Sq49ueLHSyUSMPuPbbbilg48ZOZ0 # O87T5s1EI2RmpS/Ts/Tid/Uh/dj+IkSZRpTvDXYWbnzYiakP8rDYKVes0os9ME7q # d/G848a1qWkCXjCqgaBnG+nFvbIS6cbjJlDoRA6mDV0T245ejN7eAPgeO1xzvmRx # rzKK+jAQj6uFe5VRYHu+iDhMZTEp2cO+mTkZIZec6E8OF0h36DqFHJd1mLCARr6r # 0z1dy3PhMaEOA4oWxjEWFc0lmj0pG4arp6+G3I125iuTOMO1ZLqBbxqRHn1SG4sa # xWr7gCCoRjxaVeNAYzY5OTIGeVAukHyoPvH2NGljYKrQ5ZaUrTB8f/XN5+tY3n5t # 7ztLDZM9wi50gmff1tsMbtrAoxVgMd+w8nxm/GBaRm5/plkCSmHR5gaHchXzjm1o # uR0s4K/Dj1bGqFrkOaLY6OHwaNrm/2TJjcpMXJfdPgLaxzF+Cn/rFF34MY6E1U+9 # U9r/fJFSpjmzlRinLwOdumlXudA7ax7ce8JJutv7I/J6hvWRR8xhr18TZsSygxs5 # odGAaOLxk+38l3Zs991CgEdxQ6o/CMcFQhxJzvF0lliNFvibzWrGOZrcMuO44WWM # xlNii9GIa8Qwv3FmPakdFTK/6zm/tUbBwzquM1gzirNlAzoDZEZgkZTvzQZAbRA7 # 3zD6y5y5NWt9MIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg # VGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+ # F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU # 88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqY # O7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzp # cGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0Xn # Rm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1 # zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZN # N3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLR # vWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTY # uVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUX # k8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB # 2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKR # PEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0g # BFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5t # aWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQM # MAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE # AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQ # W9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv # bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBa # BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqG # SIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOX # PTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6c # qYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/z # jj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz # /AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyR # gNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdU # bZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo # 3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4K # u+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10Cga # iQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9 # vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGC # A00wggI1AgEBMIH5oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODYwMy0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMV # ANO9VT9iP2VRLJ4MJqInYNrmFSJLoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDtJySpMCIYDzIwMjYwMTMwMTIz # ODAxWhgPMjAyNjAxMzExMjM4MDFaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAO0n # JKkCAQAwBwIBAAICEBUwBwIBAAICEr4wCgIFAO0odikCAQAwNgYKKwYBBAGEWQoE # AjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkq # hkiG9w0BAQsFAAOCAQEAhxMxeQXyETOes4osSUWpLaZseMPATvOPd6Tzet1o39bg # 0eETvOE+p2TT8EheMoXSn18vGbww6+59tp61WNrz9pNRX8JdKSdD2yeh9D79DkkJ # BD4t36o+KrS5lPMuojNR/wgrCqHcSMfk2WrEbsAgQ+CcC8qdCqOjsD/+R+TEuvq2 # jwdNLe5LNHDc67uUTWv58zk5e/ck6I8LZjUxeo5IyxRXydjqmWYgj7JF/vo7dVFS # H0Q1hDr9NmSzULiCPrzmtOalFvZVpBvb8v49mo4ECL/3HlaG7Yi3sPemZyF+yn+K # lfAK9K9k1VbPw9KDqOmhPyYvORrb/X8QOn6Hmb4L0jGCBA0wggQJAgEBMIGTMHwx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p # Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACBywROYnNhfvFAAEAAAIH # MA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQw # LwYJKoZIhvcNAQkEMSIEIFePPJfxscv4MMieDNYmq2IhSwleql1J2mfuJc53mRmG # MIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQgL/fU0dB2zUhnmw+e/n2n6/oG # MEOCgM8jCICafQ8ep7MwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMAITMwAAAgcsETmJzYX7xQABAAACBzAiBCDhPpjj8HLAlKwTxaPbCrB9fPwt # eEbA1P6zsArY79cNuDANBgkqhkiG9w0BAQsFAASCAgBaVYNDvJWyQvORcra0CGl2 # bCNH86+jxSP9u44Ngtua9UEdCjvZtdWhibWm4bD45R6h0yCuTDa+QX6usG3WYVSt # tuhLO3s3a/tlmY0l5GbFrw6Ujw/oozkjf7AIE+5XIVEMt52ZqrUrzwFQ0auzm3WO # cJX3Xmlkawu6HCxg6EQ/vwCJAUBw1QsdbZt4HWM5VVZ7LFDAsSCba9rhocrYJcjh # in0gQHUEACHCroKIZYVVXg3WB7X76qwE5yGjIzPWJ8HDMRdn8YB2lvmcmUSIG+JQ # k4y4naSjjORRPPaOVzTgwZ+Ywd7EkezUMC91txAjwdjyY86i2mcF0KVEqt60cfEG # IYSmJYcq4/5CZF7m2Xg1yRRbyOCvZ7dHjVFRBqwlOyJugJA8H1qev+VpPATCpv8n # I1GbHTzgG8W8Ux4JmG96d1WQQIxXW4U6M4TvJOdGsFJ4HGkUE5Lk4WMYv/ih93Oe # xWt8H5cjiG+PWgwJky5eAECelSu7q5qRc+ir94a7JtxGxnMomykAhlIEt1Zdm2Ec # 0b9HIpOidQ19SV2TRZ9ywcD3Q5os/igKbuZdb9DSbOsKe2AmmsMTaiIVdtnpeFsP # E+Rd5eS/gnPYXkdqYdVlxSzENdVRX8DrLnBRLHPcSN44OueUlN1Gfnq/wlTKP9Zp # WyX1hW5kJdyy0aYRB54sUQ== # SIG # End signature block |