Framework/Managers/AzSDKPDFExtension.ps1
Set-StrictMode -Version Latest class AzSDKPDFExtension { static [void] GeneratePDF([string] $reportFolderPath, [PSObject] $subscriptionObject, [PSObject] $dataObject, [bool] $isLandscape) { # Get Context Info $executedBy = (Get-AzureRmContext).Account # Verify whether word is installed on machine If (test-path HKLM:SOFTWARE\Classes\Word.Application) { # Initialize word file try { $Word = New-Object -ComObject word.application $Word.Visible = $false; $AzSDKReportDoc = $Word.Documents.Add(); if($isLandscape) { $AzSDKReportDoc.PageSetup.Orientation = 1 } else { $AzSDKReportDoc.PageSetup.Orientation = 0 } $pdfPath = "$reportFolderPath\SecurityReport.pdf" $margin = 36 # 1.26 cm $AzSDKReportDoc.PageSetup.LeftMargin = $margin $AzSDKReportDoc.PageSetup.RightMargin = $margin #$AzSDKReportDoc.PageSetup.TopMargin = $margin $AzSDKReportDoc.PageSetup.BottomMargin = $margin $isSubscriptionCore = $false $selection = $Word.Selection $selection.WholeStory $selection.Style = "No Spacing" # Region Front Page [AzSDKPDFExtension]::WriteText($selection, 'Secure DevOps Kit for Azure (AzSDK)','Title', $true, $true, $false) [AzSDKPDFExtension]::WriteText($selection, 'Security Report','TOC Heading', $true, $true, $false) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $selection.InsertBreak(6) $TitleTableRange = $selection.Range(); $AzSDKReportDoc.Tables.Add($TitleTableRange,11,2) | Out-Null $AzSDKTitleTable = $AzSDKReportDoc.Tables.item(1) [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 1, 'Subscription Name', $subscriptionObject.SubscriptionName) [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 2, 'SubscriptionId', $subscriptionObject.SubscriptionId) [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 3, 'AzSDK Version', $dataObject.MyCommand.Version.ToString()) [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 4, 'Generated by', 'AzSDK') [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 5, 'Generated on', (get-date).ToUniversalTime().ToString("MMMM dd, yyyy HH:mm") + " (UTC)") [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 6, 'Requested by', $executedBy.Id.ToString() + " (" + $executedBy.Type.ToString() + ")") [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 7, 'Command Executed', $dataObject.Line.Trim()) [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 8, 'Documentation', 'http://aka.ms/azsdkdocs') [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 9, 'FAQ', 'http://aka.ms/azsdkdocs/faq') [AzSDKPDFExtension]::WriteHeaderTableCell($AzSDKTitleTable, 10, 'Support DL', 'mailto:isrmazsdksup@microsoft.com') $AzSDKTitleTable.Borders.OutsideLineStyle = 1 $AzSDKTitleTable.Style = 'Table Grid Light' $AzSDKTitleTable.Borders.OutsideLineStyle = 1 $AzSDKTitleTable.Borders.InsideLineStyle = 0 $AzSDKTitleTable.Columns.AutoFit() $Word.Selection.Start= $AzSDKReportDoc.Content.End $selection.InsertBreak(7) #end region # Region TOC [AzSDKPDFExtension]::WriteText($selection, 'Contents','TOC Heading', $false, $true, $false) $range = $Selection.Range $toc = $AzSDKReportDoc.TablesOfContents.Add($range) $selection.TypeParagraph() $selection.InsertBreak(7) # End region TOC # Region Headers/Footers #$Section = $AzSDKReportDoc.Sections.Item(1) #$Header = $Section.Footers.Item(1) #$Header.Range.Text = (get-date).ToUniversalTime().ToString("HH:mm MMMM dd, yyyy") + "(UTC)" #$Header.Range.Font.Size = 9 #$Header.Range.ParagraphFormat.Alignment = 2 $AzSDKReportDoc.Sections(1).Footers(1).PageNumbers.Add(2) # End region Headers/Footers #region -> Add the CSV report $selection.TypeText("Security Report Summary"); $selection.Style = 'Heading 1' $selection.TypeParagraph() $selection.Style = 'No Spacing' $selection.InsertBreak(6) $ReportRange = $selection.Range(); $reportCSVFilePath = @(); $reportCSVFilePath += Get-ChildItem -Path $reportFolderPath -Filter "*.CSV" -Recurse if($reportCSVFilePath.Length -le 0) { [AzSDKPDFExtension]::WriteText($selection, 'Unable to find the required security report under the report folder.','No Spacing', $false, $true, $false) [AzSDKPDFExtension]::WriteText($selection, 'Or','No Spacing', $true, $true, $false) [AzSDKPDFExtension]::WriteText($selection, 'No controls have been found to evaluate for the Subscription.','No Spacing', $false, $true, $false) #throw "Didn't find the required security report under the report folder."; } else { $controls = Import-Csv -Path $reportCSVFilePath[0].FullName $isAttestedResult = $false if(($controls | Measure-Object).Count -gt 0) { $Number_Of_Controls = (($controls | Measure-Object).Count +1) if($controls[0] | Get-Member -Name "AttestedSubStatus") { $isAttestedResult = $true } if($isAttestedResult) { $Number_Of_Columns = 7 # ControlID, Status, RG, ResourceName, Control Severity } else { $Number_Of_Columns = 6 } $x = 2 $AzSDKReportDoc.Tables.Add($ReportRange,$Number_Of_Controls,$Number_Of_Columns) | Out-Null $AzSDKReportTable = $AzSDKReportDoc.Tables.item(2) $AzSDKReportTable.Cell(1,1).Range.Text = "ControlId" $AzSDKReportTable.Cell(1,2).Range.Text = "Status" $AzSDKReportTable.Cell(1,3).Range.Text = "ResourceGroup" $AzSDKReportTable.Cell(1,4).Range.Text = "Resource" $AzSDKReportTable.Cell(1,5).Range.Text = "Severity" $AzSDKReportTable.Cell(1,6).Range.Text = "Description" if($isAttestedResult) { $AzSDKReportTable.Cell(1,7).Range.Text = "Attestation Description" } Foreach($control in $controls) { $AzSDKReportTable.Cell($x,1).Range.Text=$control.ControlId $AzSDKReportTable.Cell($x,2).Range.Text=$control.Status if($control | Get-Member -Name "ResourceGroupName") { $AzSDKReportTable.Cell($x,3).Range.Text=$control.ResourceGroupName if(($control | Get-Member -Name "ChildResourceName") -and (-Not [string]::IsNullOrEmpty($control.ChildResourceName))) { $AzSDKReportTable.Cell($x,4).Range.Text=$control.ResourceName + "/" + $control.ChildResourceName } else { $AzSDKReportTable.Cell($x,4).Range.Text=$control.ResourceName } } else { $isSubscriptionCore = $true $AzSDKReportTable.Cell($x,3).Range.Text="Subscription" $AzSDKReportTable.Cell($x,4).Range.Text="Subscription" } $AzSDKReportTable.Cell($x,5).Range.Text=$control.ControlSeverity $AzSDKReportTable.Cell($x,6).Range.Text=$control.Description $AzSDKReportTable.Cell($x,6).Range.Font.Size = 9 if($isAttestedResult -and ($control.AttestedSubStatus)) { #$AzSDKReportTable.Cell($x,7).Range.Text=$control.ActualStatus $attstionDescription = "Attested Status: " + $control.AttestedSubStatus + "`vAttested By: " + $control.AttestedBy + "`vJustification: " + $control.AttesterJustification $AzSDKReportTable.Cell($x,7).Range.Text = $attstionDescription $AzSDKReportTable.Cell($x,7).Range.Font.Size = 9 } $x++ #if(($control | Get-Member -Name "AttestedSubStatus") -and ($control.AttestedSubStatus)) #{ #$AzSDKReportTable.Cell($x,2).Range.Text= "Actual Status : " + $control.ActualStatus #$attstionDescription = "Attestation Description`vAttested Status: " + $control.AttestedSubStatus + "`vAttested By: " + $control.AttestedBy + "`vJustification: " + $control.AttesterJustification #$AzSDKReportTable.Cell($x,6).Range.Text = $attstionDescription #$AzSDKReportTable.Cell($x,6).Range.Font.Size = 9 #$x++; # } } $AzSDKReportTable.Style = 'Grid Table 4 - Accent 1' $AzSDKReportTable.Columns.Autofit() $selection = $Word.Selection $selection.WholeStory $selection.Style = "No Spacing" $wdStory = 6 $wdMove = 0 $ret = $selection.EndKey($wdStory, $wdMove) $selection.TypeParagraph() $selection.InsertBreak(7) } #end region #region -> Adding PowerShell output Get-ChildItem -Path $reportFolderPath -Directory | Where-Object {($_.Name -eq "etc")} | %{ $rootfolder = $_ [AzSDKPDFExtension]::WriteText($selection, 'PowerShell Output','Heading 1', $false, $true, $false) Get-ChildItem -Path $rootfolder.FullName -Recurse -Filter "PowerShellOutput.log" | %{ $logfilepath = $_ $log = Get-Content $logfilepath.FullName | Out-String [AzSDKPDFExtension]::WriteText($selection, $log,'No Spacing', $false, $true, $false) $selection.TypeText("#################################################################"); $selection.TypeParagraph() } } $selection.InsertBreak(7) #end region -> Adding PowerShell output #region -> Adding detailed logs [AzSDKPDFExtension]::WriteText($selection, 'Detailed Output','Heading 1', $false, $true, $false) $selection.InsertBreak(6) Get-ChildItem -Path $reportFolderPath -Directory | Where-Object {-not ($_.Name -eq "etc")} | %{ $rootfolder = $_ if($isSubscriptionCore) { [AzSDKPDFExtension]::WriteText($selection, 'Subscription Name: '+ ($rootfolder.Name),'Heading 2', $false, $true, $false) } else { [AzSDKPDFExtension]::WriteText($selection, 'Resource Group Name: ' + ($rootfolder.Name),'Heading 2', $false, $true, $false) } Get-ChildItem -Path $rootfolder.FullName -Recurse -Filter "*.log" | %{ $logfilepath = $_ [AzSDKPDFExtension]::WriteText($selection, 'Resource Type: ' + ($logfilepath.BaseName),'Heading 3', $false, $true, $false) $logs = Get-Content $logfilepath.FullName ForEach($log in $logs) { [AzSDKPDFExtension]::WriteText($selection, ($log | Out-String),'No Spacing', $false, $false, $false) } $selection.TypeParagraph() $selection.InsertBreak(7) } } #end region # Update table of content $toc.Update() } } catch { throw $_.Exception } finally { $wdExportFormatPDF = 17 $wdDoNotSaveChanges = 0 $AzSDKReportDoc.ExportAsFixedFormat($pdfPath,$wdExportFormatPDF) $AzSDKReportDoc.close([ref]$wdDoNotSaveChanges) $Word.Quit() if (test-path variable:AzSDKReportDoc) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSDKReportDoc) | Out-Null } if (test-path variable:word) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null } if (test-path variable:range) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($range) | Out-Null } if (test-path variable:ReportRange) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ReportRange) | Out-Null } if (test-path variable:AzSDKReportTable) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSDKReportTable) | Out-Null } if (test-path variable:TitleTableRange) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($TitleTableRange) | Out-Null } if (test-path variable:AzSDKTitleTable) { [System.Runtime.Interopservices.Marshal]::ReleaseComObject($AzSDKTitleTable) | Out-Null } Remove-Variable range [gc]::collect() [gc]::WaitForPendingFinalizers() } } else { throw 'You must have Microsoft Word application installed on machine to generate PDF report.' } } static [void] WriteText([PSObject] $selectionObj, [string] $textToWrite, [string] $style, [bool] $bold, [bool] $newParagraph, [bool] $newLine) { $selectionObj.TypeText($textToWrite); $selectionObj.Style = $style if($bold) { $selectionObj.Range.Font.Bold = 1 } else { $selectionObj.Range.Font.Bold = 0 } if($newParagraph) { $selectionObj.TypeParagraph() } if($newLine) { $selectionObj.TypeText("`v"); } $selectionObj.WholeStory $selectionObj.Style = "No Spacing" } static [void] WriteHeaderTableCell([PSObject] $tableObj, [int] $row, [string] $title, [string] $value) { $tableObj.Cell($row,1).Range.Text = $title $tableObj.Cell($row,1).Range.Bold = 1 $tableObj.Cell($row,2).Range.Text = $value } } # SIG # Begin signature block # MIIkBwYJKoZIhvcNAQcCoIIj+DCCI/QCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCUBt6Lrs/yHQVD # RDInh0k4hGNedVoR1/GiMKS4jVpoCaCCDZMwggYRMIID+aADAgECAhMzAAAAjoeR # pFcaX8o+AAAAAACOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTYxMTE3MjIwOTIxWhcNMTgwMjE3MjIwOTIxWjCBgzEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9Q # UjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMIIBIjANBgkqhkiG9w0B # AQEFAAOCAQ8AMIIBCgKCAQEA0IfUQit+ndnGetSiw+MVktJTnZUXyVI2+lS/qxCv # 6cnnzCZTw8Jzv23WAOUA3OlqZzQw9hYXtAGllXyLuaQs5os7efYjDHmP81LfQAEc # wsYDnetZz3Pp2HE5m/DOJVkt0slbCu9+1jIOXXQSBOyeBFOmawJn+E1Zi3fgKyHg # 78CkRRLPA3sDxjnD1CLcVVx3Qv+csuVVZ2i6LXZqf2ZTR9VHCsw43o17lxl9gtAm # +KWO5aHwXmQQ5PnrJ8by4AjQDfJnwNjyL/uJ2hX5rg8+AJcH0Qs+cNR3q3J4QZgH # uBfMorFf7L3zUGej15Tw0otVj1OmlZPmsmbPyTdo5GPHzwIDAQABo4IBgDCCAXww # HwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0OBBYEFKvI1u2y # FdKqjvHM7Ww490VK0Iq7MFIGA1UdEQRLMEmkRzBFMQ0wCwYDVQQLEwRNT1BSMTQw # MgYDVQQFEysyMzAwMTIrYjA1MGM2ZTctNzY0MS00NDFmLWJjNGEtNDM0ODFlNDE1 # ZDA4MB8GA1UdIwQYMBaAFEhuZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEsw # SaBHoEWGQ2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0Nv # ZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsG # AQUFBzAChkVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y0NvZFNpZ1BDQTIwMTFfMjAxMS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkq # hkiG9w0BAQsFAAOCAgEARIkCrGlT88S2u9SMYFPnymyoSWlmvqWaQZk62J3SVwJR # avq/m5bbpiZ9CVbo3O0ldXqlR1KoHksWU/PuD5rDBJUpwYKEpFYx/KCKkZW1v1rO # qQEfZEah5srx13R7v5IIUV58MwJeUTub5dguXwJMCZwaQ9px7eTZ56LadCwXreUM # tRj1VAnUvhxzzSB7pPrI29jbOq76kMWjvZVlrkYtVylY1pLwbNpj8Y8zon44dl7d # 8zXtrJo7YoHQThl8SHywC484zC281TllqZXBA+KSybmr0lcKqtxSCy5WJ6PimJdX # jrypWW4kko6C4glzgtk1g8yff9EEjoi44pqDWLDUmuYx+pRHjn2m4k5589jTajMW # UHDxQruYCen/zJVVWwi/klKoCMTx6PH/QNf5mjad/bqQhdJVPlCtRh/vJQy4njpI # BGPveJiiXQMNAtjcIKvmVrXe7xZmw9dVgh5PgnjJnlQaEGC3F6tAE5GusBnBmjOd # 7jJyzWXMT0aYLQ9RYB58+/7b6Ad5B/ehMzj+CZrbj3u2Or2FhrjMvH0BMLd7Hald # G73MTRf3bkcz1UDfasouUbi1uc/DBNM75ePpEIzrp7repC4zaikvFErqHsEiODUF # he/CBAANa8HYlhRIFa9+UrC4YMRStUqCt4UqAEkqJoMnWkHevdVmSbwLnHhwCbww # ggd6MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv # ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5 # MDlaFw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIw # MTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQ # TTS68rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULT # iQ15ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYS # L+erCFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494H # DdVceaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZ # PrGMXeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5 # bmR/U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGS # rhwjp6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADh # vKwCgl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON # 7E1JMKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xc # v3coKPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqw # iBfenk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMC # AQAwHQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQM # HgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud # IwQYMBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0 # dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0Nl # ckF1dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUF # BzAChkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0Nl # ckF1dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGC # Ny4DMIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # b3BzL2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcA # YQBsAF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZI # hvcNAQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4s # PvjDctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKL # UtCw/WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7 # pKkFDJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft # 0N3zDq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4 # MnEnGn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxv # FX1Fp3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG # 0QaxdR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf # 0AApxbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkY # S//WsyNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrv # QQqxP/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIV # yjCCFcYCAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAA # AI6HkaRXGl/KPgAAAAAAjjANBglghkgBZQMEAgEFAKCBtjAZBgkqhkiG9w0BCQMx # DAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkq # hkiG9w0BCQQxIgQgZKI9T2DWIZ5N/e/kWDrjskVaR4zwNAsAvoq+HXjVvoUwSgYK # KwYBBAGCNwIBDDE8MDqgGIAWAEEAegBTAEQASwAgADIALgA1AC4AMaEegBxodHRw # czovL2FrYS5tcy9henNka29zc2RvY3MgMA0GCSqGSIb3DQEBAQUABIIBAKAYuIDs # ORYFN8D5BsnLwBcUdbJAaU1ShQ2RaBKluKo5JRNQWy3NlwCePLMpZ+cqfat7Yrqg # CUVgWaijM6eLg3nV1Olxky8QXXK34S//zRAzOI7OkgzQgqRL87Xzzx014B0O9dYJ # XCOqUe7gNsLtDwJ9gi5aoiigN+JhL74Rn/SOzVb8oBrjCh18YvcJmvHKDVpDzuoE # RzEOSnOoRxe1g7NYJzLG9dKGqDZUccsYKk7eaYCAjeDwDuqgyD8pzdn59bwYWag/ # vPabJEXERwZESXjwyDIri8OLyP6KVPcwozzjmyYYL5xcZEntW18IAw357fbOE7Vb # 9CMsoelLanF4bYShghNMMIITSAYKKwYBBAGCNwMDATGCEzgwghM0BgkqhkiG9w0B # BwKgghMlMIITIQIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBPQYLKoZIhvcNAQkQAQSg # ggEsBIIBKDCCASQCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEFAAQgZshI # 6y0dCnrnA5wNSLqYkZNZ1R4KwcDXtTl7vqrJreICBlmSFyGg/xgTMjAxNzA5MDUw # NzAzNDguMTAxWjAHAgEBgAIB9KCBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhl # ciBEU0UgRVNOOkIxQjctRjY3Ri1GRUMyMSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt # ZS1TdGFtcCBTZXJ2aWNloIIOzzCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJ # KoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 # eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIxNDY1NVowfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF++18aEssX8XD5WHCdrc+ # Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRDDNdNuDgIs0Ldk6zWczBX # JoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSxz5NMksHEpl3RYRNuKMYa # +YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1rL2KQk1AUdEPnAY+Z3/1 # ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16HgcsOmZzTznL0S6p/TcZL2k # AcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB4jAQBgkrBgEEAYI3FQEE # AwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqFbVUwGQYJKwYBBAGCNxQC # BAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYD # VR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZF # aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9v # Q2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcw # AoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJB # dXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4D # MIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2Rv # Y3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABf # AFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEB # CwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUxvs8F4qn++ldtGTCzwsVm # yWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GASinbMQEBBm9xcF/9c+V4X # NZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1L3mBZdmptWvkx872ynoA # b0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWOM7tiX5rbV0Dp8c6ZZpCM # /2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4pm3S4Zz5Hfw42JT0xqUK # loakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45V3aicaoGig+JFrphpxHL # mtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x4QDf5zEHpJM692VHeOj4 # qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEegPsbiSpUObJb2sgNVZl6 # h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKnQqLJzxlBTeCG+SqaoxFm # MNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp3lfB0d4wwP3M5k37Db9d # T+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvTX4/edIhJEjCCBNowggPC # oAMCAQICEzMAAACxcRN533X2NcgAAAAAALEwDQYJKoZIhvcNAQELBQAwfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTYwOTA3MTc1NjU3WhcNMTgwOTA3 # MTc1NjU3WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEN # MAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkIxQjctRjY3 # Ri1GRUMyMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIB # IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqQklG1Y1lu8ob0P7deumuRn # 4JvRi2GErmK94vgbnWPmd0j/9arA7539HD1dpG1uhYbmnAxc+qsuvMM0fpEvttTK # 4lZSU7ss5rJfWmbFn/J8kSGI8K9iBaB6hQkJuIX4si9ppNr9R3oZI3HbJ/yRkKUP # k4hozpY6CkehRc0/Zfu6tQiyqI7mClXYZTXjw+rLsh3/gdBvYDd38zFBllaf+3ui # mKQgUTXGjbKfqZZk3tEU3ibWVPUxAmmxlG3sWTlXmU31fCw/6TVzGg251lq+Q46O # jbeH9vB2TOcqEso4Nai3J1CdMAYUdlelVVtgQdIx/c+5Hvrw0Y6W7uGBAWnW5wID # AQABo4IBGzCCARcwHQYDVR0OBBYEFE5XPfeLLhRLV7L8Il7Tz7cnRBA7MB8GA1Ud # IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0 # dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0 # YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG # Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB # XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH # AwgwDQYJKoZIhvcNAQELBQADggEBAHPujfu0W8PBTpjfYaPrAKIBLKcljT4+YnWb # bgGvmXU8OvIUDBkkv8gNGGHRO5DSySaCARIzgn2yIheAqh6GwM2yKrfb4eVCYPe1 # CTlCseS5TOv+Tn/95mXj+NxTqvuNmrhgCVr0CQ7b3xoKcwDcQbg7TmerDgbIv2k7 # cEqbYbU/B3MtSX8Zjjf0ZngdKoX0JYkAEDbZchOrRiUtDJItegPKZPf6CjeHYjrm # KwvTOVCzv3lW0uyh1yb/ODeRH+VqENSHCboFiEiq9KpKMOpek1VvQhmI2KbTlRvK # 869gj1NwuUHH8c3WXu4A0X1+CBmU8t0gvd/fFlQvw04veKWh986hggN4MIICYAIB # ATCB46GBuaSBtjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOkIxQjct # RjY3Ri1GRUMyMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl # oiUKAQEwCQYFKw4DAhoFAAMVADq635MoZeR60+ej9uKnRG5YqlPSoIHCMIG/pIG8 # MIG5MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMQ0wCwYDVQQL # EwRNT1BSMScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046NERFOS0wQzVFLTNFMDkx # KzApBgNVBAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBNYXN0ZXIgQ2xvY2swDQYJ # KoZIhvcNAQEFBQACBQDdWK0XMCIYDzIwMTcwOTA1MDQ1NjU1WhgPMjAxNzA5MDYw # NDU2NTVaMHYwPAYKKwYBBAGEWQoEATEuMCwwCgIFAN1YrRcCAQAwCQIBAAIBIAIB # /zAHAgEAAgIY4zAKAgUA3Vn+lwIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEE # AYRZCgMBoAowCAIBAAIDFuNgoQowCAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IB # AQA+ZEd4ooPHiKXaGoIJl7xct64OrgoneiQ1Q/WdfJxEl3cjN5B2vRUuQX+6hq5k # nZBRNSvQhFHIumBL52FqmEl0yu4JrEcavJhNtK+rZYIdiYW/IJN+eYjR7Ca7UiPn # qC0AovLCq7CH9Pa2LZrFMTw3OEfjCa5O4ZZ2eVaBP0Ts30GgU+BVK7RQ1OnnV3rR # L3vwFs9m4RHh92aifGO3xwvmONclucmsupzvizd0YzkG5kFkIj38HaPpRKiR+/z1 # e1s/qQ5W90R5AVKCgBS/r47n+pjTrHtHKX65tYUnd1FCED51VBM/yI372NQ/ts1n # cTv5dpINp/KxiiIUcI+/YNj1MYIC9TCCAvECAQEwgZMwfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAACxcRN533X2NcgAAAAAALEwDQYJYIZIAWUDBAIB # BQCgggEyMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx # IgQgtuKUjexu8k9IO/O702WiS1gcnvh/CnpE1pJwMydLspAwgeIGCyqGSIb3DQEJ # EAIMMYHSMIHPMIHMMIGxBBQ6ut+TKGXketPno/bip0RuWKpT0jCBmDCBgKR+MHwx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1p # Y3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAsXETed919jXIAAAAAACx # MBYEFF50WhcZSLtM19KqNcuqdWzh7UwDMA0GCSqGSIb3DQEBCwUABIIBAGbbC6hA # +dpR8cFCn9hSUl9vNH/1oxYk/q0ROKecaLkEohMv7N1rVUQlsXdDBKjrHXsNO5O+ # aDVfslEyTr6YM9DNz6tRbWU+Eeih4rEf+n2ZVitq4BHxfSL5km2eYR8DDYjWLQmH # LPmSUynysHoOi94i2XEHrULi1ngvR4WAZYeCi165B/7IaXtMOWqgpzBbWJZrGqvL # BeMWuKGVT/g9TtYVl6fTVvHD8kF3Ss/TNNPxRVoQBVXcEXPEWYugj+P5y8om/6mh # 6sMzXOMt2STdL0kK0w+JoFm6AtU/c/jrspxuvN9cu4/zZMfE41RRiU7V+0dMx1SC # rNB6j+4Fr7iqrRU= # SIG # End signature block |