Public/Publish-AWSPowerShellLambda.ps1

<#
    .SYNOPSIS
    Publishes (deploys) a PowerShell script or script project as an AWS Lambda Function.
 
    .DESCRIPTION
    PowerShell scripts are deployed to AWS Lambda as a .NET Core package bundle. For standalone script files this cmdlet will
    create a temporary .NET Core C# project that will include the specified PowerShell script and any required PowerShell modules.
    The C# project also includes code to bootstrap the PowerShell runtime and execute the Lambda function.
 
    The cmdlet can also be used to publish a script project to AWS Lambda. Using the project based workflow is for advanced use
    cases when you need more control over how the script is loaded or need to include additional files. If you just need to deploy
    a single script consider using the simpler script based workflow.
 
    When publishing a project-based script function many of the configuration parameters can be specified in a configuration file
    in your project named aws-lambda-tools-defaults.json. If your script project was generated using the New-AWSPowerShellLambda
    cmdlet a default version of this file will have been created for you. The following cmdlet parameters can take their values
    from the aws-lambda-tools-defaults.json file:
 
    * DeadLetterQueueArn
    * EnvironmentVariable
    * Handler
    * Memory
    * Name
    * IAMRoleArn
    * SecurityGroup
    * Subnet
    * Timeout
    * KmsKeyArn
    * Profile
    * Region
    * Tag
    * TracingMode
 
    .PARAMETER DeadLetterQueue
    Optional, target ARN of an SNS topic or SQS Queue for the Dead Letter Queue.
 
    .PARAMETER DisableInteractive
    Do not prompt for missing function configuration values required for successful deployment to AWS Lambda.
 
    .PARAMETER DisableModuleRestore
    Skip restoring any required PowerShell modules when deploying a project.
 
    .PARAMETER EnvironmentVariable
    Collection of key=value environment variables to be set for the function when it executes inside AWS Lambda.
 
    .PARAMETER Handler
    The string that identifies the entry point in the Lambda package bundle.
 
    .PARAMETER KmsKeyArn
    KMS Key ARN of a customer key used to encrypt the function's environment variables.
 
    .PARAMETER Memory
    The amount of memory, in MB, your Lambda function is given when it executes inside AWS Lambda.
 
    .PARAMETER ModuleRepository
    Custom repositories to use when downloading modules to satisfy your script's declared dependencies.
 
    .PARAMETER Name
    The name of the AWS Lambda function that will execute the PowerShell script.
 
    .PARAMETER PowerShellFunctionHandler
    The name of the specific PowerShell function to be invoked when your Lambda function executes. The default
    behavior is to use the script in its entirety as the function handler. The name of the function to run, if
    specified, is communicated to Lambda using an environment variable named AWS_POWERSHELL_FUNCTION_HANDLER.
 
    .PARAMETER PowerShellSdkVersion
    Optional parameter to override the version of PowerShell that will execute the script. The version number
    must match a version of the Microsoft.PowerShell.SDK NuGet package. https://www.nuget.org/packages/Microsoft.PowerShell.SDK
 
    .PARAMETER ProfileName
    The AWS credentials profile to use when publishing to AWS Lambda. If not set environment credentials will be used.
 
    .PARAMETER ProjectDirectory
    The directory containing the AWS Lambda PowerShell project to publish.
 
    .PARAMETER PublishNewVersion
    Publish a new version as an atomic operation.
 
    .PARAMETER Region
    The region to connect to AWS services, if not set region will be detected from the environment.
 
    .PARAMETER IAMRoleArn
    The IAM role ARN that Lambda assumes when it executes your function.
 
    .PARAMETER S3Bucket
    S3 bucket to upload the build output.
 
    .PARAMETER S3KeyPrefix
    S3 key prefix for the build output.
 
    .PARAMETER StagingDirectory
    Optional parameter to set the directory where the AWS Lambda package bundle for a standalone script deployment
    will be created. If not set the system temp directory will be used.
 
    .PARAMETER ScriptPath
    The path to the PowerShell script file to be published to AWS Lambda.
 
    .PARAMETER SecurityGroup
    List of security group ids if your function references resources in a VPC.
 
    .PARAMETER Subnet
    List of subnet ids if your function references resources in a VPC.
 
    .PARAMETER Timeout
    The function execution timeout in seconds.
 
    .PARAMETER Tag
    Tags applied to the function.
 
    .PARAMETER TracingMode
    Configures when AWS X-Ray should trace the function. Valid values: PassThrough or Active.
 
    .EXAMPLE
    Publish-AWSPowerShellLambda -Name S3CleanupFunction -ScriptPath cleanup-s3-bucket.ps1
 
    This example creates a package bundle with the script and any required PowerShell modules and deploys the bundle to AWS Lambda.
    AWS Credentials and region will be determines by the environment running the cmdlet.
 
    .EXAMPLE
    Publish-AWSPowerShellLambda -Name S3CleanupFunction -ScriptPath cleanup-s3-bucket.ps1 -Profile beta -Region us-west-2
 
    In this example the profile to find AWS credentials and the AWS region are set explicily.
#>

function Publish-AWSPowerShellLambda
{
    [CmdletBinding(DefaultParameterSetName = 'DeployScript')]
    param
    (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'DeployScript',
            HelpMessage = 'The name of the AWS Lambda function that will execute the PowerShell script.')]
        [Parameter(ParameterSetName = 'DeployProject')]
        [string]$Name,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'DeployScript',
            HelpMessage = 'The path to the PowerShell script to be published to AWS Lambda.')]
        [string]$ScriptPath,

        [Parameter(ParameterSetName = 'DeployScript')]
        [string]$StagingDirectory,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'DeployProject',
            HelpMessage = 'The path to the PowerShell project to be published to AWS Lambda.')]
        [string]$ProjectDirectory,

        [Parameter(ParameterSetName = 'DeployProject')]
        [string]$Handler,

        [Parameter(ParameterSetName = 'DeployProject')]
        [switch]$DisableModuleRestore,

        [Parameter()]
        [string]$PowerShellFunctionHandler,

        [Parameter()]
        [string]$ProfileName,

        [Parameter()]
        [string]$Region,

        [Parameter()]
        [string]$IAMRoleArn,

        [Parameter()]
        [int]$Memory,

        [Parameter()]
        [int]$Timeout,

        [Parameter()]
        [Switch]$PublishNewVersion,

        [Parameter()]
        [Hashtable]$EnvironmentVariable,

        [Parameter()]
        [string]$KmsKeyArn,

        [Parameter()]
        [string[]]$Subnet,

        [Parameter()]
        [string[]]$SecurityGroup,

        [Parameter()]
        [string]$DeadLetterQueueArn,

        [Parameter()]
        [ValidateSet('Active', 'PassThrough')]
        [string]$TracingMode,

        [Parameter()]
        [string]$S3Bucket,

        [Parameter()]
        [string]$S3KeyPrefix,

        [Parameter()]
        [Hashtable]$Tag,

        [Parameter()]
        [string[]]$ModuleRepository,

        [Parameter(ParameterSetName = 'DeployScript')]
        [string]$PowerShellSdkVersion,

        [Parameter()]
        [Switch]$DisableInteractive
    )

    _validateDotnetInstall

    # If staging directory is a new temp directory then delete the stage directory after publishing completes
    $deleteStagingDirectory = $false

    if ($PSCmdlet.ParameterSetName -eq 'DeployScript')
    {
        if (!(Test-Path -Path $ScriptPath))
        {
            throw "Script $ScriptPath does not exist."
        }
        
        if (!($StagingDirectory))
        {
            $deleteStagingDirectory = $true
        }

        # Creates and returns an updated StagingDirectory with the ScriptName inside it
        $_name = [System.IO.Path]::GetFileNameWithoutExtension($ScriptPath)
        $stagingSplat = @{
            Name             = $_name
            StagingDirectory = $StagingDirectory
        }
        $StagingDirectory = _createStagingDirectory @stagingSplat

        $_scriptPath = (Resolve-Path -Path $ScriptPath).Path
        $_buildDirectory = (Resolve-Path -Path $StagingDirectory).Path

        $splat = @{
            ProjectName = $Name
            ScriptFile  = [System.IO.Path]::GetFileName($_scriptPath)
            Directory   = $_buildDirectory
            QuietMode   = $false
            PowerShellSdkVersion = $PowerShellSdkVersion
        }
        _addPowerShellLambdaProjectContent @splat

        Write-Host 'Copying PowerShell script to staging directory'
        Copy-Item -Path $_scriptPath -Destination $_buildDirectory

        $splat = @{
            Script           = $_scriptPath
            ProjectDirectory = $_buildDirectory
            ClearExisting    = $true
            ModuleRepository = $ModuleRepository
        }
        _prepareDependentPowerShellModules @splat

        $namespaceName = _makeSafeNamespaceName $Name
        $_handler = "$Name::$namespaceName.Bootstrap::ExecuteFunction"
    }
    else
    {
        if (!($ProjectDirectory))
        {
            $ProjectDirectory = $pwd.Path
        }

        if (!(Test-Path -Path $ProjectDirectory))
        {
            throw "Project directory $ProjectDirectory does not exist."
        }

        if (!($DisableModuleRestore))
        {
            $clearExisting = $true
            Get-ChildItem -Path $ProjectDirectory\*.ps1 | ForEach-Object {
                $splat = @{
                    Script           = $_.FullName
                    ProjectDirectory = $ProjectDirectory
                    ClearExisting    = $clearExisting
                    ModuleRepository = $ModuleRepository
                }
                _prepareDependentPowerShellModules @splat
                $clearExisting = $false
            }
        }

        $_buildDirectory = $ProjectDirectory
        $_handler = $Handler
    }

    Write-Host "Deploying to AWS Lambda"
    $splat = @{
        FunctionName           = $Name
        FunctionHandler        = $_handler
        PowerShellFunction     = $PowerShellFunctionHandler
        Profile                = $ProfileName
        Region                 = $Region
        FunctionRole           = $IAMRoleArn
        FunctionMemory         = $Memory
        FunctionTimeout        = $Timeout
        PublishNewVersion      = $PublishNewVersion
        EnvironmentVariables   = $EnvironmentVariable
        KmsKeyArn              = $KmsKeyArn
        FunctionSubnets        = $Subnet
        FunctionSecurityGroups = $SecurityGroup
        DeadLetterQueueArn     = $DeadLetterQueueArn
        TracingMode            = $TracingMode
        S3Bucket               = $S3Bucket
        S3KeyPrefix            = $S3KeyPrefix
        Tags                   = $Tag
        DisableInteractive     = $DisableInteractive
        BuildDirectory         = $_buildDirectory
    }
    _deployProject @splat

    if($deleteStagingDirectory)
    {
        Write-Verbose -Message "Removing staging directory $_buildDirectory"
        Remove-Item -Path $_buildDirectory -Recurse -Force       
    }
}
# SIG # Begin signature block
# MIIcUAYJKoZIhvcNAQcCoIIcQTCCHD0CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAsnxX20OrSdc54
# u/M+K8npZ7JXwNxvINKrXiYZs8QSvKCCF0cwggS5MIIDoaADAgECAhArd4OFAE9M
# ppHAfzWQwHt/MA0GCSqGSIb3DQEBCwUAMIGEMQswCQYDVQQGEwJVUzEdMBsGA1UE
# ChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0
# IE5ldHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU0hBMjU2IENvZGUg
# U2lnbmluZyBDQSAtIEcyMB4XDTE3MDcxMDAwMDAwMFoXDTIwMDcxMDIzNTk1OVow
# ajELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1Nl
# YXR0bGUxGTAXBgNVBAoMEEFtYXpvbi5jb20sIEluYy4xGTAXBgNVBAMMEEFtYXpv
# bi5jb20sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC18TJW
# m2/aGpvb+TP2CZMg49X/5bjDAgPxWlAs7bqDyZnRYJork4rLmejpeJu+2BhRjZeK
# OirT4XveddBsdL1/TR+FKp8BXEsrm0wcR4yT6mNHJ9yCgC1YBNG91bZ75kRIT+46
# chbC7eNI5703wi8ejxe2KvvOERppBTaFStVJuAHab69dvFma8qE3s7wbqPdQ5eTI
# +Xm0bXp8cObS+vj+hf3N2pfDNWM8ITva3kbowGoCW0rKzpf7fBGtBOKnOCCSL0yC
# AOwLlFkslemVyrT1/HTDjOTKCro016HxOPddA4cefvr2ZhGlRZQQHg7wMdG7TpZX
# ueQ6LoS9UxlzCYHFAgMBAAGjggE+MIIBOjAJBgNVHRMEAjAAMA4GA1UdDwEB/wQE
# AwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzBhBgNVHSAEWjBYMFYGBmeBDAEEATBM
# MCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2NwczAlBggrBgEFBQcC
# AjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTAfBgNVHSMEGDAWgBTUwAYiSes5
# S92T4lyhuEd2CXIDWDArBgNVHR8EJDAiMCCgHqAchhpodHRwOi8vcmIuc3ltY2Iu
# Y29tL3JiLmNybDBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0dHA6Ly9y
# Yi5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly9yYi5zeW1jYi5jb20vcmIu
# Y3J0MA0GCSqGSIb3DQEBCwUAA4IBAQC+C4TcK44ph2QQK/1f65jOR23DtSBC3y9a
# bzRHdo4qxmcAW5ot69os7GJfzgVsA5lh1IT4+aMuGYA4GTcF6iTSOMgFSRwP8urI
# N2BprsWuMJsQ7+Flo3PBRN3dU6idOlFKOfuRxgIHRn47t2yRan6XTNhfiWl84DrD
# NjSTnk4c72Gzu0hiwQB9OTsf8CQP3Shb3ZzcAOmeUB01TFoJU34PfJpKlKQZeQIi
# W5WdPPr1G/0cAHgejDHtdNYcSqIWfoGeYgCxUg1IFpp1VmPlqb/de8QKONzPDK6/
# 5hulSGqGgpRmEkwGGJiQeOB51GxYZRCPq3hN3UJ6N0A+hYzj7yspMIIFRzCCBC+g
# AwIBAgIQfBs1NUrn23TnQV8RacprqDANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE
# BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln
# biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5j
# LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBV
# bml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNDA3MjIw
# MDAwMDBaFw0yNDA3MjEyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEdMBsGA1UEChMU
# U3ltYW50ZWMgQ29ycG9yYXRpb24xHzAdBgNVBAsTFlN5bWFudGVjIFRydXN0IE5l
# dHdvcmsxNTAzBgNVBAMTLFN5bWFudGVjIENsYXNzIDMgU0hBMjU2IENvZGUgU2ln
# bmluZyBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15VD
# 1NzfZ645+1KktiYxBHDpt45bKro3aTWVj7vAMOeG2HO73+vRdj+KVo7rLUvwVxhO
# sY2lM9MLdSPVankn3aPT9w6HZbXerRzx9TW0IlGvIqHBXUuQf8BZTqudeakC1x5J
# sTtNh/7CeKu/71KunK8I2TnlmlE+aV8wEE5xY2xY4fAgMxsPdL5byxLh24zEgJRy
# u/ZFmp7BJQv7oxye2KYJcHHswEdMj33D3hnOPu4Eco4X0//wsgUyGUzTsByf/qV4
# IEJwQbAmjG8AyDoAEUF6QbCnipEEoJl49He082Aq5mxQBLcUYP8NUfSoi4T+Idpc
# Xn31KXlPsER0b21y/wIDAQABo4IBeDCCAXQwLgYIKwYBBQUHAQEEIjAgMB4GCCsG
# AQUFBzABhhJodHRwOi8vcy5zeW1jZC5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBm
# BgNVHSAEXzBdMFsGC2CGSAGG+EUBBxcDMEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8v
# ZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5j
# b20vcnBhMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9zLnN5bWNiLmNvbS91bml2
# ZXJzYWwtcm9vdC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDgYDVR0PAQH/BAQD
# AgEGMCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTeW1hbnRlY1BLSS0xLTcyNDAd
# BgNVHQ4EFgQU1MAGIknrOUvdk+JcobhHdglyA1gwHwYDVR0jBBgwFoAUtnf6aUhH
# n1MS1cLqBzJ2B9GXBxkwDQYJKoZIhvcNAQELBQADggEBAH/ryqfqi3ZC6z6OIFQw
# 47e53PpIPhbHD0WVEM0nhqNm8wLtcfiqwlWXkXCD+VJ+Umk8yfHglEaAGLuh1KRW
# pvMdAJHVhvNIh+DLxDRoIF60y/kF7ZyvcFMnueg+flGgaXGL3FHtgDolMp9Er25D
# KNMhdbuX2IuLjP6pBEYEhfcVnEsRjcQsF/7Vbn+a4laS8ZazrS359N/aiZnOsjhE
# wPdHe8olufoqaDObUHLeqJ/UzSwLNL2LMHhA4I2OJxuQbxq+CBWBXesv4lHnUR7J
# eCnnHmW/OO8BSgEJJA4WxBR5wUE3NNA9kVKUneFo7wjw4mmcZ26QCxqTcdQmAsPA
# WiMwggZqMIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUA
# MGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsT
# EHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQg
# Q0EtMTAeFw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYT
# AlVTMREwDwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0
# YW1wIFJlc3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNk
# Xfx8s+CCNeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85v
# Zu7dy4XpX6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmej
# vmGfrvP9Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFy
# GGi5uHzu5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjH
# yQYuRhDIjegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr
# 7GJCkawCwO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNV
# HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCC
# AbIwggGhBglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
# aWdpY2VydC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUA
# cwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMA
# bwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYA
# IAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQA
# IAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUA
# bQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkA
# dAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAA
# aABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG
# /WwDFTAfBgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQU
# YVpNJLZJMp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSG
# Mmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEu
# Y3JsMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGln
# aWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5j
# b20vRGlnaUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEA
# nSV+GzNNsiaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA
# +94GAYw3+puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n
# 4hFBpr1i2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OP
# Q6dxnSHdFMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xI
# J2bIo4sKHOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g
# 9BQKOhvjjz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhsw
# DQYJKoZIhvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
# IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNl
# cnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAw
# MDAwMFowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJl
# ZCBJRCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnK
# wkKVpYBzQHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InE
# Ctqvy15r7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH
# 6zY/2DDD/6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77b
# lUjE7h6z8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnx
# TPKvmPv2zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2Kk
# PzIUYJX9BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0
# MDIGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF
# BQcDCDCCAdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYB
# BQUHAgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9y
# eS5odG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYA
# IAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkA
# dAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAA
# RABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAA
# UgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAA
# dwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4A
# ZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkA
# bgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1Ud
# EwEB/wQIMAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRw
# Oi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1Ud
# HwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz
# c3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZ
# B+0e36K+Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0G
# CSqGSIb3DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/s
# dR90OPKyXGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZ
# Vann4+erYs37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQl
# c+Qqq8+cdkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dF
# zdaD7eeSDY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsY
# Gk38KiGtSTGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEXzCCBFsCAQEwgZkw
# gYQxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEf
# MB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazE1MDMGA1UEAxMsU3ltYW50
# ZWMgQ2xhc3MgMyBTSEEyNTYgQ29kZSBTaWduaW5nIENBIC0gRzICECt3g4UAT0ym
# kcB/NZDAe38wDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAA
# oQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4w
# DAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgrRD5jl2p0D1Vsk6OQtaZAJOi
# QSzZ9iun/BP/5HbRiMwwDQYJKoZIhvcNAQEBBQAEggEAiGz9a2ixV8GhQqvIYrwP
# j54LNNk9ocZXeIqnUcqCwPKTeSGa640L3ohue4XTkbZ8C5QXetRHUy6IE0MObGgG
# rI7iepPadrm10PhBINowr5S1iZ/KkFyKwgKN0ZT0zpFloPyoeuSm46HLYtmPy83/
# dx/4Gf+nB+sjyp5YEn6UVQpYrByyAUGxH1PKxPBRA/BNCzAQ+T2JqVCihHhrxumm
# JYcAW62gyGWGrK2YTrzkEx7KAN4RPhZQPwWu1j+AZG2ERZ03SPW7llovG5mWDu3n
# A/NrqetaHY55Rlp7X4vWCpjLEdOywMMjmbjVQurA4zgioHBEplt5CZ6dQPgiE82m
# 5qGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNVBAYTAlVT
# MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
# b20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGaAjr/WLFr
# 1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMTgwOTExMTcyOTQ1WjAjBgkqhkiG9w0BCQQxFgQUbM3p
# A/CXTTOeldSz91PyaSTXYvUwDQYJKoZIhvcNAQEBBQAEggEADNrdB2lG/VK326gf
# eJODt36fsNVmYQiigGs4LBrPhmDWSflPnqmoVF5LveseELfTOFEVfdABEdBS0JeZ
# Ftk0Fa8bOILi78DfC5lU8g2fHnoS/5E07pK9WAGp1HA1Bzakmjcvh7NHi0kHvvGF
# as9TonyLGZHm0zn0l9cWLOy36Uw1RxXhllJifUizwNrkktOR8L0tRnBsCgByLgZR
# XPw0JzWGgnGy02zcfHwFHGUxhb0ymP0/Vdu+WMKgcuWaPePNP5uW2P1f3/idPIei
# f8hWbcYA8Zudjv0r/BNQpboaOVmDjwmM8mhJv0T+K345djW6kIabrJApt+u2AMFx
# xvdiLA==
# SIG # End signature block