From 28004dc82ecb47f31f551d55c4f2594dd63c248b Mon Sep 17 00:00:00 2001 From: Artur Oliveira Date: Sun, 21 Jul 2024 16:49:29 +0100 Subject: [PATCH 1/2] Improved formatting, messaging and performance --- Usage/AppService/Common.ps1 | 307 +++++++++--------- .../Get-AppServiceBillingRecords.ps1 | 99 +++--- 2 files changed, 205 insertions(+), 201 deletions(-) diff --git a/Usage/AppService/Common.ps1 b/Usage/AppService/Common.ps1 index cb1e11a9..16050f75 100644 --- a/Usage/AppService/Common.ps1 +++ b/Usage/AppService/Common.ps1 @@ -13,182 +13,183 @@ -TenantUsage $TenantUsage #> -function Get-AppServiceBillingRecords -{ - Param - ( - [Parameter(Mandatory = $true)] - [datetime] - $StartTime, +function Get-AppServiceBillingRecords { + Param + ( + [Parameter(Mandatory = $true)] + [datetime] + $StartTime, - [Parameter(Mandatory = $true)] - [datetime] - $EndTime , + [Parameter(Mandatory = $true)] + [datetime] + $EndTime , - [Parameter(Mandatory = $false)] - [ValidateSet("Hourly", "Daily")] - [String] - $Granularity = 'Hourly', + [Parameter(Mandatory = $false)] + [ValidateSet("Hourly", "Daily")] + [String] + $Granularity = 'Hourly', - [Parameter(Mandatory = $false)] - [bool] - $TenantUsage = $true - ) + [Parameter(Mandatory = $false)] + [bool] + $TenantUsage = $true + ) - $CsvFile = "AppServiceUsageSummary.csv" - - <# + <# This is the meter used by Microsoft to bill App Service on Azure Stack. For example - for Pay as you use model, we charge $0.056/vCPU/hour ($42/vCPU/month) but we generate billing records for every minute, so if you are not using the machine the full hour, you will be charged for a fraction of it. We only generate billing records only for web worker machines and not for other machines which are of the service. the price can change, please review https://docs.microsoft.com/en-us/azure/azure-stack/azure-stack-usage-related-faq #> - $AppServiceAdminMeters = @{ - '190c935e-9ada-48ff-9ab8-56ea1cf9adaa' = @{ - name = 'App Service virtual core hours' - price = 0.056 - } + $AppServiceAdminMeters = @{ + '190c935e-9ada-48ff-9ab8-56ea1cf9adaa' = @{ + name = 'App Service virtual core hours' + price = 0.056 } + } - <# + <# The following meters can be used to bill to the tenants if using the default App Service Worker Tiers. For this example, the prices are IMPORTANT: For the custom App Service Worker Tier, each worker tier will have a unique dynamically generated identifier. Please refer the usage report -> Identify the meter id generated for your custom worker tier and add to the table below. #> - $AppServiceTenantMeters = @{ - '957e9f36-2c14-45a1-b6a1-1723ef71a01d' = @{ - name = 'Shared App Service Hours' - price = 0.009 - } - '539cdec7-b4f5-49f6-aac4-1f15cff0eda9' = @{ - name = 'Free App Service Hours' - price = 0.003 - } - 'db658d61-ef2d-4888-9843-72f5c774fd3c' = @{ - name = 'Small Basic App Service Hours' - price = 1 - } - '27b01104-e0df-4f30-a171-f1b00ecb76b3' = @{ - name = 'Medium Basic App Service Hours' - price = 1 - } - '50db6a92-5dff-4c9b-8238-8ea5fb1be107' = @{ - name = 'Large Basic App Service Hours' - price = 1 - } - '88039d51-a206-3a89-e9de-c5117e2d10a6' = @{ - name = 'Small Standard App Service Hours' - price = 0.01 - } - '83a2a13e-4788-78dd-5d55-2831b68ed825' = @{ - name = 'Medium Standard App Service Hours' - price = 0.02 - } - '1083b9db-e9bb-24be-a5e9-d6fdd0ddefe6' = @{ - name = 'Large Standard App Service Hours' - price = 0.03 - } - '26bd6580-c3bd-4e7e-8092-58b28eb1bb94' = @{ - name = 'Small Premium App Service Hours' - price = 1 - } - 'a1cba406-e83e-45c3-bd36-485191c215d9' = @{ - name = 'Medium Premium App Service Hours' - price = 1 - } - 'a2104a9d-5a78-4f8f-a2df-034bd43d602d' = @{ - name = 'Large Premium App Service Hours' - price = 1 - } - 'a91eed6c-dbbc-4532-859c-86de776433a4' = @{ - name = 'Extra Large Premium App Service Hours' - price = 1 - } - '73215a6c-fa54-4284-b9c1-7e8ec871cc5b' = @{ - name = 'Web Process' - price = 0.002 - } - '5887d39b-0253-4e12-83c7-03e1a93dffd9' = @{ - name = 'External Egress Bandwidth' - price = 1 - } - '264acb47-ad38-47f8-add3-47f01dc4f473' = @{ - name = 'SNI SSL' - price = 1 - } - '60b42d72-dc1c-472c-9895-6c516277edb4' = @{ - name = 'IP SSL' - price = 1 - } - 'd1d04836-075c-4f27-bf65-0a1130ec60ed' = @{ - name = 'Functions Compute' - price = 1 - } - '67cc4afc-0691-48e1-a4b8-d744d1fedbde' = @{ - name = 'Functions Requests' - price = 1 - } - } - - #build usage uri and set meters - if ($TenantUsage) - { - $usageResourceType = "Microsoft.Commerce.Admin/locations/subscriberUsageAggregates" - $meters = $AppServiceTenantMeters + $AppServiceTenantMeters = @{ + '957e9f36-2c14-45a1-b6a1-1723ef71a01d' = @{ + name = 'Shared App Service Hours' + price = 0.009 } - else - { - $usageResourceType = "Microsoft.Commerce/locations/UsageAggregates" - $meters = $AppServiceAdminMeters + '539cdec7-b4f5-49f6-aac4-1f15cff0eda9' = @{ + name = 'Free App Service Hours' + price = 0.003 } - - $params = @{ - ResourceName = '../' - ResourceType = $usageResourceType - ApiVersion = "2015-06-01-preview" - ODataQuery = "reportedStartTime={0:s}&reportedEndTime={1:s}&showDetails=true&aggregationGranularity={2}" -f $StartTime, $EndTime, $Granularity + 'db658d61-ef2d-4888-9843-72f5c774fd3c' = @{ + name = 'Small Basic App Service Hours' + price = 1 + } + '27b01104-e0df-4f30-a171-f1b00ecb76b3' = @{ + name = 'Medium Basic App Service Hours' + price = 1 + } + '50db6a92-5dff-4c9b-8238-8ea5fb1be107' = @{ + name = 'Large Basic App Service Hours' + price = 1 + } + '88039d51-a206-3a89-e9de-c5117e2d10a6' = @{ + name = 'Small Standard App Service Hours' + price = 0.01 + } + '83a2a13e-4788-78dd-5d55-2831b68ed825' = @{ + name = 'Medium Standard App Service Hours' + price = 0.02 + } + '1083b9db-e9bb-24be-a5e9-d6fdd0ddefe6' = @{ + name = 'Large Standard App Service Hours' + price = 0.03 + } + '26bd6580-c3bd-4e7e-8092-58b28eb1bb94' = @{ + name = 'Small Premium App Service Hours' + price = 1 + } + 'a1cba406-e83e-45c3-bd36-485191c215d9' = @{ + name = 'Medium Premium App Service Hours' + price = 1 + } + 'a2104a9d-5a78-4f8f-a2df-034bd43d602d' = @{ + name = 'Large Premium App Service Hours' + price = 1 } + 'a91eed6c-dbbc-4532-859c-86de776433a4' = @{ + name = 'Extra Large Premium App Service Hours' + price = 1 + } + '73215a6c-fa54-4284-b9c1-7e8ec871cc5b' = @{ + name = 'Web Process' + price = 0.002 + } + '5887d39b-0253-4e12-83c7-03e1a93dffd9' = @{ + name = 'External Egress Bandwidth' + price = 1 + } + '264acb47-ad38-47f8-add3-47f01dc4f473' = @{ + name = 'SNI SSL' + price = 1 + } + '60b42d72-dc1c-472c-9895-6c516277edb4' = @{ + name = 'IP SSL' + price = 1 + } + 'd1d04836-075c-4f27-bf65-0a1130ec60ed' = @{ + name = 'Functions Compute' + price = 1 + } + '67cc4afc-0691-48e1-a4b8-d744d1fedbde' = @{ + name = 'Functions Requests' + price = 1 + } + } - $result = Get-AzureRmResource @params -ErrorVariable RestError -Verbose + #build usage uri and set meters + if ($TenantUsage) { + $usageResourceType = "Microsoft.Commerce.Admin/locations/subscriberUsageAggregates" + $meters = $AppServiceTenantMeters + } + else { + $usageResourceType = "Microsoft.Commerce/locations/UsageAggregates" + $meters = $AppServiceAdminMeters + } - if ($RestError) - { - return - } + $params = @{ + ResourceName = '../' + ResourceType = $usageResourceType + ApiVersion = "2015-06-01-preview" + ODataQuery = "reportedStartTime={0:s}&reportedEndTime={1:s}&showDetails=true&aggregationGranularity={2}" -f $StartTime, $EndTime, $Granularity + } + + "Fetching Usage Records from ARM endpoint ..." | Write-Host -ForegroundColor Cyan + $result = Get-AzResource @params -ErrorVariable RestError -Verbose + + if ($RestError) { + return + } + + "Fetched {0} Usage Records" -f $result.count | Write-Host -ForegroundColor Green - $usageSummary = @() - $count = $result.Count - $result | ForEach-Object { - if ($meters.ContainsKey($_.Properties.MeterId)) - { - $record = New-Object -TypeName System.Object - $resourceInfo = ($_.Properties.InstanceData | ConvertFrom-Json).'Microsoft.Resources' - $resourceText = $resourceInfo.resourceUri.Replace('\', '/') - $subscription = $resourceText.Split('/')[2] - $resourceType = $resourceText.Split('/')[7] - $resourceName = $resourceText.Split('/')[8] - #$record | Add-Member -Name Name -MemberType NoteProperty -Value $_.Name - #$record | Add-Member -Name Type -MemberType NoteProperty -Value $_.Type - $record | Add-Member -Name MeterId -MemberType NoteProperty -Value $_.Properties.MeterId - $record | Add-Member -Name MeterName -MemberType NoteProperty -Value $meters[$_.Properties.MeterId].name - $record | Add-Member -Name Quantity -MemberType NoteProperty -Value ([decimal]($_.Properties.Quantity)) -ErrorAction Continue - $record | Add-Member -Name UnitPrice -MemberType NoteProperty -Value $meters[$_.Properties.MeterId].price - $record | Add-Member -Name Amount -MemberType NoteProperty -Value ($meters[$_.Properties.MeterId].price * [decimal]($_.Properties.Quantity)) -ErrorAction Continue - $record | Add-Member -Name UsageStartTime -MemberType NoteProperty -Value $_.Properties.UsageStartTime - $record | Add-Member -Name UsageEndTime -MemberType NoteProperty -Value $_.Properties.UsageEndTime - $record | Add-Member -Name additionalInfo -MemberType NoteProperty -Value $resourceInfo.additionalInfo - $record | Add-Member -Name location -MemberType NoteProperty -Value $resourceInfo.location - $record | Add-Member -Name tags -MemberType NoteProperty -Value $resourceInfo.tags - $record | Add-Member -Name subscription -MemberType NoteProperty -Value $_.Properties.subscriptionId - $record | Add-Member -Name resourceType -MemberType NoteProperty -Value $resourceType - $record | Add-Member -Name resourceName -MemberType NoteProperty -Value $resourceName - $record | Add-Member -Name resourceUri -MemberType NoteProperty -Value $resourceText - $usageSummary += $record - } + "Processing Usage Records ..." | Write-Host -ForegroundColor Cyan + $usageSummary = [System.Collections.Generic.List[System.Object]]::new() + $count = $result.Count + $index = 0 + $result | ForEach-Object { + $index++ + $status = "Processing Record $index of $count" + Write-Progress -Activity "Processing Usage Records" -Status $status -PercentComplete ($index / $count * 100) + if ($meters.ContainsKey($_.Properties.MeterId)) { + $record = New-Object -TypeName System.Object + $resourceInfo = ($_.Properties.InstanceData | ConvertFrom-Json).'Microsoft.Resources' + $resourceText = $resourceInfo.resourceUri.Replace('\', '/') + #$subscription = $resourceText.Split('/')[2] + $resourceType = $resourceText.Split('/')[7] + $resourceName = $resourceText.Split('/')[8] + #$record | Add-Member -Name Name -MemberType NoteProperty -Value $_.Name + #$record | Add-Member -Name Type -MemberType NoteProperty -Value $_.Type + $record | Add-Member -Name MeterId -MemberType NoteProperty -Value $_.Properties.MeterId + $record | Add-Member -Name MeterName -MemberType NoteProperty -Value $meters[$_.Properties.MeterId].name + $record | Add-Member -Name Quantity -MemberType NoteProperty -Value ([decimal]($_.Properties.Quantity)) -ErrorAction Continue + $record | Add-Member -Name UnitPrice -MemberType NoteProperty -Value $meters[$_.Properties.MeterId].price + $record | Add-Member -Name Amount -MemberType NoteProperty -Value ($meters[$_.Properties.MeterId].price * [decimal]($_.Properties.Quantity)) -ErrorAction Continue + $record | Add-Member -Name UsageStartTime -MemberType NoteProperty -Value $_.Properties.UsageStartTime + $record | Add-Member -Name UsageEndTime -MemberType NoteProperty -Value $_.Properties.UsageEndTime + $record | Add-Member -Name additionalInfo -MemberType NoteProperty -Value $resourceInfo.additionalInfo + $record | Add-Member -Name location -MemberType NoteProperty -Value $resourceInfo.location + $record | Add-Member -Name tags -MemberType NoteProperty -Value $resourceInfo.tags + $record | Add-Member -Name subscription -MemberType NoteProperty -Value $_.Properties.subscriptionId + $record | Add-Member -Name resourceType -MemberType NoteProperty -Value $resourceType + $record | Add-Member -Name resourceName -MemberType NoteProperty -Value $resourceName + $record | Add-Member -Name resourceUri -MemberType NoteProperty -Value $resourceText + $usageSummary.Add($record) | Out-Null } + } - return $usageSummary -} + return $usageSummary +} \ No newline at end of file diff --git a/Usage/AppService/Get-AppServiceBillingRecords.ps1 b/Usage/AppService/Get-AppServiceBillingRecords.ps1 index da4b9b6f..fa5f7127 100644 --- a/Usage/AppService/Get-AppServiceBillingRecords.ps1 +++ b/Usage/AppService/Get-AppServiceBillingRecords.ps1 @@ -12,67 +12,70 @@ *****To get current subscription data use (-TenantUsage $false) **** .\Get-AppServiceBillingRecords.ps1 -StartTime 01/08/2018 -EndTime 01/24/2018 -Granularity Daily -TenantUsage $false #> - [CmdletBinding()] - Param - ( - [Parameter(Mandatory = $true)] - [datetime] - $StartTime, +[CmdletBinding()] +Param +( + [Parameter(Mandatory = $true)] + [datetime] + $StartTime, - [Parameter(Mandatory = $true)] - [datetime] - $EndTime , + [Parameter(Mandatory = $true)] + [datetime] + $EndTime , - [Parameter(Mandatory = $false)] - [ValidateSet("Hourly", "Daily")] - [String] - $Granularity = 'Hourly', + [Parameter(Mandatory = $false)] + [ValidateSet("Hourly", "Daily")] + [String] + $Granularity = 'Hourly', - [Parameter(Mandatory = $false)] - [bool] - $TenantUsage = $true, + [Parameter(Mandatory = $false)] + [bool] + $TenantUsage = $true, - [Parameter(Mandatory = $false)] - [bool] - $ExportToCSV = $false - ) + [Parameter(Mandatory = $false)] + [bool] + $ExportToCSV = $false +) - $VerbosePreference = 'Continue' +$VerbosePreference = 'Continue' - # Load common functions - . "$PSScriptRoot\Common.ps1" +# Load common functions +. "$PSScriptRoot\Common.ps1" - # Main - $CsvFile = "AppServiceUsageSummary.csv" +# Main +if ($TenantUsage) { + $type = "Tenant" +} +else { + $type = "Admin" +} +$CsvFile = "AppServiceUsageSummary_{0}_{1}.csv" -f $type, (Get-Date).ToString("yyyy-MM-dd_HH-mm-ss") - $usageSummary = Get-AppServiceBillingRecords ` - -StartTime $StartTime ` - -EndTime $EndTime ` - -Granularity $Granularity ` - -TenantUsage $TenantUsage +$usageSummary = Get-AppServiceBillingRecords ` + -StartTime $StartTime ` + -EndTime $EndTime ` + -Granularity $Granularity ` + -TenantUsage $TenantUsage - if (!$ExportToCSV) - { - Write-Output $usageSummary | Format-Table -AutoSize +if (!$ExportToCSV) { + Write-Output $usageSummary | Format-Table -AutoSize - Write-Host "Complete - billing records" $usageSummary.Count + Write-Host "Complete - billing records" $usageSummary.Count - return - } + return +} - #Export to CSV - if (Test-Path -Path $CsvFile -ErrorAction SilentlyContinue) - { - Remove-Item -Path $CsvFile -Force - } +#Export to CSV +if (Test-Path -Path $CsvFile -ErrorAction SilentlyContinue) { + Remove-Item -Path $CsvFile -Force +} - New-Item -Path $CsvFile -ItemType File | Out-Null +New-Item -Path $CsvFile -ItemType File | Out-Null - $usageSummary | Export-Csv -Path $CsvFile -Append -NoTypeInformation +$usageSummary | Export-Csv -Path $CsvFile -Append -NoTypeInformation - if ($PSBoundParameters.ContainsKey('Debug')) - { - $result | Export-Csv -Path "$CsvFile.raw" -Append -NoTypeInformation - } +if ($PSBoundParameters.ContainsKey('Debug')) { + $result | Export-Csv -Path "$CsvFile.raw" -Append -NoTypeInformation +} - Write-Host "Complete - billing records written to $CsvFile " $usageSummary.Count \ No newline at end of file +Write-Host "Complete - billing records written to $CsvFile " $usageSummary.Count \ No newline at end of file From db89994e4aec914237f0745668e6dc8b31d46eb7 Mon Sep 17 00:00:00 2001 From: Artur Oliveira Date: Sun, 21 Jul 2024 17:07:16 +0100 Subject: [PATCH 2/2] Update Get-AzsSSLEndPoints.ps1 to include ACR and SQL RPs and only optionally check all ports Also improved reused certificates check logic --- Support/scripts/Get-AzsSSLEndPoints.ps1 | 287 ++++-------------------- 1 file changed, 41 insertions(+), 246 deletions(-) diff --git a/Support/scripts/Get-AzsSSLEndPoints.ps1 b/Support/scripts/Get-AzsSSLEndPoints.ps1 index 8435f900..e81fe426 100644 --- a/Support/scripts/Get-AzsSSLEndPoints.ps1 +++ b/Support/scripts/Get-AzsSSLEndPoints.ps1 @@ -59,15 +59,29 @@ param ( $exclude, [Parameter(Mandatory = $false, HelpMessage = "Return PSObject")] [switch] - $PassThru + $PassThru, + [Parameter(Mandatory = $false, HelpMessage = "Include all ports")] + [switch] + $AllPorts ) # endpoint data -$allendPoints = @( - 443,12495,12499,12646,12647,12648,12649,12650,13001,13003,13010,13011,13012,13020,13021,13026,30015 | ForEach-Object {"adminportal.{0}:{1}" -f $FQDN, $PSITEM} - 443,12495,12649,13001,13010,13011,13012,13020,13021,30015,13003 | ForEach-Object {"portal.{0}:{1}" -f $FQDN, $PSITEM} - 443,30024 | Foreach-Object {"management.{0}:{1}" -f $FQDN, $PSITEM} - 443,30024 | Foreach-Object {"adminmanagement.{0}:{1}" -f $FQDN, $PSITEM} +if(!$AllPorts) { + $allendPoints = @( + 443 | ForEach-Object {"adminportal.{0}:{1}" -f $FQDN, $PSITEM} + 443 | ForEach-Object {"portal.{0}:{1}" -f $FQDN, $PSITEM} + 443 | Foreach-Object {"management.{0}:{1}" -f $FQDN, $PSITEM} + 443 | Foreach-Object {"adminmanagement.{0}:{1}" -f $FQDN, $PSITEM} + ) +} else { + $allendPoints = @( + 443,12495,12499,12646,12647,12648,12649,12650,13001,13003,13010,13011,13012,13020,13021,13026,30015 | ForEach-Object {"adminportal.{0}:{1}" -f $FQDN, $PSITEM} + 443,12495,12649,13001,13010,13011,13012,13020,13021,30015,13003 | ForEach-Object {"portal.{0}:{1}" -f $FQDN, $PSITEM} + 443,30024 | Foreach-Object {"management.{0}:{1}" -f $FQDN, $PSITEM} + 443,30024 | Foreach-Object {"adminmanagement.{0}:{1}" -f $FQDN, $PSITEM} + ) +} +$allendPoints += @( 443 | Foreach-Object {"{0}.queue.{1}:{2}" -f (new-guid),$FQDN,$PSITEM} 443 | Foreach-Object {"{0}.table.{1}:{2}" -f (new-guid),$FQDN,$PSITEM} 443 | Foreach-Object {"{0}.blob.{1}:{2}" -f (new-guid),$FQDN,$PSITEM} @@ -88,11 +102,13 @@ if ($UsePaaS) { $allendPoints += @( 44300 | ForEach-Object {"mysqladapter.dbadapter.{0}:{1}" -f $FQDN,$PSITEM} 44300 | ForEach-Object {"sqladapter.dbadapter.{0}:{1}" -f $FQDN,$PSITEM} + 44300 | ForEach-Object {"sqlrp.dbadapter.{0}:{1}" -f $FQDN,$PSITEM} "sso.appservice.$FQDN" - 443,8172 | ForEach-Object {"$(new-guid).appservice.{0}:{1}" -f $FQDN, $PSITEM} + 443 | ForEach-Object {"$(new-guid).appservice.{0}:{1}" -f $FQDN, $PSITEM} "$(new-guid).scm.appservice.$FQDN" "$(new-guid).sso.appservice.$FQDN" 443,44300 | ForEach-Object {"api.appservice.{0}:{1}" -f $FQDN, $PSITEM} + 443 | ForEach-Object {"$(new-guid).azsacr.{0}:{1}" -f $FQDN, $PSITEM} ) } @@ -140,18 +156,20 @@ function Get-ThumbprintMask function Test-CertificateReuse { param ($results) - $svcCount = ($results | Where-Object name -ne $null).count - $uniqueThumbprints = $results.thumbprint | Sort-Object | Get-Unique - if ($uniqueThumbprints.count -lt $svcCount) - { + + # Here we are looking for duplicate thumbprints that are used on more than one fqdn + if ($dups = $results | Where-Object { $null -ne $_.name -and $null -ne $_.thumbprint } | Group-Object thumbprint | Where-Object Count -gt 1 | Where-Object { $_.Group | Group-Object fqdn | Where-Object Count -eq 1 }) { Write-Host "`n" - Write-Warning -Message "Certificate Reuse Detected. We recommend using seperate certificates for each endpoint." - foreach ($uniqueThumbprint in $uniqueThumbprints) + Write-Warning -Message "Certificate Reuse Detected. We recommend using separate certificates for each endpoint." + foreach ($_dup in $dups) { - $names = ($results | Where-Object thumbprint -eq $uniqueThumbprint).name | Foreach-Object {$PSITEM.split(':')[0]} | Get-Unique - Write-Host ("`nCertificate {0} is configured for use on the following endpoint(s): " -f $uniqueThumbprint) - $names | Foreach-Object {Write-Host ("`t{0}" -f ($_.trimstart('testcert.')))} + $names = $_dup.Group.FQDN | Foreach-Object {$PSITEM.split(':')[0]} | Get-Unique + Write-Host ("`nCertificate {0} is configured for use on the following endpoint(s): " -f $_dup.name) + $names | Foreach-Object {Write-Host ("`t{0}" -f ($_ -replace 'testcert.'))} } + Write-Host "`n" + } else { + Write-Host "`nNo Certificate Reuse Detected." -ForegroundColor Green } } @@ -165,7 +183,7 @@ function Write-Table { process { foreach ($obj in $object) { Write-Host "`n" - foreach ($key in $obj.keys) { + foreach ($key in $obj.PSObject.Properties.Name) { Write-Host ("{0}: " -f $key).PadRight($padright) -NoNewline Write-Host (": " -f $key) -NoNewline if ($obj.State -like 'EXPIRING') { @@ -192,14 +210,15 @@ else { $endPoints = $allendPoints } -$results = @() +$results = New-Object System.Collections.ArrayList $i = 1 foreach ($endPoint in $endPoints) { [int]$percentageComplete = $i / $endPoints.count * 100 Write-Progress -Activity "Testing Azure Stack Endpoints" -Status "$percentageComplete% Complete:" -PercentComplete $percentageComplete -CurrentOperation $endPoint - $result = New-Object -TypeName PSCustomObject @{ + $result = New-Object -TypeName PSCustomObject -Property @{ Name = $endPoint + FQDN = $null Thumbprint = $null Subject = $null Expires = $null @@ -223,6 +242,7 @@ foreach ($endPoint in $endPoints) { $pattern = '(\{|\()?[A-Za-z0-9]{4}([A-Za-z0-9]{4}\-?){4}[A-Za-z0-9]{12}(\}|\()?' $endPoint = [regex]::replace($endPoint, $pattern, "testcert") $result.Name = $endPoint + $result.fqdn = ($endpoint -split ':')[0] if ($servicePoint.Certificate) { $result.Thumbprint = Get-ThumbprintMask -thumbprint $servicePoint.Certificate.GetCertHashString() $result.Expires = $servicePoint.Certificate.GetExpirationDateString() @@ -251,7 +271,7 @@ foreach ($endPoint in $endPoints) { $result.notes = "Cannot resolve name {0}" -f $endPoint } $i++ - $results += $result + $results.Add($result) | Out-Null } # if the PSEdition is desktop and the user doesn't use passthru colour-code the output in a table @@ -261,230 +281,5 @@ if ($PSEdition -eq 'Desktop' -AND -not $passThru) { Test-CertificateReuse -results $results } else { - $results | ForEach-Object { [pscustomobject] $_ } + $results } -# SIG # Begin signature block -# MIIppwYJKoZIhvcNAQcCoIIpmDCCKZQCAQExDzANBglghkgBZQMEAgEFADB5Bgor -# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG -# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDBTm7ZbNSRQJ5z -# iYxuecCkRI+5NBMwYoOeHYMLHs19rqCCDYEwggX/MIID56ADAgECAhMzAAABA14l -# HJkfox64AAAAAAEDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD -# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p -# bmcgUENBIDIwMTEwHhcNMTgwNzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQsw -# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u -# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy -# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -# AQDRlHY25oarNv5p+UZ8i4hQy5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU5 -# 3Rt+M2wR/Dsm85iqXVNrqsPsE7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94S -# nYW/fHaGfXKxdpth5opkTEbOttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjR -# hIjiATwi/K+WCMxdmcDt66VamJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8A -# DwH+S15UD8rEZT8K46NG2/YsAzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0 -# NIZ5rCrVXzCRO+QUauuxygQjAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE -# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8w -# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 -# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhu -# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu -# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w -# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 -# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx -# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJ -# Uw0/DSbsokTYDdGfY5YGSz8eXMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHn -# wya0rPUn00iThoj+EjWRZCLRay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjY -# DBu2KWAndjQkm925l3XLATutghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs -# 1JPyZ6vic8g9o838Mh5gHOmwGzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCy -# RhxdXnYqWJ/U7vL0+khMtWGLsIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuy -# bR/5WcF9ttE5yXnggxxgCto9sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpD -# MYLa2ISuubAfdfX2HX1RETcn6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy -# 2TUxuNty+rR1yIibar+YRcdmstf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v4 -# 6Mp79mwV+QMZDxGFqk+av6pX3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOla -# P470I3gwsvKmOMs1jaqYWSRAuDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6Iwh -# SkHC3uuOW+4Dwx1ubuZUNWZncnwa6lL2IsRyP64wggd6MIIFYqADAgECAgphDpDS -# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 -# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla -# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS -# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT -# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB -# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG -# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S -# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz -# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 -# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u -# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 -# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl -# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP -# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB -# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF -# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM -# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ -# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud -# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO -# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 -# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p -# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y -# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB -# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw -# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA -# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY -# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj -# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd -# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ -# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf -# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ -# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j -# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B -# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 -# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 -# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I -# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIbfDCCG3gCAQEwgZUwfjELMAkG -# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx -# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z -# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAQNeJRyZH6MeuAAAAAABAzAN -# BglghkgBZQMEAgEFAKCB3jAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor -# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQguUm1Upw8 -# sT9xuGKliUPZtKbcV8fC/0/vtJcRMqkXQPkwcgYKKwYBBAGCNwIBDDFkMGKgSIBG -# AE0AaQBjAHIAbwBzAG8AZgB0ACAAQQB6AHUAcgBlAFMAdABhAGMAawAgAFAAYQBy -# AHQAbgBlAHIAVABvAG8AbABrAGkAdKEWgBRodHRwOi8vQ29kZVNpZ25JbmZvIDAN -# BgkqhkiG9w0BAQEFAASCAQCXDjoCT21k9O1X3oBbo6u+JQ3KDvz80gTfkj3pGVwt -# kpZJ6sAWuThXURg3NPDKR46w/zupun6v7LjwX6pqhkF9IlG+y7a8jqunTqvGk8TC -# T/yFpCodMwNd1oci3/Pp70rOd3j/J7jKUCXo4KpxfMOwMWxPHdA/1C+p7aABBE4L -# if8Fly2OpAqCXMwG8f/eCUprjTFhqIzTOvWdaSQ0aC+OibHvfoFM3cO+L263qdbj -# Rw5f67tGbf67lzGZUiLg1Uuxoz0QMd5SoZ/Whuk204+UOJQwQ7F0uJYl6DDwHOcR -# Ls9ddEUpOqlQAUCBAaqmOL18nV5oPBAwpnqfCqDwqzq2oYIY1jCCGNIGCisGAQQB -# gjcDAwExghjCMIIYvgYJKoZIhvcNAQcCoIIYrzCCGKsCAQMxDzANBglghkgBZQME -# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB -# MDEwDQYJYIZIAWUDBAIBBQAEIF+19sCf86hrRPnEcyPu8fN6iuFNHQCDlrzngd6v -# CpyFAgZbhw4w3fIYEzIwMTgwODMxMTY0MzE3Ljc1N1owBIACAfSggdCkgc0wgcox -# CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG -# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg -# SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg -# RVNOOjhFOUUtNEJEMC0yRUQwMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt -# cCBzZXJ2aWNloIIULTCCBPEwggPZoAMCAQICEzMAAADDGu2K0g0+y1EAAAAAAMMw -# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 -# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh -# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN -# MTgwMTMxMTkwMDQ2WhcNMTgwOTA3MTkwMDQ2WjCByjELMAkGA1UEBhMCVVMxCzAJ -# BgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg -# Q29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlv -# bnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046OEU5RS00QkQwLTJF -# RDAxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIHNlcnZpY2UwggEiMA0G -# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCMN/dJFVWpHposmQoHrI9ldf5Uur+U -# 75SrN4UPt+QxlW/vKFylbLXNzfEitzbazmK4KQirtWWmLcBH4JqpjxThYpSjU87b -# mhStUQ7tpqziUWKA/GbYQSH4iZOq2TWY4QN4fsb39L3y7xQa062mpliGgwB5gnYm -# KPQ9Uwe+J6XIzKN7IKLGjPBncY4OoS4PjPUXC7n/yYgzcvkJDc4Wh/lYiZtnzjSc -# lVRjnf6MUnJng/SO4y9wBju6JN155idAMbBvnuJnZbtZZy3mR/pnX8CKnuuYjpSC -# 584MTsH+/qvaz4GJrknh3n9TwGeY5eoJBOwVIRdjZna9DuiqlrPcgisTAgMBAAGj -# ggEbMIIBFzAdBgNVHQ4EFgQUqKX8YULH7IWJxGWi+APSuTLftdowHwYDVR0jBBgw -# FoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov -# L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENB -# XzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0 -# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAx -# MC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDAN -# BgkqhkiG9w0BAQsFAAOCAQEAqAGHNw86RThfkGA9tVq9GyTu4FEDw9AW3dxSBWEU -# CY/CiKHVbV47tTC3pogDgH0MjYkET9rReUEWzpQcILRvnpz+xJS0Pa34Mrhf4e3B -# RAhXyWg2iA6QbRIBpdWgNZWDPT/B9oRzN9vUA6i1IoXLc4PFBMrB7TNpQLSPzFvl -# kgDu3S3FQmletMf09msZy8WJjoYJnRIEQRbfnqtIdJ37NQPmfmtJ7pUJdyLd6hXk -# eWwRtAmN6SbmFbKeIxZyGETtZQZpa2mQSg7nyEvpr82wBCYwqR+71xH+ECiMDJMZ -# 6fF7NXtywdcOvAA9Tpznl/oSEa78xBeEDqgt3zqgYhHUlDCCBe0wggPVoAMCAQIC -# ECjMOiW/ukSsRJqbWGtDOaowDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVT -# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK -# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290 -# IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDYyMzIxNTcyNFoXDTM1 -# MDYyMzIyMDQwMVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u -# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp -# b24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 -# eSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuQieKOTk7AZO -# UGizQcV76662jq+BuiJEH2U0aUy+cEAX8hZ74nn9hu0NOfQbqK2SkB7LPXaPWtm1 -# kRAuPAWNim0kVOcf7Vatg7RQnBWlF3SIWSD8CMWEdtNo1G8oeM5cuPNQkET/42Nf -# vqGaLJYVBNYH/h6EIeBCMRHEKDaUz1CkYp7J1qtxALJbDOaW1AoklvX/xtW3G9fL -# tyFirxLcoV034xr7GkaYwJvA52MfKgiTAn4eao7ynxiJ5CKForGEV0D/9Q7Yb5zt -# 4kUxAc0X6X+wgUXjqiFAJqFyqqdPPAEFfu6DWLFeBmOZYpF4grcNkwwkarQb2yfs -# X5UEP5NKMPWXGLOn+RmnkzMdAcjbIlJc1yXJRvmi+4dZQ76bYrGNLYZEGkaseGF+ -# MAn6ronEQSoiZgOROUWcx4sMqMoNL/tS6gz3YzMjnf6wH61n1qdQA8YEcGO1LLGG -# WkO3+675biluISFBJgaMycPusMKFk6G5hdnmMmxLTD/WXaPltZ13w5zAVbd0AOO4 -# OKuDl1DhmkIkHcbAozDRGlrIUjT3c/HHGB8zrXrsy0Fg8yOUIMJIRaxcUcYugMLi -# dxW9hYftNp2Wke4AtaNw7J/jjYBog3a6r11wUiIW4mb7urPFwvc+L3emyt7BpsZI -# TMM3USPTJ9e4TnCW8KFEdq94z5rhZhMCAwEAAaNRME8wCwYDVR0PBAQDAgGGMA8G -# A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNX2VsuP6KJcYmjRPZSQW9fOmhjEMBAG -# CSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBCwUAA4ICAQCspZaMv7uupvbXcYdD -# MVaI/RwycVs1t9TwkfKvN+IU8fMCJgU+FhR/FLq4T/uJsrLn1AnMbblbO2RlcGa3 -# 8rFa3xoC8/VRuGdtefO/VnvkhLkrHptAnCY0+UcYmGnYHNe20b+PYcJnxLXvYEOO -# EBs2SeQgyq2nwbEnZQn4zfVbKtCEM/PvH/L1nAtYkzegdaDect5sdSpmIvWMBjBW -# n0C5MKpAdxWC14vswNOyvYPFdwwerq8ZU6BNeXGfD68wzmf51izMIkF6B/KXQhjO -# WXkQVd5vEOS42oNmQBYJaCNbly4mmgK7V4zFuLppYjKAiZ6h/cCSfHsrMxmEKmPF -# AGhi+p9HjZl6RTqn6e3uaUK184GbR1YQe/xwNoQYc+rv+ZdNnjMj3SYLuiq3P0Tc -# gyf/vWFZKxG3yk/bxYsMHDGuMvj4uUL3f9xhmnaxWgThET1mRbcYcb7JJIXW89S6 -# QTRdEi0luY2mE0htS7AHfZmTCWGBdFcmiqtp4+TZx4jMJNjsUiRcHryRFOKW3usK -# 2p7dX7Nb29SC7MYgUIclQDr7x+7N/jPlbsOECVUDJTnA6TVdZTGo9r+gCc0px7M2 -# Mi7clfODwVrPi4326rMh+KTtHjEOtkwRq2ALpBIjIhejNmSCkQQS4KtvHstQBWG0 -# QP9ZhnHR1TNpfKlzijjXZAzxaTCCBnEwggRZoAMCAQICCmEJgSoAAAAAAAIwDQYJ -# 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/edIhJEqGCAs4wggI3 -# AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNV -# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsG -# A1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYD -# VQQLEx1UaGFsZXMgVFNTIEVTTjo4RTlFLTRCRDAtMkVEMDElMCMGA1UEAxMcTWlj -# cm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZaIjCgEBMAcGBSsOAwIaAxUAybtFiTJ7 -# M24kwW5YE9d6GKXiCMOggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg -# MjAxMDANBgkqhkiG9w0BAQUFAAIFAN8zhucwIhgPMjAxODA4MzExNzIwMzlaGA8y -# MDE4MDkwMTE3MjAzOVowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA3zOG5wIBADAK -# AgEAAgImeAIB/zAHAgEAAgITVTAKAgUA3zTYZwIBADA2BgorBgEEAYRZCgQCMSgw -# JjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3 -# DQEBBQUAA4GBACqi4VCcKP15HurYYkCJwgChOJcwXO8jXERhGN2o17vsjyE+ssNV -# nYdeFGk+63HhhzxYXHJRRugS/Lj4Gwb9H1ls3ek3OIg58NyC4JMpaJJNcas1ghhj -# mZh82ZQGIfSk8/0IQv5NZlLsbHAxGaoWclmOMe1JKqGfCOYCZ+CTsXFpMYIDDTCC -# AwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO -# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEm -# MCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADDGu2K -# 0g0+y1EAAAAAAMMwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsq -# hkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgz+Nq6+PUbqCtbfZftDoAu7Y12wGO -# Ca/WsmHn9icilDYwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCz+WivtpFS -# JEGUqqiE9MIF44SXs1OtNR4BAQA6MNq16jCBmDCBgKR+MHwxCzAJBgNVBAYTAlVT -# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK -# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l -# LVN0YW1wIFBDQSAyMDEwAhMzAAAAwxrtitINPstRAAAAAADDMCIEIKt0j+HbgGUd -# VjQOxbGuPR5RK+jlm05Zfdk++RUOysGsMA0GCSqGSIb3DQEBCwUABIIBAHrHLegK -# Bo4OsdhXvw7HkdQabuMRyok98DEhj7OZFQMdR/PUEiM1wCrsb8CZjLYEmUIwlgZq -# T5u1lwPWAbvKhqUn2NRf5cTThuyUPkSrGxdUuMCgj3+b98S6h7XmLik3y6zUcuxv -# WCvBksIeVW6vWlN7m8PRzgcC2drMbEKDXMY3qCpKQoX1xPrywE9jmg/8r/UqMhB/ -# qMMKxA6gaSVI4G/eMm0yNo3iWFFSnGNZB1FxpG4P226B7uHjXohgAQ8LvHCRH1ZO -# aQ4U74woSboBlvFdedmrGH5TH4epdSNjHR35ulfWpN2wniPHPUSB8K6NsJGCjoox -# ggKwgEU4fcTu2Ws= -# SIG # End signature block