Obs/bin/ObsDep/content/Powershell/Roles/Common/MountedImage.psm1
<###################################################
# # # Copyright (c) Microsoft. All rights reserved. # # # ##################################################> Import-Module -Name "$PSScriptRoot\..\..\Common\Helpers.psm1" Import-Module -Name "$PSScriptRoot\RoleHelpers.psm1" Import-Module -Name Defender <# MountedImage Class provides a standard way of mounting and updating an image. The class supports both WIM and VHDx files and can be used to inject Windows updates, drivers and deployment artifacts. Any injection of content that is to be done on an offline image should be added to this class. #> class MountedImage : System.IDisposable { [string] $MountPath = $null [string] $ImagePath [string] $imageStatusFolder = "ImageStatus" [string] $imageStatusFileName = "ImageStatus.xml" # Constructors <# .SYNOPSIS This function mounts a WIM for offline update. .PARAMETER ImagePath Path to the WIM file to be updated. .PARAMETER ImageIndex Index for WIM file. This value should be set to 1 for most WIM files. #> MountedImage( [string] $ImagePath, [int] $ImageIndex ) { if ($ImagePath -notlike '*.wim') { throw "$ImagePath is not a WIM." } $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop $this.Init($ImagePath) # Mount the image Trace-Execution "Mount '$ImagePath' to '$($this.MountPath)'." Mount-WindowsImageWithRetry -ImagePath $ImagePath -Path $this.mountPath -Index $ImageIndex } <# .SYNOPSIS This function mounts VHD image for offline update. .PARAMETER ImagePath Path to the VHD to be updated. #> MountedImage( [string] $ImagePath ) { if ($ImagePath -notlike '*.vhd*') { throw "$ImagePath is not a VHD." } $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop $this.Init($ImagePath) # Mount the image Trace-Execution "Mount '$ImagePath' to '$($this.MountPath)'." Mount-WindowsImageWithRetry -ImagePath $ImagePath -Path $this.mountPath -Index 1 } # Initializes the infrastructure pieces needed to mount the image [void] Init( [string] $ImagePath ) { $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop $this.ImagePath = $ImagePath $this.DiscardPreviousImage() $this.MountPath = Join-Path -Path "C:\Temp\" -ChildPath $(Get-Item -Path $([IO.Path]::GetTempFileName())).Name # Create the mount path if it does not exist if(-not (Test-Path $this.MountPath)) { New-Item -Path $this.MountPath -ItemType Directory } # Attempt to start defender if not already running $service = Get-Service -Name "windefend" -ErrorAction SilentlyContinue if ($service -ne $null -and $service.Status -ine "Running") { $service | Start-Service -ErrorAction SilentlyContinue | Out-Null } # set exclusion path when windefend is running if($service -ne $null -and $service.Status -eq "Running") { Trace-Execution "Exclude mount folders $($this.MountPath) from Windows Defender scan to speed up configuration." Add-MpPreference -ExclusionPath $this.MountPath -ErrorAction SilentlyContinue } } <# Gets the list of all previously mounted images and dismount them. #> [void] DiscardPreviousImage() { $mountedImages = Get-WindowsImage -Mounted foreach ($mountedImage in $mountedImages) { if ($mountedImage.ImagePath -eq $this.ImagePath) { $mountedImage | Dismount-WindowsImage -Discard } } } <# Gets the build of the mounted image from ntoskrnl.exe. #> [string] GetBuild() { $filePath = Join-Path -Path $this.MountPath -ChildPath 'Windows\System32\ntoskrnl.exe' Trace-Execution "Getting image build from '$filePath'" $fileInfo = Get-Item -Path $filePath $build = $fileInfo.VersionInfo.ProductBuildPart Trace-Execution "Image build is '$($build.ToString())'" if ($build -ge 18000) { $build = 17763 Trace-Execution "Forcing image build to '$($build.ToString())'" } return $build.ToString() } <# .SYNOPSIS Get version and status information from the image .DESCRIPTION If the image status file is present. The function reads contents of the file and returns it back. If the image status file is not present. It return null #> [xml] GetStatusInfoFromImage() { $content = $null $imageStatusFilePath = "$this.MountPath\$this.ImageStatusFolder\$this.ImageStatusFileName" if(Test-Path $imageStatusFilePath) { # Read the contents of the file $content = [xml](Get-Content $imageStatusFilePath) } return $content } <# .SYNOPSIS Updated the image status file with version and status. .DESCRIPTION Takes in version and the status as input. Generates a XML blob and saves it into a predefined location. .PARAMETER Version Version of deployment or update being applied to the stamp. .PARAMETER Status Status value that is to be set in the image #> UpdateImageStatusFile( [string] $Version, [string] $Status ) { $statusId = $this.GetStatusIdForStatusString($Status) Trace-Execution "Updating image status by Setting Version: $Version StatusId: $statusId Status: $Status" $imageStatusXML = "<ImageStatus Version=`"$Version`" StatusId=`"$statusId`" Status=`"$Status`" />" if( -not (Test-Path "$this.MountPath\$this.ImageStatusFolder") ) { $null = New-Item -Path $this.MountPath -Name $this.ImageStatusFolder -ItemType Directory -Force } $imageStatusFilePath = (Join-Path $this.MountPath (Join-Path $this.ImageStatusFolder $this.ImageStatusFileName)) $imageStatusXML | Out-File $imageStatusFilePath -Force } <# Returns an integer value corresponding to the status string #> [int] GetStatusIdForStatusString( [string] $Status ) { $statusToIdMap = @{} $statusToIdMap.Add("BaseBuildCompleted", 0) $statusToIdMap.Add("BuildCompleted", 1) return $statusToIdMap[$Status] } <# .SYNOPSIS Injects windows updates into the mounted image. .DESCRIPTION Looks for the windows update staging folder and updates inside it. If updates are present, it calls into Add-WindowsPackage to inject Windows Updates into the mounted image. .PARAMETER WindowsUpdateStagingFolder Folder containing all the Windows Updates to be applied. .PARAMETER ScratchDirectory Optional Scratch directory for Add-WindowsPackage #> UpdateImageWithWindowsUpdates( [string] $WindowsUpdateStagingFolder ) { $this.UpdateImageWithWindowsUpdates($WindowsUpdateStagingFolder, $null, $false) } UpdateImageWithWindowsUpdates( [string] $WindowsUpdateStagingFolder, [string] $ScratchDirectory ) { $this.UpdateImageWithWindowsUpdates($WindowsUpdateStagingFolder, $ScratchDirectory, $false) } UpdateImageWithWindowsUpdates( [string] $WindowsUpdateStagingFolder, [string] $ScratchDirectory, [bool] $IgnoreCheck ) { $ErrorActionPreference = "Stop" Trace-Execution "Performing Windows Update injection for: $($this.ImagePath). WindowsUpdateStagingFolder = '$WindowsUpdateStagingFolder', ScratchDirectory = '$ScratchDirectory'" if (Test-Path $WindowsUpdateStagingFolder) { $versionedFolder = Get-AzSVersionedPath -Path $WindowsUpdateStagingFolder -ErrorAction SilentlyContinue if ($versionedFolder -ne $null) { $WindowsUpdateStagingFolder = $versionedFolder } } Trace-Execution "Found Windows update folder: $WindowsUpdateStagingFolder" $subFolders = @("SSU", "LCU") foreach ($subFolder in $subFolders) { $UpdateFolder = Join-Path $WindowsUpdateStagingFolder $subFolder if(Test-Path $UpdateFolder) { $items = @(Get-ChildItem -Path $UpdateFolder -Include @("*.cab", "*.msu") -Recurse) if($items.Count -gt 0) { Trace-Execution "Injecting windows updates staged at: $UpdateFolder" -Verbose if ($ScratchDirectory) { Trace-Execution "Refreshing PS Drive cache." Get-PSDrive | Out-Null Trace-Execution "Installing updates using ScratchDirectory '$ScratchDirectory'" New-Item -ItemType Directory -Path $ScratchDirectory -Force try { Add-WindowsPackage -PackagePath $UpdateFolder -Path $this.MountPath -ScratchDirectory $ScratchDirectory -IgnoreCheck:$IgnoreCheck } finally { Remove-Item -Force -Recurse -Path $ScratchDirectory -ErrorAction Ignore } } else { Add-WindowsPackage -PackagePath $UpdateFolder -Path $this.MountPath -IgnoreCheck:$IgnoreCheck } } else { Trace-Warning "Windows update folder: $UpdateFolder exists but no updated were found. Skipping windows update injection." -Verbose } } else { Trace-Warning "Windows update folder: $UpdateFolder not found. Skipping windows update injection." -Verbose } } } <# .SYNOPSIS Adds drivers to the mounted image .DESCRIPTION Adds drivers to the mounted image .PARAMETER DriverPath Location where the drivers will be staged for consumption .PARAMETER SkipDriverInjection Value from the customer config that specifies if the driver injection should be skipped or not. #> AddDriversToImage( [string] $DriverPath, [bool] $SkipDriverInjection ) { Trace-Execution "Performing driver injection for $($this.ImagePath)" # Perform driver injection if needed if (-not ($this.TestSkipDriverInjection($DriverPath, $SkipDriverInjection) )) { Trace-Execution "Adding drivers to the image from: $DriverPath" Trace-Execution "Getting existing OEM drivers." $existingDrivers = Get-WindowsDriver -Path $this.MountPath Trace-Execution "Add Windows drivers from $DriverPath." $addDrivers = Add-WindowsDriver -Driver $DriverPath -Path $this.MountPath -Recurse if($existingDrivers) { $addDrivers = $addDrivers | Where-Object {$_.Driver -notin $existingDrivers.Driver} } foreach($driver in $addDrivers) { Trace-Execution "$($driver.ProviderName) $(Split-Path -Path $driver.OriginalFileName -Leaf) version $($driver.Version) added as $($driver.Driver)" } } else { Trace-Execution "Skipping driver injection." } } <# .SYNOPSIS Adds a directory to the mounted image .DESCRIPTION Adds a directory to the mounted image .PARAMETER SourcePath Location where the directory will be staged for consumption .PARAMETER Destination Destination path of the directory to be added #> AddDirectoryToImage( [string] $SourcePath, [string] $Destination ) { $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # only copy if the source exists if(Test-Path -Path $SourcePath) { $Directory = Split-Path -Path $SourcePath -Leaf $DestinationPath = Join-Path -Path $this.MountPath (Join-Path -Path $Destination -ChildPath $Directory) # only copy if the destination doesn't already exist if(-not (Test-Path -Path $DestinationPath)) { $TempDestinationPath = "${DestinationPath}_Temp" try { # create a temporary version of the destination directory if(-not (Test-Path -Path $TempDestinationPath)) { Trace-Execution "Creating temporary directory $TempDestinationPath" $null = New-Item -Path $TempDestinationPath -ItemType Directory } # copy each file or directory from the source to the temporary folder foreach($item in (Get-ChildItem -Path $SourcePath)) { Trace-Execution "Copying $($item.FullName) to $TempDestinationPath" Copy-Item -Path $item.FullName -Destination $TempDestinationPath -Recurse } } finally { # rename the temporary folder Trace-Execution "Renaming temporary directory $TempDestinationPath to $Directory" Rename-Item -Path $TempDestinationPath -NewName $Directory } } else { Trace-Execution "$DestinationPath already exists" } } else { Trace-Execution "$SourcePath does not exist" } } <# .SYNOPSIS Adds OEM SLP license to the mounted image .DESCRIPTION Adds OEM SLP license file to the mounted image .PARAMETER Path Source location of the OEM license file #> AddOemLicense( [string] $Path ) { $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop if(Test-Path -Path $Path) { $DestinationPath = Join-Path -Path $this.MountPath -ChildPath "\Windows\System32\OEM" New-Item -Path $DestinationPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null Trace-Execution "Copying $Path to $DestinationPath" Copy-Item -Path $Path -Destination $DestinationPath -Force } else { Trace-Execution "$Path does not exist" } } <# .SYNOPSIS Evaluates if driver injection should be skipped or not .PARAMETER DriverPath Location where the drivers will be staged for consumption .PARAMETER SkipDriverInjection Value from the customer config that specifies if the driver injection should be skipped or not. #> [bool] TestSkipDriverInjection( [string] $DriverPath, [bool] $SkipDriverInjection ) { $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop if ($skipDriverInjection) { Trace-Warning 'Driver injection will be skipped as instructed by $CloudBuilder.SkipDriverInjection setting.' } elseif (-not (Test-Path $DriverPath)) { Trace-Execution "Driver injection will be skipped as the driver path '$DriverPath' does not exist." $skipDriverInjection = $true } elseif (-not (Get-ChildItem $DriverPath)) { Trace-Execution "Driver injection will be skipped as the driver path '$DriverPath' does not contain any files." $skipDriverInjection = $true } elseif (-not (Get-ChildItem $DriverPath -Filter *.inf -Recurse)) { Trace-Execution "Driver injection will be skipped as the driver path '$DriverPath' does not contain any drivers (no *.inf files found)." $skipDriverInjection = $true } return $skipDriverInjection } <# .SYNOPSIS Expands the deployment artifacts to the destination location. .DESCRIPTION Expands all deployment artifacts into the destination root path. .EXAMPLE ExpandDeploymentArtifacts($Parameters.Configuration.Role.PrivateInfo.DeploymentContent) .PARAMETER DeploymentContentNode The xml node containing the deployment content to deliver to the destination. #> ExpandDeploymentArtifacts( [System.Xml.XmlElement] $DeploymentContentNode, [string] $NugetStorePath ) { Expand-DeploymentArtifacts -DeploymentContentNode $DeploymentContentNode -DestinationRootPath $this.MountPath -NugetStorePath $NugetStorePath } <# .SYNOPSIS Expands the deployment artifacts to the destination location. .DESCRIPTION Expands all deployment artifacts into the destination root path. .EXAMPLE ExpandDeploymentArtifacts($Parameters.Configuration.Role.PrivateInfo.DeploymentContent) .PARAMETER DeploymentContentNode The xml node containing the deployment content to deliver to the destination. #> ExpandDeploymentArtifacts( [System.Xml.XmlElement] $DeploymentContentNode) { $NugetStorePath = "$env:SystemDrive\CloudDeployment\NuGetStore" Expand-DeploymentArtifacts -DeploymentContentNode $DeploymentContentNode -DestinationRootPath $this.MountPath -NugetStorePath $NugetStorePath } # Dispose methods for the class # Invoke this directly if you want the VHD to be unmounted immediately, rather than when the powershell session dies. [void]Dispose() { $this.Dispose($true) } [void]Dispose( [bool]$disposing ) { # Dismount the image that was updated if ($this.MountPath -ne $null) { Trace-Execution "Dismount and save mounted image." $logFilePath = "C:\Temp\vstor.etl" $successlogFilePath = "C:\Temp\vstorGood.etl" Copy-Item $PSScriptRoot\vstortrace.wprp C:\Temp -Force $logEnabled = $false if (-not (Test-Path $logFilePath)) { Trace-Execution "Starting WPR tracing for dismount." $logEnabled = $true & Wpr.exe -start C:\Temp\vstortrace.wprp!VStorWPP -filemode } try { Dismount-WindowsImage -Path $this.mountPath -Save -Verbose:$false } finally { if ($logEnabled) { Trace-Execution "Stopping WPR tracing for dismount." & Wpr.exe -stop $logFilePath } } if ($logEnabled) { if (-not (Test-Path $successlogFilePath)) { Trace-Execution "Keeping the first successful dismount log on this host." Copy-Item $logFilePath $successlogFilePath } Trace-Execution "Removing WPR log file since dismount is successful." Remove-Item $logFilePath -Force -ErrorAction Ignore } # Attempt to start defender if not already running $service = Get-Service -Name "windefend" -ErrorAction SilentlyContinue if ($service -ne $null -and $service.Status -ine "Running") { $service | Start-Service -ErrorAction SilentlyContinue | Out-Null } if($service -ne $null -and $service.Status -eq "Running") { Trace-Execution "Remove Windows Defender exclusions for paths $($this.MountPath)." Remove-MpPreference -ExclusionPath $this.MountPath -ErrorAction SilentlyContinue } $this.MountPath = $null } } } # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA5mVzTf3zNLZmJ # PZc9nVejLGirvBYp0WbNgBdopNhaFKCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEINuptWxgrG/fWeSjLWX+5sj8 # eI5BrT2MRY2b21KU8rleMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAWL0IQsI0mqwYAjLjCqIV+MK6wxn5xwodeEkEzq/hrfrALgeMA32ujTf6 # 6sOKRW7gFqwAxSFrMJMDEIEj7Ib4syWBu6+8kfX+ioyc5c1fXGaxHiTHWdiQY47f # KToT636UNOPoSFsXhHxLUqiVdZQG1tqOy/5V/W3AlVI/g/j8I5o4XWm+d/0KBtXH # GOsYDa2QIz+e2bsp7LK0AzsJsccViTpX0EShOqQ3DTK5BbnyBUMVPugzEC+J3M0/ # b+8Z54oLI7Zc0jBMRJkcTGpWUjzgCzhJwn0AptuzXS8phGWugMV4zqHyJjHf+Tcz # a6r7NHkRBZFBRFyrRm6wBtc6giUvt6GCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCC7CfnO9IGPtiM2SSpqgS9KK0XhPP1pTYGWvMk8C1kSKAIGZQQYsX0H # GBMyMDIzMDkyMjA4MzA1OC4wNzNaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODkwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAdMdMpoXO0AwcwABAAAB0zANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy # MjRaFw0yNDAyMDExOTEyMjRaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODkwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQC0jquTN4g1xbhXCc8MV+dOu8Uqc3KbbaWti5vdsAWM # 1D4fVSi+4NWgGtP/BVRYrVj2oVnnMy0eazidQOJ4uUscBMbPHaMxaNpgbRG9FEQR # FncAUptWnI+VPl53PD6MPL0yz8cHC2ZD3weF4w+uMDAGnL36Bkm0srONXvnM9eNv # nG5djopEqiHodWSauRye4uftBR2sTwGHVmxKu0GS4fO87NgbJ4VGzICRyZXw9+Rv # vXMG/jhM11H8AWKzKpn0oMGm1MSMeNvLUWb31HSZekx/NBEtXvmdo75OV030NHgI # XihxYEeSgUIxfbI5OmgMq/VDCQp2r/fy/5NVa3KjCQoNqmmEM6orAJ2XKjYhEJzo # p4nWCcJ970U6rXpBPK4XGNKBFhhLa74TM/ysTFIrEXOJG1fUuXfcdWb0Ex0FAeTT # r6gmmCqreJNejNHffG/VEeF7LNvUquYFRndiCUhgy624rW6ptcnQTiRfE0QL/gLF # 41kA2vZMYzcc16EiYXQQBaF3XAtMduh1dpXqTPPQEO3Ms5/5B/KtjhSspMcPUvRv # b35IWN+q+L+zEwiphmnCGFTuyOMqc5QE0ruGN3Mx0Vv6x/hcOmaXxrHQGpNKI5Pn # 79Yk89AclqU2mXHz1ZHWp+KBc3D6VP7L32JlwxhJx3asa085xv0XPD58MRW1WaGv # aQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFNLHIIa4FAD494z35hvzCmm0415iMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQBAYlhYoUQ+4aaQ54MFNfE6Ey8v4rWv+LtD # RSjMM2X9g4uanA9cU7VitdpIPV/zE6v4AEhe/Vng2UAR5qj2SV3sz+fDqN6VLWUZ # sKR0QR2JYXKnFPRVj16ezZyP7zd5H8IsvscEconeX+aRHF0xGGM4tDLrS84vj6Rm # 0bgoWLXWnMTZ5kP4ownGmm0LsmInuu0GKrDZnkeTVmfk8gTTy8d1y3P2IYc2UI4i # JYXCuSaKCuFeO0wqyscpvhGQSno1XAFK3oaybuD1mSoQxT9q77+LAGGQbiSoGlgT # jQQayYsQaPcG1Q4QNwONGqkASCZTbzJlnmkHgkWlKSLTulOailWIY4hS1EZ+w+sX # 0BJ9LcM142h51OlXLMoPLpzHAb6x22ipaAJ5Kf3uyFaOKWw4hnu0zWs+PKPd192n # deK2ogWfaFdfnEvkWDDH2doL+ZA5QBd8Xngs/md3Brnll2BkZ/giZE/fKyolriR3 # aTAWCxFCXKIl/Clu2bbnj9qfVYLpAVQEcPaCfTAf7OZBlXmluETvq1Y/SNhxC6MJ # 1QLCnkXSI//iXYpmRKT783QKRgmo/4ztj3uL9Z7xbbGxISg+P0HTRX15y4TReBbO # 2RFNyCj88gOORk+swT1kaKXUfGB4zjg5XulxSby3uLNxQebE6TE3cAK0+fnY5UpH # aEdlw4e7ijCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjg5MDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBS # x23cMcNB1IQws/LYkRXa7I5JsKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6LcosjAiGA8yMDIzMDkyMTIwNDAx # OFoYDzIwMjMwOTIyMjA0MDE4WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDotyiy # AgEAMAcCAQACAim4MAcCAQACAhRRMAoCBQDouHoyAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAKNPq+XJcVs621pT9TKpT1C9KWDPM/j1QAKdBm8SIH3RAhkT # jDo1vQJw2NrG0B/ZoZHQ6S2kpYGkeihrSmWYjttlNmnhSeMDmDCuIkopLGmSe3vv # GHgVyh3Qb66FAVyCAVtXx1Noqvhr10n+1LxLwoYec5DB2M5B/a5e2gigIiJ4fZwm # frxDjGLSpNwCmWJX8Xvislu0/1+34lQnirqPme4/lYAepAlrvqSw8P4jMQyRG6Ki # 3D7hiW4Hp+9ki0/IOWcKL89/2Pdyi5OtOjs/TacfMGtmLSzxd5F43+bTVmq6ZUmC # JPl1V+GTtEgKf4SDPbnB2ZgUM61d7zyjGCsDLIAxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdMdMpoXO0AwcwABAAAB0zAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCB8SoheSY2QBEAqRvUQyWQOpb1n7s/VSpRydJY9/QzcszCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIJJm9OrE4O5PWA1KaFaztr9uP96r # QgEn+tgGtY3xOqr1MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHTHTKaFztAMHMAAQAAAdMwIgQgKhxshKW47dBHLt8ptvcJDffkMRIl # KQoQlu49jjc6vW0wDQYJKoZIhvcNAQELBQAEggIAF7aJtRaxSur0h6MaZCHEEMaF # kqbwHWgw+OIO2ihE1bqEVe6SklQ9le88TyWHE5R2PhdL315BZlqJ9VqKZRxWzpCP # 1LhvhJlMxS/vk3UJ96B5NALsUD7XH74nKLSbRsHa54/vT9GH9v76EgKxFjW6yz0t # JEzIPZqDUqLPsWYoJR8lGMMDzBDdmlHxGiqf2adXaSVuQhusQUeZgAaKKWuiYRHp # t5jLjilzb/z3Vyyl2nA48PjEKRy1ADFuaVuvYAvjEMnHIzhUQ6mgfkkpFUa575BC # IcQOR0wABv0WfgpxLUMM4wZJP6zMagY3gntJaaOdcUMkb6OJb/YDu91UQSPKPffd # 3tu3l1lw3zwsi9OFmvDpYZ44RZOz4xsQNso3jDok7tN7QUzCPm6LhZ5ySYp1CAfO # 1kz3jqcn9GrqZOiapYj9u7Y4Yv+iUB1zhdthfZlwa4T3RIBxoBPdfsyMXi+lWJjW # MF5v0PjaX3WtnGTra8iC1Wol7GWfZXVpjo/iNj8URntF+UugkIs/eng3zDnfD6iD # 0WXU7DunExOAeI0a2qPLRCzl3ogW9qcVWCHyC1HA3dfhqxmuvBsNBEUnRUbg2RCS # Tiquza2nRK4tGbJoCSr6FCRXpQJEStJKqVvAo1Ew2rPQWiDPPbW/i+eXmZ8VXET0 # +NnzlVfYixaew8ZdTrg= # SIG # End signature block |