AnyPackageDsc.psm1
# Copyright (c) Thomas Nieto - All Rights Reserved # You may use, distribute and modify this code under the # terms of the MIT license. using module AnyPackage using namespace AnyPackage.Provider enum Ensure { Absent Present } class APReason { [string] $Code [string] $Phrase } [DscResource()] class Package { [DscProperty(Key)] [string] $Name [DscProperty(Key)] [string] $Version [DscProperty(Key)] [string] $Provider [DscProperty()] [bool] $Prerelease [DscProperty()] [string] $Source [DscProperty()] [hashtable] $AdditionalParameters = @{ } [DscProperty()] [bool] $Latest [DscProperty()] [Ensure] $Ensure = [Ensure]::Present [DscProperty(NotConfigurable)] [APReason[]] $Reasons static [Package[]] Export() { Get-PackageProvider -ListAvailable | Select-Object -ExpandProperty ModuleName -Unique | Import-Module $packages = @() foreach ($installedPackage in (Get-Package -ErrorAction Stop)) { $package = [Package]::new() $package.Name = $installedPackage.Name $package.Provider = $installedPackage.Provider.FullName if ($installedPackage.Version) { $package.Version = $installedPackage.Version $package.Prerelease = $installedPackage.Version.IsPrerelease } else { $package.Version = '*' } $package.Source = $installedPackage.Source $packages += $package } return $packages } [Package] Get() { $currentState = [Package]@{ Name = $this.Name Provider = $this.Provider } $params = @{ Name = $this.Name Provider = $this.Provider ErrorAction = 'Stop' } if ($this.Version -ne '*') { $params['Version'] = $this.Version } if (-not $this.Provider.Contains('\')) { throw "Not a valid Provider full name: '$($this.Provider)'" } try { Import-Module ($this.Provider).Split('\')[0] -ErrorAction Stop $package = Get-Package @params | Sort-Object -Property Version -Descending | Select-Object -First 1 $currentState.Ensure = [Ensure]::Present $currentState.Source = $package.Source $currentState.Version = $package.Version $currentState.Prerelease = $package.Version.IsPrerelease } catch [PackageNotFoundException] { $currentState.Ensure = [Ensure]::Absent } if ($this.Ensure -ne $currentState.Ensure) { $currentState.Reasons += [APReason]@{ Code = 'Package:Package:Ensure' Phrase = "Package '$($this.Name)' should be '$($this.Ensure)' but was '$($currentState.Ensure)'." } } if ($this.Source -and $this.Source -ne $currentState.Source) { $currentState.Reasons += [APReason]@{ Code = 'Package:Package:Source' Phrase = "Source should be '$($this.Source)' but was '$($currentState.Source)'." } } if (-not $this.Prerelease -and $this.Prerelease -ne $currentState.Prerelease) { $currentState.Reasons += [APReason]@{ Code = 'Package:Package:Prerelease' Phrase = 'Prerelease versions not allowed.' } } if ($this.Latest) { if ($this.Source) { $params['Source'] = $this.Source } $params['Prerelease'] = $this.Prerelease $latestPackage = Find-Package @params | Sort-Object -Property Version -Descending | Select-Object -First 1 if ([PackageVersion]$currentState.Version -lt $latestPackage.Version) { $currentState.Reasons += [APReason]@{ Code = 'Package:Package:Latest' Phrase = "Version should be '$($latestPackage.Version)' but was '$($currentState.Version)'." } } } return $currentState } [void] Set() { if ($this.Test()) { return } $params = @{ Name = $this.Name Provider = $this.Provider ErrorAction = 'Stop' } if ($this.Version -ne '*') { $params['Version'] = $this.Version } $currentState = $this.Get() if ($this.Ensure -eq [Ensure]::Present) { if ($this.Source) { $params['Source'] = $this.Source } if ($this.Latest) { $params['Version'] = Find-Package @params | Sort-Object -Property Version -Descending | Select-Object -ExpandProperty Version -First 1 } # Can't splat using property $additionalParams = $this.AdditionalParameters if ($currentState.Ensure -eq [Ensure]::Present -and (Get-PackageProvider -Name $this.Provider).Operations.HasFlag([PackageProviderOperations]::Update)) { Update-Package @params @additionalParams } else { Install-Package @params @additionalParams } } elseif ($currentState.Ensure -eq [Ensure]::Present) { Uninstall-Package @params } } [bool] Test() { return $this.Get().Reasons.Count -eq 0 } } [DscResource()] class Source { [DscProperty(Key)] [string] $Name [DscProperty(Key)] [string] $Provider [DscProperty()] [string] $Location [DscProperty()] [bool] $Trusted [DscProperty()] [hashtable] $AdditionalParameters = @{ } [DscProperty()] [Ensure] $Ensure = [Ensure]::Present [DscProperty(NotConfigurable)] [APReason[]] $Reasons static [Source[]] Export() { Get-PackageProvider -ListAvailable | Select-Object -ExpandProperty ModuleName -Unique | Import-Module $sources = @() foreach ($installedSource in (Get-PackageSource -ErrorAction Stop)) { $source = [Source]::new() $source.Name = $installedSource.Name $source.Provider = $installedSource.Provider.FullName $source.Location = $installedSource.Location $source.Trusted = $installedSource.Trusted $sources += $source } return $sources } [Source] Get() { $currentState = [Source]@{ Name = $this.Name Provider = $this.Provider } $params = @{ Name = $this.Name Provider = $this.Provider ErrorAction = 'Stop' } if (-not $this.Provider.Contains('\')) { throw "Not a valid Provider full name: '$($this.Provider)'" } try { Import-Module ($this.Provider).Split('\')[0] -ErrorAction Stop $source = Get-PackageSource @params $currentState.Ensure = [Ensure]::Present $currentState.Location = $source.Location $currentState.Trusted = $source.Trusted } catch [PackageSourceNotFoundException] { $currentState.Ensure = [Ensure]::Absent } if ($this.Ensure -ne $currentState.Ensure) { $currentState.Reasons += [APReason]@{ Code = 'Source:Source:Ensure' Phrase = "Package source '$($this.Name)' should be '$($this.Ensure)' but was '$($currentState.Ensure)'." } } if ($this.Location -and $this.Location -ne $currentState.Location) { $currentState.Reasons += [APReason]@{ Code = 'Source:Source:Location' Phrase = "Location should be '$($this.Location)' but was '$($currentState.Location)'." } } if ($this.Trusted -ne $currentState.Trusted) { $currentState.Reasons += [APReason]@{ Code = 'Source:Source:Trusted' Phrase = "Trusted should be '$($this.Trusted)' but was '$($currentState.Trusted)'." } } return $currentState } [void] Set() { if ($this.Test()) { return } $params = @{ Name = $this.Name Provider = $this.Provider ErrorAction = 'Stop' } $currentState = $this.Get() if ($this.Ensure -eq [Ensure]::Present) { $params['Location'] = $this.Location $params['Trusted'] = $this.Trusted # Can't splat using property $additionalParams = $this.AdditionalParameters if ($currentState.Ensure -eq [Ensure]::Present) { Set-PackageSource @params @additionalParams } else { Register-PackageSource @params @additionalParams } } elseif ($currentState.Ensure -eq [Ensure]::Present) { Unregister-PackageSource @params } } [bool] Test() { return $this.Get().Reasons.Count -eq 0 } } # SIG # Begin signature block # MIIlSAYJKoZIhvcNAQcCoIIlOTCCJTUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAe0shp3YOi1CS5 # +Ri4LpQxS/OKHGST1BTv+/VqdooHVKCCHtwwggW2MIIEHqADAgECAhEApPLKEVUi # zMqDeJHsqFBBojANBgkqhkiG9w0BAQwFADBUMQswCQYDVQQGEwJHQjEYMBYGA1UE # ChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2Rl # IFNpZ25pbmcgQ0EgUjM2MB4XDTIyMTIwNjAwMDAwMFoXDTI1MTIwNTIzNTk1OVow # TDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkthbnNhczEVMBMGA1UECgwMVGhvbWFz # IE5pZXRvMRUwEwYDVQQDDAxUaG9tYXMgTmlldG8wggGiMA0GCSqGSIb3DQEBAQUA # A4IBjwAwggGKAoIBgQDKp+8DxaXlIDZh+gMApqRZ4hD4IDTdRkUEZZqSnUwxQnD5 # k8VpbDeqrEkmww2TKUeOWQbSRMT70T/8R8+ld2GrwzLDQ83k5mrGXIXSXCHRiLIZ # UnLmVjDtf/4FXTEni+acu8dddbCS0GAAzDfnINFgVRQR9GdoaYLqoipRVpLVRBzb # eR2gSCtzwmpuX1d4NR58NvfUkfzNxy0kaLfqamHl9ae0JHyGvyzrHgOUyt2ttA/F # idNo8KudwGau/gfyko5egOAURpEqgJHrcaekTVio+GRQs2IFNHIvNnfF9Rm7kn5w # PZuxWL4UaV5xGyYWLupny3Lpp1n+XlVg6UgYZX25BWwbdbfLvsDPHnlbPB2L0WgC # CeG6ZOZjB2dmFaYHhwozRzcvX0FLoYXv1/Vo9QviUTm2QIqb/gaxt3xl/rtcCZOj # ly518iHNR2f1ClS+dPiD7KO5r2owmUsaMZiPVeMnD8/dKT8c9jPhyRBntbX9V6ho # RXD6J2mf5SKSql8pwC8CAwEAAaOCAYkwggGFMB8GA1UdIwQYMBaAFA8qyyCHKLjs # b0iuK1SmKaoXpM0MMB0GA1UdDgQWBBQOV7tpbOOE2AnSg3DwNoU56VePWzAOBgNV # HQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzBK # BgNVHSAEQzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczov # L3NlY3RpZ28uY29tL0NQUzAIBgZngQwBBAEwSQYDVR0fBEIwQDA+oDygOoY4aHR0 # cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIz # Ni5jcmwweQYIKwYBBQUHAQEEbTBrMEQGCCsGAQUFBzAChjhodHRwOi8vY3J0LnNl # Y3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNydDAjBggr # BgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEMBQAD # ggGBADWsCefpJbT4oKIh1SXIR4hnfGN/YqI6g3aFIntNgoiurd5uxLkow2WSpfaC # iXjWjMubDpBvynVvaBsRfcrwT0WGBTwz4miuITPKKkIXYIVb5dCf33ghMJ4UD+zH # P73ioUtDV545Yeb5WVLrPN8ZCC++u0kX+jck30w56LCvng6gDpPHU/KNroQxENjG # pcycTf7Gq9tkcEVbGersD6R64NhI6r8uDH6l0s5NMep1x4yTs0MBPmlB6ZHK+88Y # GDVdfTSYbLpQuvmLkEMHNaPOL0YyTjJJbeaHvGuTfqQb17HDLFJGd70CKrQumvcI # CrJM9il3B+bNsSKjTLQtGbp5JdJ5paUahybiJKyZlDw5QPRrFEnwHiWaTI/9zfRc # iZyX4kYnLkPpp8rlZFXBqfIzf0gRhgCjVVZoMwZHWqyZNL7gS4C6uvEn2t/3BqM7 # 548OuoPLH7W07orz8T7iP7aNYtJpAttZOhAbqB2EKoY3qcKClqKOMQGvc12/16mA # KE7E+TCCBhQwggP8oAMCAQICEHojrtpTaZYPkcg+XPTH4z8wDQYJKoZIhvcNAQEM # BQAwVzELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwG # A1UEAxMlU2VjdGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBSb290IFI0NjAeFw0y # MTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFUxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLDAqBgNVBAMTI1NlY3RpZ28gUHVibGljIFRp # bWUgU3RhbXBpbmcgQ0EgUjM2MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKC # AYEAzZjYQ0GrboIr7PYzfiY05ImM0+8iEoBUPu8mr4wOgYPjoiIz5vzf7d5wu8GF # K1JWN5hciN9rdqOhbdxLcSVwnOTJmUGfAMQm4eXOls3iQwfapEFWuOsYmBKXPNSp # wZAFoLGl5y1EaGGc5LByM8wjcbSF52/Z42YaJRsPXY545E3QAPN2mxDh0OLozhiG # gYT1xtjXVfEzYBVmfQaI5QL35cTTAjsJAp85R+KAsOfuL9Z7LFnjdcuPkZWjssME # TFIueH69rxbFOUD64G+rUo7xFIdRAuDNvWBsv0iGDPGaR2nZlY24tz5fISYk1sPY # 4gir99aXAGnoo0vX3Okew4MsiyBn5ZnUDMKzUcQrpVavGacrIkmDYu/bcOUR1mVB # IZ0X7P4bKf38JF7Mp7tY3LFF/h7hvBS2tgTYXlD7TnIMPrxyXCfB5yQq3FFoXRXM # 3/DvqQ4shoVWF/mwwz9xoRku05iphp22fTfjKRIVpm4gFT24JKspEpM8mFa9eTgK # WWCvAgMBAAGjggFcMIIBWDAfBgNVHSMEGDAWgBT2d2rdP/0BE/8WoWyCAi/QCj0U # JTAdBgNVHQ4EFgQUX1jtTDF6omFCjVKAurNhlxmiMpswDgYDVR0PAQH/BAQDAgGG # MBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0g # BAowCDAGBgRVHSAAMEwGA1UdHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuc2VjdGln # by5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ1Jvb3RSNDYuY3JsMHwGCCsG # AQUFBwEBBHAwbjBHBggrBgEFBQcwAoY7aHR0cDovL2NydC5zZWN0aWdvLmNvbS9T # ZWN0aWdvUHVibGljVGltZVN0YW1waW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGG # F2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAS13sg # rQ41WAyegR0lWP1MLWd0r8diJiH2VVRpxqFGhnZbaF+IQ7JATGceTWOS+kgnMAzG # YRzpm8jIcjlSQ8JtcqymKhgx1s6cFZBSfvfeoyigF8iCGlH+SVSo3HHr98NepjSF # JTU5KSRKK+3nVSWYkSVQgJlgGh3MPcz9IWN4I/n1qfDGzqHCPWZ+/Mb5vVyhgaeq # xLPbBIqv6cM74Nvyo1xNsllECJJrOvsrJQkajVz4xJwZ8blAdX5umzwFfk7K/0K3 # fpjgiXpqNOpXaJ+KSRW0HdE0FSDC7+ZKJJSJx78mn+rwEyT+A3z7Ss0gT5CpTrcm # hUwIw9jbvnYuYRKxFVWjKklW3z83epDVzoWJttxFpujdrNmRwh1YZVIB2guAAjEQ # oF42H0BA7WBCueHVMDyV1e4nM9K4As7PVSNvQ8LI1WRaTuGSFUd9y8F8jw22BZC6 # mJoB40d7SlZIYfaildlgpgbgtu6SDsek2L8qomG57Yp5qTqof0DwJ4Q4HsShvRl/ # 59T4IJBovRwmqWafH0cIPEX7cEttS5+tXrgRtMjjTOp6A9l0D6xcKZtxnLqiTH9K # PCy6xZEi0UDcMTww5Fl4VvoGbMG2oonuX3f1tsoHLaO/Fwkj3xVr3lDkmeUqiveb # QTvGkx5hGuJaSVQ+x60xJ/Y29RBr8Tm9XJ59AjCCBhowggQCoAMCAQICEGIdbQxS # AZ47kHkVIIkhHAowDQYJKoZIhvcNAQEMBQAwVjELMAkGA1UEBhMCR0IxGDAWBgNV # BAoTD1NlY3RpZ28gTGltaXRlZDEtMCsGA1UEAxMkU2VjdGlnbyBQdWJsaWMgQ29k # ZSBTaWduaW5nIFJvb3QgUjQ2MB4XDTIxMDMyMjAwMDAwMFoXDTM2MDMyMTIzNTk1 # OVowVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkG # A1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNjCCAaIwDQYJ # KoZIhvcNAQEBBQADggGPADCCAYoCggGBAJsrnVP6NT+OYAZDasDP9X/2yFNTGMjO # 02x+/FgHlRd5ZTMLER4ARkZsQ3hAyAKwktlQqFZOGP/I+rLSJJmFeRno+DYDY1UO # AWKA4xjMHY4qF2p9YZWhhbeFpPb09JNqFiTCYy/Rv/zedt4QJuIxeFI61tqb7/fo # XT1/LW2wHyN79FXSYiTxcv+18Irpw+5gcTbXnDOsrSHVJYdPE9s+5iRF2Q/TlnCZ # GZOcA7n9qudjzeN43OE/TpKF2dGq1mVXn37zK/4oiETkgsyqA5lgAQ0c1f1IkOb6 # rGnhWqkHcxX+HnfKXjVodTmmV52L2UIFsf0l4iQ0UgKJUc2RGarhOnG3B++OxR53 # LPys3J9AnL9o6zlviz5pzsgfrQH4lrtNUz4Qq/Va5MbBwuahTcWk4UxuY+PynPjg # w9nV/35gRAhC3L81B3/bIaBb659+Vxn9kT2jUztrkmep/aLb+4xJbKZHyvahAEx2 # XKHafkeKtjiMqcUf/2BG935A591GsllvWwIDAQABo4IBZDCCAWAwHwYDVR0jBBgw # FoAUMuuSmv81lkgvKEBCcCA2kVwXheYwHQYDVR0OBBYEFA8qyyCHKLjsb0iuK1Sm # KaoXpM0MMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBBAEwSwYD # VR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVi # bGljQ29kZVNpZ25pbmdSb290UjQ2LmNybDB7BggrBgEFBQcBAQRvMG0wRgYIKwYB # BQUHMAKGOmh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVT # aWduaW5nUm9vdFI0Ni5wN2MwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3Rp # Z28uY29tMA0GCSqGSIb3DQEBDAUAA4ICAQAG/4Lhd2M2bnuhFSCbE/8E/ph1RGHD # VpVx0ZE/haHrQECxyNbgcv2FymQ5PPmNS6Dah66dtgCjBsULYAor5wxxcgEPRl05 # pZOzI3IEGwwsepp+8iGsLKaVpL3z5CmgELIqmk/Q5zFgR1TSGmxqoEEhk60FqONz # Dn7D8p4W89h8sX+V1imaUb693TGqWp3T32IKGfIgy9jkd7GM7YCa2xulWfQ6E1xZ # tYNEX/ewGnp9ZeHPsNwwviJMBZL4xVd40uPWUnOJUoSiugaz0yWLODRtQxs5qU6E # 58KKmfHwJotl5WZ7nIQuDT0mWjwEx7zSM7fs9Tx6N+Q/3+49qTtUvAQsrEAxwmzO # TJ6Jp6uWmHCgrHW4dHM3ITpvG5Ipy62KyqYovk5O6cC+040Si15KJpuQ9VJnbPvq # YqfMB9nEKX/d2rd1Q3DiuDexMKCCQdJGpOqUsxLuCOuFOoGbO7Uv3RjUpY39jkkp # 0a+yls6tN85fJe+Y8voTnbPU1knpy24wUFBkfenBa+pRFHwCBB1QtS+vGNRhsceP # 3kSPNrrfN2sRzFYsNfrFaWz8YOdU254qNZQfd9O/VjxZ2Gjr3xgANHtM3HxfzPYF # 6/pKK8EE4dj66qKKtm2DTL1KFCg/OYJyfrdLJq1q2/HXntgr2GVw+ZWhrWgMTn8v # 1SjZsLlrgIfZHDCCBmIwggTKoAMCAQICEQCkKTtuHt3XpzQIh616TrckMA0GCSqG # SIb3DQEBDAUAMFUxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxLDAqBgNVBAMTI1NlY3RpZ28gUHVibGljIFRpbWUgU3RhbXBpbmcgQ0EgUjM2 # MB4XDTI1MDMyNzAwMDAwMFoXDTM2MDMyMTIzNTk1OVowcjELMAkGA1UEBhMCR0Ix # FzAVBgNVBAgTDldlc3QgWW9ya3NoaXJlMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxMDAuBgNVBAMTJ1NlY3RpZ28gUHVibGljIFRpbWUgU3RhbXBpbmcgU2lnbmVy # IFIzNjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANOElfRupFN48j0Q # S3gSBzzclIFTZ2Gsn7BjsmBF659/kpA2Ey7NXK3MP6JdrMBNU8wdmkf+SSIyjX++ # UAYWtg3Y/uDRDyg8RxHeHRJ+0U1jHEyH5uPdk1ttiPC3x/gOxIc9P7Gn3OgW7DQc # 4x07exZ4DX4XyaGDq5LoEmk/BdCM1IelVMKB3WA6YpZ/XYdJ9JueOXeQObSQ/doh # QCGyh0FhmwkDWKZaqQBWrBwZ++zqlt+z/QYTgEnZo6dyIo2IhXXANFkCHutL8765 # NBxvolXMFWY8/reTnFxk3MajgM5NX6wzWdWsPJxYRhLxtJLSUJJ5yWRNw+NBqH1e # zvFs4GgJ2ZqFJ+Dwqbx9+rw+F2gBdgo4j7CVomP49sS7CbqsdybbiOGpB9DJhs5Q # VMpYV73TVV3IwLiBHBECrTgUfZVOMF0KSEq2zk/LsfvehswavE3W4aBXJmGjgWSp # cDz+6TqeTM8f1DIcgQPdz0IYgnT3yFTgiDbFGOFNt6eCidxdR6j9x+kpcN5RwApy # 4pRhE10YOV/xafBvKpRuWPjOPWRBlKdm53kS2aMh08spx7xSEqXn4QQldCnUWRz3 # Lki+TgBlpwYwJUbR77DAayNwAANE7taBrz2v+MnnogMrvvct0iwvfIA1W8kp155L # o44SIfqGmrbJP6Mn+Udr3MR2oWozAgMBAAGjggGOMIIBijAfBgNVHSMEGDAWgBRf # WO1MMXqiYUKNUoC6s2GXGaIymzAdBgNVHQ4EFgQUiGGMoSo3ZIEoYKGbMdCM/SwC # zk8wDgYDVR0PAQH/BAQDAgbAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYI # KwYBBQUHAwgwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIBAwgwJTAjBggrBgEFBQcC # ARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQCMEoGA1UdHwRDMEEw # P6A9oDuGOWh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY1RpbWVT # dGFtcGluZ0NBUjM2LmNybDB6BggrBgEFBQcBAQRuMGwwRQYIKwYBBQUHMAKGOWh0 # dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ0NB # UjM2LmNydDAjBggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJ # KoZIhvcNAQEMBQADggGBAAKBPqSGclEh+WWpLj1SiuHlm8xLE0SThI2yLuq+75s1 # 1y6SceBchpnKpxWaGtXc8dya1Aq3RuW//y3wMThsvT4fSba2AoSWlR67rA4fTYGM # Ihgzocsids0ct/pHaocLVJSwnTYxY2pE0hPoZAvRebctbsTqENmZHyOVjOFlwN2R # 3DRweFeNs4uyZN5LRJ5EnVYlcTOq3bl1tI5poru9WaQRWQ4eynXp7Pj0Fz4DKr86 # HYECRJMWiDjeV0QqAcQMFsIjJtrYTw7mU81qf4FBc4u4swphLeKRNyn9DDrd3HIM # J+CpdhSHEGleeZ5I79YDg3B3A/fmVY2GaMik1Vm+FajEMv4/EN2mmHf4zkOuhYZN # zVm4NrWJeY4UAriLBOeVYODdA1GxFr1ycbcUEGlUecc4RCPgYySs4d00NNuicR4a # 9n7idJlevAJbha/arIYMEuUqTeRRbWkhJwMKmb9yEvppRudKyu1t6l21sIuIZqcp # VH8oLWCxHS0LpDRF9Y4jijCCBoIwggRqoAMCAQICEDbCsL18Gzrno7PdNsvJdWgw # DQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVy # c2V5MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVT # VCBOZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24g # QXV0aG9yaXR5MB4XDTIxMDMyMjAwMDAwMFoXDTM4MDExODIzNTk1OVowVzELMAkG # A1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEuMCwGA1UEAxMlU2Vj # dGlnbyBQdWJsaWMgVGltZSBTdGFtcGluZyBSb290IFI0NjCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAIid2LlFZ50d3ei5JoGaVFTAfEkFm8xaFQ/ZlBBE # tEFAgXcUmanU5HYsyAhTXiDQkiUvpVdYqZ1uYoZEMgtHES1l1Cc6HaqZzEbOOp6Y # iTx63ywTon434aXVydmhx7Dx4IBrAou7hNGsKioIBPy5GMN7KmgYmuu4f92sKKjb # xqohUSfjk1mJlAjthgF7Hjx4vvyVDQGsd5KarLW5d73E3ThobSkob2SL48LpUR/O # 627pDchxll+bTSv1gASn/hp6IuHJorEu6EopoB1CNFp/+HpTXeNARXUmdRMKbnXW # flq+/g36NJXB35ZvxQw6zid61qmrlD/IbKJA6COw/8lFSPQwBP1ityZdwuCysCKZ # 9ZjczMqbUcLFyq6KdOpuzVDR3ZUwxDKL1wCAxgL2Mpz7eZbrb/JWXiOcNzDpQsmw # GQ6Stw8tTCqPumhLRPb7YkzM8/6NnWH3T9ClmcGSF22LEyJYNWCHrQqYubNeKolz # qUbCqhSqmr/UdUeb49zYHr7ALL8bAJyPDmubNqMtuaobKASBqP84uhqcRY/pjnYd # +V5/dcu9ieERjiRKKsxCG1t6tG9oj7liwPddXEcYGOUiWLm742st50jGwTzxbMpe # pmOP1mLnJskvZaN5e45NuzAHteORlsSuDt5t4BBRCJL+5EZnnw0ezntk9R8QJyAk # L6/bAgMBAAGjggEWMIIBEjAfBgNVHSMEGDAWgBRTeb9aqitKz1SA4dibwJ3ysgNm # yzAdBgNVHQ4EFgQU9ndq3T/9ARP/FqFsggIv0Ao9FCUwDgYDVR0PAQH/BAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wEwYDVR0lBAwwCgYIKwYBBQUHAwgwEQYDVR0gBAow # CDAGBgRVHSAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0 # LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDA1Bggr # BgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5j # b20wDQYJKoZIhvcNAQEMBQADggIBAA6+ZUHtaES45aHF1BGH5Lc7JYzrftrIF5Ht # 2PFDxKKFOct/awAEWgHQMVHol9ZLSyd/pYMbaC0IZ+XBW9xhdkkmUV/KbUOiL7g9 # 8M/yzRyqUOZ1/IY7Ay0YbMniIibJrPcgFp73WDnRDKtVutShPSZQZAdtFwXnuiWl # 8eFARK3PmLqEm9UsVX+55DbVIz33Mbhba0HUTEYv3yJ1fwKGxPBsP/MgTECimh7e # XomvMm0/GPxX2uhwCcs/YLxDnBdVVlxvDjHjO1cuwbOpkiJGHmLXXVNbsdXUC2xB # rq9fLrfe8IBsA4hopwsCj8hTuwKXJlSTrZcPRVSccP5i9U28gZ7OMzoJGlxZ5384 # OKm0r568Mo9TYrqzKeKZgFo0fj2/0iHbj55hc20jfxvK3mQi+H7xpbzxZOFGm/yV # Qkpo+ffv5gdhp+hv1GDsvJOtJinJmgGbBFZIThbqI+MHvAmMmkfb3fTxmSkop2mS # JL1Y2x/955S29Gu0gSJIkc3z30vU/iXrMpWx2tS7UVfVP+5tKuzGtgkP7d/doqDr # LF1u6Ci3TpjAZdeLLlRQZm867eVeXED58LXd1Dk6UvaAhvmWYXoiLz4JA5gPBcz7 # J311uahxCweNxE+xxxR3kT0WKzASo5G/PyDez6NHdIUKBeE3jDPs2ACc6CkJ1Sji # 4PKWVT0/MYIFwjCCBb4CAQEwaTBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2Vj # dGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25p # bmcgQ0EgUjM2AhEApPLKEVUizMqDeJHsqFBBojANBglghkgBZQMEAgEFAKCBhDAY # BgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3 # AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEi # BCBvO68N0NApt6+h0+lhMJ0W/6TZcKD7mbGa788xYrhmkzANBgkqhkiG9w0BAQEF # AASCAYCz8QNTW2Ti2qRDIS0fJoEebn3p39IBtIs4glL41aSR04d9u0b7ontk5TlD # 6n+KYkIph0JIYPPMSSCa1kgraEpODwYkLweErD2GRt2HsOg1VKnVAC+07OyYDu8e # CfTclETvFVOn4ZXuoQKXjvqsMqfYHWfTVAt8pJgtfUXg/jx3FudKh4B6KOvDh4C3 # 8dMCPbUQuC0ktEivCxXG/rnBR/PrBxJsTiwHNgrGK4oykCjNofLr0w3jJY5NuIXw # uYUiiQjTc4BThW3KAv/aU4LdEbotPd+YdZT4DiYVRa0mqUCNZQWLExFwm1kAfaro # 5+Xsdq1azwTSPFLKbXovlyon9dNqxl+SjL27NZgk6YURJ0bMoRIEuyJOkAV7n+P3 # qu0kHT608X+1mmZFBvXH/c2R+5DuJZvrVcHLPbp+Wt6TYMGfjAuExLOMork58U7n # +tygsvOkjlaPLKTR+osOFiWl1CvQ/l4YHOuo/uYBBMMrlFoRMBILTE1agJSOxTNa # +UMn4KGhggMjMIIDHwYJKoZIhvcNAQkGMYIDEDCCAwwCAQEwajBVMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSwwKgYDVQQDEyNTZWN0aWdv # IFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNgIRAKQpO24e3denNAiHrXpOtyQw # DQYJYIZIAWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqG # SIb3DQEJBTEPFw0yNTA1MTAwNDU1MTVaMD8GCSqGSIb3DQEJBDEyBDDfAJAgho79 # PsVtnDrbfTxA49qgZmL7i7tWumjFaKYjWg4l3eQ9U79TRll1wXkluCwwDQYJKoZI # hvcNAQEBBQAEggIAw7f/zsRhzU7aqKcR0OtN8SKnB+4KMqihJHwVuzB+1rfor9tN # 7UzRnjNADMozNmul8bb2HzY+CzKlgSN8E09e4xnUvUVgFKOQHDFJGkJ+BepRQq7+ # oRwp9u/qMbRJXEoG2kuWPvoNG9rX9xkf2LKaDrDrlfnK/Luf3JI7KeGSdwq9UcND # F4RAs02BemfxVtgg5jucUS0vKJ6QKkgG9LhkXiYlIajFmF9L2C+KjRWA0YHTkVsi # jFvULjluY3Cj6HVtU/PyXQbqZ+k+UKWSU8dw4tWvSi2UOcmpapIMEporSQemORPB # 8HxOravN3lN+Nh2Q4w5JbVhFWqaJI/cMSnmR/YzPF/R0dfM8PxSMfnXPatCk7lsQ # DBWYhvocV4Z1+dBPw4gRYtOv2eO57DWQqkaKkqC/mUpa8oIZ3y9oqHZZTAJxg+xe # zbhH7tveedowC+ghN3kQT4oT3fT7KCATDnZDmZ6GOu8WM8ehjaMhvNSj+TD20X9L # 957Jag7FSNlRw8senw0+rGNJRwIV23eeWb7Yzx5ODttAqC1tRA5irD2g7CdsspeN # 9gHAu8bsE34QOupT4Q/HvCgTmS+0e7zIpKSHWfYt1tkR0Fq8qGAfgl9jvvcrxNuf # 8rkodqiBH96DR9v0wE0ToUp7pa1HR8t4EdnUGm9KRHxb5hXLZAr1IvrA2jI= # SIG # End signature block |