VaporShell.psm1

[CmdletBinding()]
Param ()
$VaporshellPath = $PSScriptRoot
function Add-ObjectDetail {
    <#
    .SYNOPSIS
        Decorate an object with
            - A TypeName
            - New properties
            - Default parameters

        ** This is a stripped down version of the function built out by Warren Frame -- http://ramblingcookiemonster.github.io/Decorating-Objects/ | https://github.com/RamblingCookieMonster/PSStackExchange/blob/master/PSStackExchange/Private/Add-ObjectDetail.ps1

    .DESCRIPTION
        Helper function to decorate an object with
            - A TypeName
            - New properties
            - Default parameters

    .PARAMETER InputObject
        Object to decorate. Accepts pipeline input.

    .PARAMETER TypeName
        Typenames to insert.
        
        This will show up when you use Get-Member against the resulting object.

    .PARAMETER Passthru
        Whether to pass the resulting object on. Defaults to true

    .NOTES
        This breaks the 'do one thing' rule from certain perspectives...
        The goal is to decorate an object all in one shot
   
        This abstraction simplifies decorating an object, with a slight trade-off in performance. For example:

        10,000 objects, add a property and typename:
            Add-ObjectDetail: ~4.6 seconds
            Add-Member + PSObject.TypeNames.Insert: ~3 seconds

        Initial code borrowed from Shay Levy:
        http://blogs.microsoft.co.il/scriptfanatic/2012/04/13/custom-objects-default-display-in-powershell-30/
        
    .LINK
        http://ramblingcookiemonster.github.io/Decorating-Objects/

    .FUNCTIONALITY
        PowerShell Language
    #>

    [CmdletBinding()] 
    param(
        [Parameter( Mandatory = $true,
            Position = 0,
            ValueFromPipeline = $true )]
        [ValidateNotNullOrEmpty()]
        [psobject[]]$InputObject,

        [Parameter( Mandatory = $true,
            Position = 1)]
        [string[]]$TypeName,

        [boolean]$Passthru = $True
    )
    
    Process {
        foreach ($Object in $InputObject) {
            #Add specified type(s)
            foreach ($T in $TypeName) {
                [void]$Object.PSObject.TypeNames.Insert(0,$T)
            }
            if ($Passthru) {
                $Object
            }
        }
    }
}

function Colorize {
    Param
    (
        [parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $true)]
        [String[]]
        $Strings
    )
    Process {
        foreach ($row in $Strings) {
            $hostParams = @{
                Object   = $row
                NoNewLine = $true
            }
            switch -Regex -CaseSensitive ($row) {
                "(STACK NAME|REFRESH)" {
                    $hostParams['ForegroundColor'] = 'Black'
                    $hostParams['BackgroundColor'] = 'Cyan'
                }
                "_FAILED" {
                    if ($row -match 'AWS::CloudFormation::Stack') {
                        $hostParams['ForegroundColor'] = 'White'
                        $hostParams['BackgroundColor'] = 'DarkRed'
                    }
                    else {
                        $hostParams['ForegroundColor'] = 'Black'
                        $hostParams['BackgroundColor'] = 'Red'
                    }
                }
                "REVIEW_IN_PROGRESS" {
                    $hostParams['ForegroundColor'] = 'Black'
                    $hostParams['BackgroundColor'] = 'Red'
                }
                "ROLLBACK_IN_PROGRESS" {
                    $hostParams['ForegroundColor'] = 'Cyan'
                }
                "ROLLBACK_COMPLETE" {
                    $hostParams['ForegroundColor'] = 'Black'
                    $hostParams['BackgroundColor'] = 'Cyan'
                }
                "(UPDATE_IN_PROGRESS|CREATE_IN_PROGRESS|IMPORT_IN_PROGRESS)" {
                    if ($row -match 'AWS::CloudFormation::Stack') {
                        $hostParams['ForegroundColor'] = 'White'
                        $hostParams['BackgroundColor'] = 'DarkMagenta'
                    }
                    else {
                        $hostParams['ForegroundColor'] = 'Green'
                    }
                }
                "(UPDATE_COMPLETE|CREATE_COMPLETE|IMPORT_COMPLETE)" {
                    if ($row -match 'AWS::CloudFormation::Stack') {
                        $hostParams['ForegroundColor'] = 'White'
                        $hostParams['BackgroundColor'] = 'DarkGreen'
                    }
                    else {
                        $hostParams['ForegroundColor'] = 'Black'
                        $hostParams['BackgroundColor'] = 'Green'
                    }
                }
                "DELETE_IN_PROGRESS" {
                    $hostParams['ForegroundColor'] = 'Yellow'
                }
                "DELETE_COMPLETE" {
                    $hostParams['ForegroundColor'] = 'Black'
                    $hostParams['BackgroundColor'] = 'Yellow'
                }
                "DELETE_SKIPPED" {
                    $hostParams['ForegroundColor'] = 'Black'
                    $hostParams['BackgroundColor'] = 'Magenta'
                }
            }
            Write-Host @hostParams
            Write-Host ''
        }
    }
}

function Format-Json {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [String]
        $Json
    )
    Begin {
        $cleaner = {
            param([String]$Line)
            Process{
                [Regex]::Replace(
                    $Line,
                    "\\u(?<Value>[a-zA-Z0-9]{4})",
                    {
                        param($m)([char]([int]::Parse(
                            $m.Groups['Value'].Value,
                            [System.Globalization.NumberStyles]::HexNumber
                        ))).ToString()
                    }
                )
            }
        }
    }
    Process {
        if ($PSVersionTable.PSVersion.Major -lt 6) {
            try {
                $indent = 0;
                $res = $Json -split '\n' | ForEach-Object {
                    if ($_ -match '[\}\]]') {
                        # This line contains ] or }, decrement the indentation level
                        $indent--
                    }
                    $line = (' ' * $indent * 2) + $_.TrimStart().Replace(': ', ': ')
                    if ($_ -match '[\{\[]') {
                        # This line contains [ or {, increment the indentation level
                        $indent++
                    }
                    $cleaner.Invoke($line)
                }
                $res -join "`n"
            }
            catch {
                ($Json -split '\n' | ForEach-Object {$cleaner.Invoke($_)}) -join "`n"
            }
        }
        else {
            ($Json -split '\n' | ForEach-Object {$cleaner.Invoke($_)}) -join "`n"
        }
    }
}

function Get-TrueCount {
    Param
    (
        [parameter(Mandatory = $false,Position = 0,ValueFromPipeline = $true)]
        $Array
    )
    Process {
        if ($array) {
            if ($array.Count) {
                $count = $array.Count
            }
            else {
                $count = 1
            }
        }
        else {
            $count = 0
        }
    }
    End {
        return $count
    }
}

function Import-AWSSDK {
    [CmdletBinding()]
    Param()
    Process {
        # Load the AWSSDK assemblies without conflict and kill any warning messages thrown by AWS.Tools.* modules
        try {
            $currentWarningPref = $WarningPreference
            $WarningPreference = "SilentlyContinue"
            $currentErrorPref = $ErrorActionPreference
            $ErrorActionPreference = "SilentlyContinue"
            $awsModules = if ($tools = (Get-Module AWS.Tools* -ListAvailable -Verbose:$false).Name | Where-Object {$_ -match '^AWS\.Tools\.(CloudFormation|S3)$'}) {
                $tools | Select-Object -Unique
            }
            else {
                (Get-Module AWS* -ListAvailable -Verbose:$false).Name | Select-Object -Unique
            }
            @(
                'AWSSDK.CloudFormation.dll'
                'AWSSDK.S3.dll'
            ) | ForEach-Object {
                $assemblyName = $_
                if ($null -eq ([System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object {$_.Location -match $assemblyName})) {
                    $toolsModule = switch ($assemblyName) {
                        'AWSSDK.CloudFormation.dll' {'AWS.Tools.CloudFormation'}
                        'AWSSDK.S3.dll' {'AWS.Tools.S3'}
                    }
                    if ($awsModules -contains $toolsModule) {
                        Write-Verbose "Importing $assemblyName via module $toolsModule"
                        Import-Module $toolsModule -Verbose:$false -ErrorAction SilentlyContinue
                    }
                    elseif ($awsModules -contains 'AWSPowerShell.NetCore') {
                        Write-Verbose "Importing $assemblyName via module AWSPowerShell.NetCore"
                        Import-Module 'AWSPowerShell.NetCore' -Verbose:$false -ErrorAction SilentlyContinue
                    }
                    elseif ($awsModules -contains 'AWSPowerShell') {
                        Write-Verbose "Importing $assemblyName via module AWSPowerShell"
                        Import-Module 'AWSPowerShell' -Verbose:$false -ErrorAction SilentlyContinue
                    }
                    else {
                        Write-Verbose "Importing $assemblyName from VaporShell module base"
                        [System.Reflection.Assembly]::LoadFrom((Join-Path $PSScriptRoot $assemblyName)) | Out-Null
                    }
                }
            }
        }
        catch {}
        finally {
            $WarningPreference = $currentWarningPref
            $ErrorActionPreference = $currentErrorPref
        }
    }
}

function New-VSError {
    <#
    .SYNOPSIS
    Error generator function to use in tandem with $PSCmdlet.ThrowTerminatingError()
    
    .PARAMETER Result
    Allows input of an error from AWS SDK, resulting in the Exception message being parsed out.
    
    .PARAMETER String
    Used to create basic String message errors in the same wrapper
    #>

    [cmdletbinding(DefaultParameterSetName="Result")]
    param(
        [parameter(Position=0,ParameterSetName="Result")]
        $Result,
        [parameter(Position=0,ParameterSetName="String")]
        $String
    )
    switch ($PSCmdlet.ParameterSetName) {
        Result { $Exception = "$($result.Exception.InnerException.Message)" }
        String { $Exception = "$String" }
    }
    $e = New-Object "System.Exception" $Exception
    $errorRecord = New-Object 'System.Management.Automation.ErrorRecord' $e, $null, ([System.Management.Automation.ErrorCategory]::InvalidOperation), $null
    return $errorRecord
}

function ProcessRequest {
    <#
    .SYNOPSIS
    Receives AWS SDK requests, then sends them to the appropriate processor function depending on PowerShell version, as PSv3 does not allow dot sourcing method names.
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false,Position=0)]
        [String]
        $ParameterSetName,
        [parameter(Mandatory = $false,Position=1)]
        [String]
        $ProfileName = $env:AWS_PROFILE,
        [parameter(Mandatory = $true,Position=2)]
        [String]
        $Method,
        [parameter(Mandatory = $true,Position=3)]
        $Request,
        [parameter(Mandatory = $false,Position=4)]
        [String]
        $Expand
    )
    Process {
        if ($PSVersionTable.PSVersion.Major -eq 3) {
            ProcessRequest3 @PSBoundParameters
        }
        else {
            ProcessRequest4 @PSBoundParameters
        }
    }
}

function ProcessRequest3 {
    <#
    .SYNOPSIS
    Receives AWS SDK requests, then sends them to the appropriate processor function depending on PowerShell version, as PSv3 does not allow dot sourcing method names.
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false,Position=0)]
        [String]
        $ParameterSetName,
        [parameter(Mandatory = $false,Position=1)]
        [String]
        $ProfileName = $env:AWS_PROFILE,
        [parameter(Mandatory = $true,Position=2)]
        [String]
        $Method,
        [parameter(Mandatory = $true,Position=3)]
        $Request,
        [parameter(Mandatory = $false,Position=4)]
        [String]
        $Expand
    )
    Process {
        if (!$ProfileName) {
            $ProfileName = "default"
            $PSBoundParameters["ProfileName"] = "default"
        }
        $results = @()
        try {
            $service = ($request.PSObject.TypeNames)[0].split('.')[1]
            $sharedFile = New-Object Amazon.Runtime.CredentialManagement.SharedCredentialsFile -ErrorAction Stop
            $matchedProfile = $sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}
            if ($null -eq $matchedProfile) {
                $creds = [Amazon.Runtime.FallbackCredentialsFactory]::GetCredentials()
                $endPoint = if ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
                    [Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
                }
                else {
                    # Need to set a default if we can't resolve the region
                    Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
                    Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
                    Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
                    Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
                    Start-Sleep -Seconds 5
                    [Amazon.RegionEndpoint]::USEast1
                }
            }
            else {
                $creds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
                $endPoint = if ($matchedProfile.Region) {
                    $matchedProfile.Region
                }
                elseif ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
                    [Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
                }
                else {
                    # Need to set a default if we can't resolve the region
                    Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
                    Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
                    Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
                    Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
                    Start-Sleep -Seconds 5
                    [Amazon.RegionEndpoint]::USEast1
                }
            }
            Write-Verbose "Building '$service' client in region '$($endPoint.DisplayName)' [$($endPoint.SystemName)]"
            if ($endPoint) {
                $client = New-Object "Amazon.$($service).Amazon$($service)Client" $creds,$endPoint -ErrorAction Stop
            }
            else {
                return (New-VSError -String "No region set for profile '$ProfileName'! Please run the following to set a region:`n`nSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>")
            }
        }
        catch {
            return (New-VSError -String "$($_.Exception.Message)")
        }
        Write-Verbose "Processing request:`n$($PSBoundParameters | Format-Table -AutoSize | Out-String)"
        $i = 0
        do {
            $i++
            $result = $client.PSObject.Methods[$Method].Invoke($Request)
            if ($Expand) {
                $results += $result.$Expand
            }
            else {
                $results += $result
            }
            if ($result.NextToken -and !$request.MaxResults) {
                $Request.NextToken = $result.NextToken
                $done = $false
            }
            else {
                $done = $true
            }
        }
        until ($done)
        if (!$result) {
            return
        }
        if ($results) {
            return $results
        }
        elseif ($IsCoreCLR) {
            if ($result.Result) {
                return $result.Result
            }
            elseif ($result.Exception) {
                return (New-VSError $result)
            }
        }
        else {
            return $result
        }
    }
}

function ProcessRequest4 {
    <#
    .SYNOPSIS
    Receives AWS SDK requests, then sends them to the appropriate processor function depending on PowerShell version, as PSv3 does not allow dot sourcing method names.
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false,Position=0)]
        [String]
        $ParameterSetName,
        [parameter(Mandatory = $false,Position=1)]
        [String]
        $ProfileName = $env:AWS_PROFILE,
        [parameter(Mandatory = $true,Position=2)]
        [String]
        $Method,
        [parameter(Mandatory = $true,Position=3)]
        $Request,
        [parameter(Mandatory = $false,Position=4)]
        [String]
        $Expand
    )
    Process {
        if (!$ProfileName) {
            $ProfileName = "default"
            $PSBoundParameters["ProfileName"] = "default"
        }
        $results = @()
        try {
            $service = ($request.PSObject.TypeNames)[0].split('.')[1]
            $sharedFile = New-Object Amazon.Runtime.CredentialManagement.SharedCredentialsFile -ErrorAction Stop
            $matchedProfile = $sharedFile.ListProfiles() | Where-Object {$_.Name -eq $ProfileName}
            if ($null -eq $matchedProfile) {
                $creds = [Amazon.Runtime.FallbackCredentialsFactory]::GetCredentials()
                $endPoint = if ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
                    [Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
                }
                else {
                    # Need to set a default if we can't resolve the region
                    Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
                    Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
                    Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
                    Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
                    Start-Sleep -Seconds 5
                    [Amazon.RegionEndpoint]::USEast1
                }
            }
            else {
                $creds = New-Object Amazon.Runtime.StoredProfileAWSCredentials $ProfileName -ErrorAction Stop
                $endPoint = if ($matchedProfile.Region) {
                    $matchedProfile.Region
                }
                elseif ([Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()) {
                    [Amazon.Runtime.FallbackRegionFactory]::GetRegionEndpoint()
                }
                else {
                    # Need to set a default if we can't resolve the region
                    Write-Warning "Unable to resolve target region! Defaulting to us-east-1 and continuing in 5 seconds."
                    Write-Warning "If you do not want to execute method [$Method] on service [$service] in this region,"
                    Write-Warning "please set the environment variable 'AWS_REGION' or run the following to set a region"
                    Write-Warning "on the shared credential file:`n`n`tSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>"
                    Start-Sleep -Seconds 5
                    [Amazon.RegionEndpoint]::USEast1
                }
            }
            Write-Verbose "Building '$service' client in region '$($endPoint.DisplayName)' [$($endPoint.SystemName)]"
            if ($endPoint) {
                $client = New-Object "Amazon.$($service).Amazon$($service)Client" $creds,$endPoint -ErrorAction Stop
            }
            else {
                return (New-VSError -String "No region set for profile '$ProfileName'! Please run the following to set a region:`n`nSet-VSCredential -ProfileName $ProfileName -Region <PREFERRED REGION>")
            }
        }
        catch {
            return (New-VSError -String "$($_.Exception.Message)")
        }
        if ($client | Get-Member -MemberType Method -Name "$Method*" | Where-Object {$_.Name -eq "$($Method)Async"}) {
            $useAsync = $true
            Write-Verbose "Processing async request:`n$($PSBoundParameters | Format-Table -AutoSize | Out-String)"
        }
        else {
            $useAsync = $false
            Write-Verbose "Processing request:`n$($PSBoundParameters | Format-Table -AutoSize | Out-String)"
        }
        $i = 0
        do {
            $i++
            if ($useAsync) {
                $result = $client."$($Method)Async"($Request)
                if ($Expand) {
                    $results += $result.Result.$Expand
                }
                else {
                    $results += $result.Result
                }
            }
            else {
                $result = $client.$Method($Request)
                if ($Expand) {
                    $results += $result.$Expand
                }
                else {
                    $results += $result
                }
            }
            if ($result.Result.NextToken -and !$request.MaxResults) {
                $Request.NextToken = $result.Result.NextToken
                $done = $false
            }
            elseif ($result.NextToken -and !$request.MaxResults) {
                $Request.NextToken = $result.NextToken
                $done = $false
            }
            else {
                $done = $true
            }
        }
        until ($done)
        if (!$result) {
            return
        }
        if ($results) {
            return $results
        }
        elseif ($IsCoreCLR) {
            if ($result.Result) {
                return $result.Result
            }
            elseif ($result.Exception) {
                return (New-VSError $result)
            }
        }
        else {
            return $result
        }
    }
}

function ResolveS3Endpoint {
    <#
    .SYNOPSIS
    Resolves the S3 endpoint most appropriate for each region.
    #>

    Param
    (
      [parameter(Mandatory=$true,Position=0)]
      [ValidateSet("eu-west-2","ap-south-1","us-east-2","sa-east-1","us-west-1","us-west-2","eu-west-1","ap-southeast-2","ca-central-1","ap-northeast-2","us-east-1","eu-central-1","ap-southeast-1","ap-northeast-1")]
      [String]
      $Region
    )
    $endpointMap = @{
        "us-east-2" = "s3.us-east-2.amazonaws.com"
        "us-east-1" = "s3.amazonaws.com"
        "us-west-1" = "s3-us-west-1.amazonaws.com"
        "us-west-2" = "s3-us-west-2.amazonaws.com"
        "ca-central-1" = "s3.ca-central-1.amazonaws.com"
        "ap-south-1" = "s3.ap-south-1.amazonaws.com"
        "ap-northeast-2" = "s3.ap-northeast-2.amazonaws.com"
        "ap-southeast-1" = "s3-ap-southeast-1.amazonaws.com"
        "ap-southeast-2" = "s3-ap-southeast-2.amazonaws.com"
        "ap-northeast-1" = "s3-ap-northeast-1.amazonaws.com"
        "eu-central-1" = "s3.eu-central-1.amazonaws.com"
        "eu-west-1" = "s3-eu-west-1.amazonaws.com"
        "eu-west-2" = "s3.eu-west-2.amazonaws.com"
        "sa-east-1" = "s3-sa-east-1.amazonaws.com"
    }
    return $endpointMap[$Region]
}

function Add-ConAnd {
    <#
    .SYNOPSIS
        Adds the condition function "Fn::And" to a resource property

    .DESCRIPTION
        Returns true if all the specified conditions evaluate to true, or returns false if any one of the conditions evaluates to false. Fn::And acts as an AND operator. The minimum number of conditions that you can include is 2, and the maximum is 10.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-and

    .PARAMETER Conditions
        A collection of conditions in object form where each evaluates to true or false. There must be at least 2 conditions but no more than 10 defined.

    .EXAMPLE
        Add-ConAnd -Conditions (Add-ConEquals -FirstValue "sg-mysggroup" -SecondValue (Add-FnRef -Ref "ASecurityGroup")),(Add-ConEquals -FirstValue "Production" -SecondValue (Add-FnRef -Ref "Environment"))

        When the template is exported, this will convert to: {"Fn::And":[{"Fn::Equals":["sg-mysggroup",{"Ref":"ASecurityGroup"}]},{"Fn::Equals":["Production",{"Ref":"Environment"}]}]}

    .NOTES
        You can use the following functions in this condition statement:
            Fn::FindInMap
            Ref
            Other condition functions

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConAnd])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory = $true,Position = 0)]
        [ValidateCount(2,10)]
        [object[]]
        $Conditions
    )
    $obj = [ConAnd]::new($Conditions)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConAnd'

function Add-ConEquals {
    <#
    .SYNOPSIS
        Adds the condition function "Fn::Equals" to a resource property

    .DESCRIPTION
        Compares if two values are equal. Returns true if the two values are equal or false if they aren't.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-equals

    .PARAMETER FirstValue
        A value of any type that you want to compare against the SecondValue

    .PARAMETER SecondValue
        A value of any type that you want to compare against the FirstValue

    .EXAMPLE
        Add-ConEquals -FirstValue "sg-mysggroup" -SecondValue (Add-FnRef -Ref "ASecurityGroup")

        When the template is exported, this will convert to: {"Fn::Equals":["sg-mysggroup",{"Ref":"ASecurityGroup"}]}

    .EXAMPLE
        Add-ConEquals -FirstValue (Add-FnRef -Ref "EnvironmentType") -SecondValue "prod"

        When the template is exported, this will convert to: {"Fn::Equals":[{"Ref":"EnvironmentType"},"prod"]}

    .NOTES
        You can use the following functions in this condition statement:
            Fn::FindInMap
            Ref
            Other condition functions

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConEquals])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object]
        $FirstValue,
        [parameter(Mandatory,Position = 1)]
        [object]
        $SecondValue
    )
    $obj = [ConEquals]::new($FirstValue,$SecondValue)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConEquals'

function Add-ConIf {
    <#
    .SYNOPSIS
        Adds the condition function "Fn::If" to a resource property

    .DESCRIPTION
        Returns one value if the specified condition evaluates to true and another value if the specified condition evaluates to false. Currently, AWS CloudFormation supports the Fn::If intrinsic function in the metadata attribute, update policy attribute, and property values in the Resources section and Outputs sections of a template. You can use the AWS::NoValue pseudo parameter as a return value to remove the corresponding property.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-if

    .PARAMETER ConditionName
        A reference to a condition in the Conditions section. Use the condition's name to reference it.

    .PARAMETER ValueIfTrue
        A value to be returned if the specified condition evaluates to true.

    .PARAMETER ValueIfFalse
        A value to be returned if the specified condition evaluates to false.

    .EXAMPLE
        Add-ConIf -ConditionName "CreateNewSecurityGroup" -ValueIfTrue (Add-FnRef -Ref "NewSecurityGroup") -ValueIfFalse (Add-FnRef -Ref "ExistingSecurityGroup")

        When the template is exported, this will convert to: {"Fn::If":["CreateNewSecurityGroup",{"Ref":"NewSecurityGroup"},{"Ref":"ExistingSecurityGroup"}]}

    .NOTES
        You can use the following functions in the Fn::If condition:
            Fn::Base64
            Fn::FindInMap
            Fn::GetAtt
            Fn::GetAZs
            Fn::If
            Fn::Join
            Fn::Select
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConIf])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $ConditionName,
        [parameter(Mandatory,Position = 1)]
        [object]
        $ValueIfTrue,
        [parameter(Mandatory,Position = 2)]
        [object]
        $ValueIfFalse
    )
    $obj = [ConIf]::new($ConditionName,$ValueIfTrue,$ValueIfFalse)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConIf'

function Add-ConNot {
    <#
    .SYNOPSIS
        Adds the condition function "Fn::Not" to a resource property

    .DESCRIPTION
        Returns true for a condition that evaluates to false or returns false for a condition that evaluates to true. Fn::Not acts as a NOT operator.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-not

    .PARAMETER Condition
        A condition such as Fn::Equals that evaluates to true or false.

    .EXAMPLE
        Add-ConNot -Condition (Add-ConEquals -FirstValue (Add-FnRef -Ref "EnvironmentType") -SecondValue "prod")

        When the template is exported, this will convert to: {"Fn::Equals":["sg-mysggroup",{"Ref":"ASecurityGroup"}]}

    .NOTES
        You can use the following functions in this condition statement:
            Fn::FindInMap
            Ref
            Other condition functions

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConNot])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object[]]
        $Conditions
    )
    $obj = [ConNot]::new($Conditions)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConNot'

function Add-ConOr {
    <#
    .SYNOPSIS
        Adds the condition function "Fn::Or" to a resource property

    .DESCRIPTION
        Returns true if any one of the specified conditions evaluate to true, or returns false if all of the conditions evaluates to false. Fn::Or acts as an OR operator. The minimum number of conditions that you can include is 2, and the maximum is 10.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-or

    .PARAMETER Condition
        A condition such as Fn::Equals that evaluates to true or false.

    .EXAMPLE
        Add-ConOr -Conditions (Add-ConEquals -FirstValue "sg-mysggroup" -SecondValue (Add-FnRef -Ref "ASecurityGroup")),(Add-ConEquals -FirstValue "Production" -SecondValue (Add-FnRef -Ref "Environment"))

        When the template is exported, this will convert to: {"Fn::Or":[{"Fn::Equals":["sg-mysggroup",{"Ref":"ASecurityGroup"}]},{"Fn::Equals":["Production",{"Ref":"Environment"}]}]}

    .NOTES
        You can use the following functions in this condition statement:
            Fn::FindInMap
            Ref
            Other condition functions

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConOr])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object[]]
        $Conditions
    )
    $obj = [ConOr]::new($Conditions)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConOr'

function Add-ConRef {
    <#
    .SYNOPSIS
        Adds the condition helper function "Condition" to a resource property to reference a condition on the stack by LogicalId.

    .DESCRIPTION
        Adds the condition helper function "Condition" to a resource property to reference a condition on the stack by LogicalId.

    .LINK
        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#intrinsic-function-reference-conditions-or

    .PARAMETER Ref
        The logical name of the condition you want to reference.

    .EXAMPLE

        Add-ConRef -Condition "HasSSHKey"

        When the template is exported, this will convert to: {"Condition":"HasSSHKey"}

    .NOTES
        You cannot use any functions in the Condition function. You must specify a string that is a condition logical ID.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([ConRef])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $Condition
    )
    $obj = [ConRef]::new($Condition)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-ConRef'

function Export-Vaporshell {
    <#
    .SYNOPSIS
        Exports the template object to JSON file.

    .DESCRIPTION
        Exports the template object to JSON file.

        Requires the Vaporshell input object to be type 'Vaporshell.Template'

    .PARAMETER VaporshellTemplate
        The input template object

    .PARAMETER As
        Specify JSON or YAML for your preferred output. Defaults to JSON.

        **Important**: In order to use YAML, you must have cfn-flip installed: https://github.com/awslabs/aws-cfn-template-flip

    .PARAMETER Path
        Path to save the resulting JSON file.

    .PARAMETER ValidateTemplate
        Validates the template using the AWS .NET SDK

    .PARAMETER Force
        Forces an overwrite if the Path already exists

    .EXAMPLE
        $Template = Initialize-Vaporshell -Description "This is a sample template that builds an S3 bucket"
        # Add items to the $Template object here
        $Template | Export-Vaporshell -Path "C:\CloudFormation\Templates\S3Bucket.json" -Force

    .EXAMPLE
        $Template = Initialize-Vaporshell -Description "This is a sample template that builds an S3 bucket"
        # Add items to the $Template object here
        Export-Vaporshell -VaporshellTemplate $Template -Path "C:\CloudFormation\Templates\S3Bucket.json" -Force

    .FUNCTIONALITY
        Vaporshell
    #>

    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0,ValueFromPipeline)]
        [VSTemplate]
        $VaporshellTemplate,
        [parameter(Position = 1)]
        [ValidateSet("JSON","YAML")]
        [string]
        $As = "JSON",
        [parameter(Position = 2)]
        [string]
        $Path,
        [parameter()]
        [switch]
        $ValidateTemplate,
        [parameter()]
        [switch]
        $Force
    )
    Begin {
        if (-not $PSBoundParameters.ContainsKey('As')) {
            $As = if (-not $PSBoundParameters.ContainsKey('Path')) {
                'JSON'
            }
            else {
                switch -RegEx ($Path) {
                    '\.(yml|yaml)$' {
                        'YAML'
                    }
                    default {
                        'JSON'
                    }
                }
            }
        }
    }
    Process {
        Write-Verbose "Converting template object to $As"
        $final = $VaporshellTemplate.Export($true, $As)
    }
    End {
        if ($ValidateTemplate) {
            Get-TemplateValidation -TemplateBody ($Final -join [System.Environment]::NewLine)
        }
        if ($Path) {
            Write-Verbose "Exporting $As template to: $Path"
            $VaporshellTemplate.Export($Path, $As, $Force)
        }
        else {
            return ($Final -join [System.Environment]::NewLine)
        }
    }
}

Export-ModuleMember -Function 'Export-Vaporshell'

function Import-Vaporshell {
    <#
    .SYNOPSIS
        Allows you to import an existing CloudFormation template as a starting point.

    .DESCRIPTION
        Allows you to import an existing CloudFormation template as a starting point.

    .PARAMETER Path
        The path to the existing template.

    .EXAMPLE
        $Template = Import-Vaporshell -Path "C:\CloudFormation\Templates\S3Bucket.json"

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSTemplate])]
    [cmdletbinding(DefaultParameterSetName = "Path")]
    Param
    (
        [parameter(Mandatory,Position = 0,ParameterSetName = "Path")]
        [Alias("FullName")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory,Position = 0,ValueFromPipeline,ParameterSetName = "TemplateBody")]
        [String]
        $TemplateBody,
        [parameter(Mandatory,Position = 0,ParameterSetName = "RawUrl")]
        [String]
        $RawUrl
    )
    Process {
        $finalString = $PSBoundParameters[$PSCmdlet.ParameterSetName] -join [System.Environment]::NewLine
        $obj = [VSTemplate]::new($finalString)
        Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
        $obj
    }
}

Export-ModuleMember -Function 'Import-Vaporshell'

function Import-VSTemplateConfig {
    <#
    .SYNOPSIS
        Imports a configuration from either a JSON or PSD1 file at the specified key.

    .DESCRIPTION
        Imports a configuration from either a JSON or PSD1 file at the specified key.

    .PARAMETER Path
        The path to the configuration file. This can be relative or absolute

    .PARAMETER Key
        The key of the sub-configuration to return, i.e. "Dev".

    .EXAMPLE
        Import-VSTemplateConfig -Path .\WebServerConfig.psd1 -Key Prd

        Imports the "Prd" sub-configuration from the WebServerConfig.psd1 configuration file

    .FUNCTIONALITY
        Vaporshell
    #>

    [cmdletbinding()]
    Param(
        [parameter(Mandatory, Position = 0)]
        [ValidateScript({Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory, Position = 1)]
        [String]
        $Key
    )
    try {
        $item = Get-Item $Path -ErrorAction Stop
        $contents = Get-Content $item.FullName -Raw -ErrorAction Stop
        $Global:VSConfig = switch ($item.Extension) {
            '.json' {
                $obj = ConvertFrom-Json -InputObject $contents -ErrorAction Stop
                [PSCustomObject]$obj.$key
            }
            '.psd1' {
                $sb = [scriptblock]::Create($contents)
                $hash = $sb.Invoke()
                [PSCustomObject]($hash.$Key)
            }
            Default {
                throw "$Path must be a JSON or PSD1 file!"
            }
        }
        Write-Verbose "Imported config:`n`n$(($Global:VSConfig | Out-String).Trim())"
        return $Global:VSConfig
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($_)
    }
}

Export-ModuleMember -Function 'Import-VSTemplateConfig'

function Initialize-Vaporshell {
    <#
    .SYNOPSIS
        The starting point for your template buildout. This should always be the first thing called in your template script.

    .DESCRIPTION
        The starting point for your template buildout. This should always be the first thing called in your template script.

        This creates a PSObject, custom typed as 'Vaporshell.Template'. It builds out the containers for Metadata, Parameters, Mappings, Conditions, Resources and Outputs.

    .PARAMETER FormatVersion
        The AWSTemplateFormatVersion section (optional) identifies the capabilities of the template. The latest template format version is 2010-09-09 and is currently the only valid value.

    .PARAMETER Description
        The template description. Total byte count for the description has to be greater than 0 but less than 1024.

    .EXAMPLE
        $Template = Initialize-Vaporshell -Description "This is a sample template that builds an S3 bucket"

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSTemplate])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory = $false,Position = 0)]
        [ValidateScript( {[System.Text.Encoding]::UTF8.GetByteCount($_) -lt 1024 -and [System.Text.Encoding]::UTF8.GetByteCount($_) -gt 0})]
        [string]
        $Description,
        [parameter(Mandatory = $false)]
        [ValidateSet("2010-09-09")]
        [Alias('AWSTemplateFormatVersion')]
        [string]
        $FormatVersion
    )
    $obj = [VSTemplate]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Initialize-Vaporshell'

function Install-VaporShellModule {
    [CmdletBinding(DefaultParameterSetName = 'Name', SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param(
        [parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,Position = 0,ParameterSetName = 'Name')]
        [VaporShellModule[]]
        $Name,
        [parameter(ParameterSetName = 'All')]
        [switch]
        $All,
        [parameter()]
        [string]
        $MinimumVersion,
        [parameter()]
        [string]
        $MaximumVersion,
        [parameter()]
        [string]
        $RequiredVersion,
        [parameter()]
        [string[]]
        $Repository,
        [parameter()]
        [PSCredential]
        $Credential,
        [parameter()]
        [ValidateSet('AllUsers','CurrentUser')]
        [string]
        $Scope,
        [parameter()]
        [string]
        $Proxy,
        [parameter()]
        [PSCredential]
        $ProxyCredential,
        [parameter()]
        [switch]
        $AllowClobber,
        [parameter()]
        [switch]
        $SkipPublisherCheck,
        [parameter()]
        [switch]
        $Force,
        [parameter()]
        [switch]
        $AllowPrerelease,
        [parameter()]
        [switch]
        $PassThru
    )
    Begin {
        $installed = Get-Module VaporShell* -ListAvailable
    }
    Process {
        $params = $PSBoundParameters
        if ($PSCmdlet.ParameterSetName -eq 'All') {
            $params.Remove('All') | Out-Null
            $params['Name'] = [enum]::GetValues([VaporShellModule])
        }
        $names = $params['Name'] | ForEach-Object {
            if ($_ -ne 'VaporShell') {
                "VaporShell.$_"
            }
            else {
                $_
            }
        }
        if ($alreadyInstalled = $names | Where-Object {$_ -in $installed.Name}) {
            Write-Verbose "The following modules are already installed and will be updated instead:`n- $($alreadyInstalled -join "`n- ")"
            $updateParams = $params
            $updateParams['Name'] = $alreadyInstalled

            if (($updateParams.Keys -join '-') -match 'Version|Prerelease') {
                $updateParams.Remove('Name') | Out-Null
                foreach ($mod in $alreadyInstalled) {
                    Update-Module -Name $mod @updateParams
                }
            }
            else {
                Update-Module @updateParams
            }

            $params['Name'] = $names | Where-Object {$_ -notin $installed.Name}
        }
        Write-Verbose "Installing modules:`n- $($params['Name'] -join "`n- ")"
        if (($params.Keys -join '-') -match 'Version|Prerelease') {
            $list = $params['Name']
            $params.Remove('Name') | Out-Null
            foreach ($mod in $list) {
                Install-Module -Name $mod @params
            }
        }
        else {
            Install-Module @params
        }
    }
}

Export-ModuleMember -Function 'Install-VaporShellModule'

function Add-FnBase64 {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Base64" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Base64 returns the Base64 representation of the input string. This function is typically used to pass encoded data to Amazon EC2 instances by way of the UserData property.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-base64.html

    .PARAMETER ValueToEncode
        The string value you want to convert to Base64.

    .EXAMPLE
        Add-FnBase64 -ValueToEncode "AWS CloudFormation"

        When the template is exported, this will convert to: {"Fn::Base64":"AWS CloudFormation"}

    .EXAMPLE
        Add-FnBase64 -ValueToEncode (Add-FnRef "$_AWSRegion"")

        When the template is exported, this will convert to: {"Fn::Base64":{"Ref":"AWS::Region"}}

    .NOTES
        You can use any function that returns a string inside the Fn::Base64 function.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType('Vaporshell.Function.Base64')]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object]
        $ValueToEncode
    )
    $obj = [FnBase64]::new($ValueToEncode)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnBase64'

function Add-FnCidr {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Cidr" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Cidr returns an array of CIDR address blocks. The number of CIDR blocks returned is dependent on the count parameter.

    .LINK
        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html

    .PARAMETER IpBlock
        The user-specified CIDR address block to be split into smaller CIDR blocks.

    .PARAMETER Count
        The number of CIDRs to generate. Valid range is between 1 and 256.

    .PARAMETER CidrBits
        The number of subnet bits for the CIDR. For example, specifying a value "8" for this parameter will create a CIDR with a mask of "/24".

    .EXAMPLE
        Add-FnCidr -IpBlock "192.168.0.0/24" -Count 6 -CidrBits 5

        When the template is exported, this will convert to: { "Fn::Cidr" : [ "192.168.0.0/24", "6", "5"] }

    .NOTES
        You can use the following functions in a Fn::Cidr function:
            Fn::Select
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnCidr])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object]
        $IpBlock,
        [parameter(Mandatory,Position = 1)]
        [object]
        $Count,
        [parameter(Mandatory,Position = 2)]
        [object]
        $CidrBits
    )
    $obj = [FnCidr]::new($IpBlock,$Count,$CidrBits)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnCidr'

function Add-FnFindInMap {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::FindInMap" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::FindInMap returns the value corresponding to keys in a two-level map that is declared in the Mappings section.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html

    .PARAMETER MapName
        The logical name of a mapping declared in the Mappings section that contains the keys and values. The value can be another function.

    .PARAMETER TopLevelKey
        The top-level key name. Its value is a list of key-value pairs. The value can be another function.

    .PARAMETER SecondLevelKey
        The second-level key name, which is set to one of the keys from the list assigned to TopLevelKey. The value can be another function.

    .EXAMPLE
        Add-FnFindInMap -MapName "RegionMap" -TopLevelKey (Add-FnRef -Ref "$_AWSRegion") -SecondLevelKey "32"

        When the template is exported, this will convert to: {"Fn::FindInMap":["RegionMap",{"Ref":"AWS::Region"},"32"]}

    .NOTES
        You can use the following functions in a Fn::FindInMap function:
            Fn::FindInMap
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType('Vaporshell.Function.FindInMap')]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $MapName,
        [parameter(Mandatory,Position = 1)]
        [object]
        $TopLevelKey,
        [parameter(Mandatory,Position = 2)]
        [object]
        $SecondLevelKey
    )
    $obj = [FnFindInMap]::new($MapName,$TopLevelKey,$SecondLevelKey)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnFindInMap'

function Add-FnGetAtt {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::GetAtt" to a resource property

    .DESCRIPTION
        The Fn::GetAtt intrinsic function returns the value of an attribute from a resource in the template.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html

    .PARAMETER LogicalNameOfResource
        The logical name of the resource that contains the attribute that you want.

    .PARAMETER AttributeName
        The name of the resource-specific attribute whose value you want. See the resource's reference page for details about the attributes available for that resource type.

    .EXAMPLE
        Add-FnGetAtt -LogicalNameOfResource "MyLB" -AttributeName "DNSName"

        When the template is exported, this will convert to: {"Fn::GetAtt":["MyLB","DNSName"]}

    .NOTES
        For the Fn::GetAtt logical resource name, you cannot use functions. You must specify a string that is a resource's logical ID.

        For the Fn::GetAtt attribute name, you can use the Ref function.



        You can retrieve the following attributes using Fn::GetAtt :

        Resource TypeName Attribute Description
        ----------------- --------- -----------
        AWS::ApiGateway::RestApi RootResourceId The root resource ID for a?RestApi?resource.

                                                                                                            Example: a0bc123d4e
        AWS::CloudFormation::WaitCondition Data For more information about wait condition signals, see Wait Condition Signal JSON Format.

                                                                                                            Example of a wait condition with two signals:
                                                                                                            {"Signal1":"Step 1 complete.","Signal2":"Step 2 complete."}

        AWS::CloudFormation::Stack Outputs.NestedStackOutputName The output value from the nested stack that you specified, where?NestedStackOutputName?is the name of the output value.
        AWS::CloudFront::Distribution DomainName Example:?d2fadu0nynjpfn.cloudfront.net
        AWS::CodeBuild::Project Arn Example:?arn:aws:codebuild:us-west-2:123456789012:project/myProjectName
        AWS::CodeCommit::Repository Arn Example:?arn:aws:codecommit:us-east-1:123456789012:MyDemoRepo
        AWS::CodeCommit::Repository CloneUrlHttp Example:?https://codecommit.us-east-1.amazonaws.com/v1/repos/MyDemoRepo
        AWS::CodeCommit::Repository CloneUrlSsh Example:?ssh://git-codecommit.us-east-1.amazonaws.com/v1/repos//v1/repos/MyDemoRepo
        AWS::CodeCommit::Repository Name Example:?MyDemoRepo
        AWS::Config::ConfigRule Arn Example:?arn:aws:config:us-east-1:123456789012:config-rule/config-rule-a1bzhi
        AWS::Config::ConfigRule ConfigRuleId Example:?config-rule-a1bzhi
        AWS::Config::ConfigRule Compliance.Type Example:?COMPLIANT
        AWS::DirectoryService::MicrosoftAD and AWS::DirectoryService::SimpleAD Alias The alias for a directory.

                                                                                                            Examples:?d-12373a053a?or?alias4-mydirectory-12345abcgmzsk?(if you have the?CreateAliasproperty set to true)
        AWS::DirectoryService::MicrosoftAD and AWS::DirectoryService::SimpleAD DnsIpAddresses The IP addresses of the DNS servers for the directory.

                                                                                                            Example:?[ "192.0.2.1", "192.0.2.2" ]
        AWS::DynamoDB::Table StreamArn The Amazon Resource Name (ARN) of the DynamoDB stream. To use this attribute, you must specify the DynamoDB table?StreamSpecification?property.

                                                                                                            Example:?arn:aws:dynamodb:us-east-1:123456789012:table/testddbstack-myDynamoDBTable-012A1SL7SMP5Q/stream/2015-11-30T20:10:00.000
        AWS::EC2::EIP AllocationId The ID that AWS assigns to represent the allocation of the address for use with Amazon VPC. It is returned only for VPC Elastic IP addresses.

                                                                                                            Example:?eipalloc-5723d13e
        AWS::EC2::Instance AvailabilityZone The Availability Zone where the instance that you specified is launched.

                                                                                                            Example:?us-east-1b
        AWS::EC2::Instance PrivateDnsName The private DNS name of the instance that you specified.

                                                                                                            Example:?ip-10-24-34-0.ec2.internal
        AWS::EC2::Instance PublicDnsName The public DNS name of the instance that you specified.

                                                                                                            Example:?ec2-107-20-50-45.compute-1.amazonaws.com
        AWS::EC2::Instance PrivateIp The private IP address of the instance that you specified.

                                                                                                            Example:?10.24.34.0
        AWS::EC2::Instance PublicIp The public IP address of the instance that you specified.

                                                                                                            Example:?192.0.2.0
        AWS::EC2::NetworkInterface PrimaryPrivateIpAddress The primary private IP address of the network interface that you specified.

                                                                                                            Example:?10.0.0.192
        AWS::EC2::NetworkInterface SecondaryPrivateIpAddresses The secondary private IP addresses of the network interface that you specified.

                                                                                                            Example:?["10.0.0.161", "10.0.0.162", "10.0.0.163"]
        AWS::EC2::SecurityGroup GroupId The group ID of the specified security group.

                                                                                                            Example:?sg-94b3a1f6
        AWS::EC2::Subnet AvailabilityZone The Availability Zone of the subnet.

                                                                                                            Example:?us-east-1a
        AWS::EC2::Subnet Ipv6CidrBlocks A list of IPv6 CIDR blocks that are associated with the subnet.

                                                                                                            Example:?[ 2001:db8:1234:1a00::/64 ]
        AWS::EC2::SubnetNetworkAclAssociation AssociationId The?NetworkAcl associationId?that is attached to a subnet.
        AWS::EC2::VPC CidrBlock The set of IP addresses for the VPC.

                                                                                                            Example:?10.0.0.0/16
        AWS::EC2::VPC DefaultNetworkAcl The default network ACL ID that is associated with the VPC, which AWS creates when you create a VPC.

                                                                                                            Example:?acl-814dafe3
        AWS::EC2::VPC DefaultSecurityGroup The default security group ID that is associated with the VPC, which AWS creates when you create a VPC.

                                                                                                            Example:?sg-b178e0d3
        AWS::EC2::VPC Ipv6CidrBlocks A list of IPv6 CIDR blocks that are associated with the VPC.

                                                                                                            Example:?[ 2001:db8:1234:1a00::/56 ]
        AWS::ECS::Service Name The name of an Amazon EC2 Container Service service.

                                                                                                            Example:?sample-webapp
        AWS::ElastiCache::CacheCluster ConfigurationEndpoint.Address The DNS address of the configuration endpoint for the Memcached cache cluster.

                                                                                                            Example:?test.abc12a.cfg.use1.cache.amazonaws.com:11111
        AWS::ElastiCache::CacheCluster ConfigurationEndpoint.Port The port number of the configuration endpoint for the Memcached cache cluster.
        AWS::ElastiCache::CacheCluster RedisEndpoint.Address The DNS address of the configuration endpoint for the Redis cache cluster.

                                                                                                            Example:?test.abc12a.cfg.use1.cache.amazonaws.com:11111
        AWS::ElastiCache::CacheCluster RedisEndpoint.Port The port number of the configuration endpoint for the Redis cache cluster.
        AWS::ElastiCache::ReplicationGroup ConfigurationEndPoint.Address The DNS hostname of the cache node.
        AWS::ElastiCache::ReplicationGroup ConfigurationEndPoint.Port The port number that the cache engine is listening on.
        AWS::ElastiCache::ReplicationGroup PrimaryEndPoint.Address The DNS address of the primary read-write cache node.
        AWS::ElastiCache::ReplicationGroup PrimaryEndPoint.Port The port number that the primary read-write cache engine is listening on.
        AWS::ElastiCache::ReplicationGroup ReadEndPoint.Addresses A string with a list of endpoints for the read-only replicas. The order of the addresses map to the order of the ports from the?ReadEndPoint.Ports...

                                                                                                            Example:?"[abc12xmy3d1w3hv6-001.rep12a.0001.use1.cache.amazonaws.com, abc12xmy3d1w3hv6-002.rep12a.0001.use1.cache.amazonaws.com, abc12xmy3d1w3hv6-...
        AWS::ElastiCache::ReplicationGroup ReadEndPoint.Ports A string with a list of ports for the read-only replicas. The order of the ports maps to the order of the addresses from the?ReadEndPoint.Addresse...

                                                                                                            Example:?"[6379, 6379, 6379]"
        AWS::ElastiCache::ReplicationGroup ReadEndPoint.Addresses.List A list of endpoints for the read-only replicas.

                                                                                                            Example:?["abc12xmy3d1w3hv6-001.rep12a.0001.use1.cache.amazonaws.com", "abc12xmy3d1w3hv6-002.rep12a.0001.use1.cache.amazonaws.com", "abc12xmy3d1w3...
        AWS::ElastiCache::ReplicationGroup ReadEndPoint.Ports.List A list of ports for the read-only replicas.

                                                                                                            Example:?["6379","6379","6379"]
        AWS::ElasticBeanstalk::Environment EndpointURL The URL to the load balancer for this environment.

                                                                                                            Example:?awseb-myst-myen-132MQC4KRLAMD-1371280482.us-east-1.elb.amazonaws.com
        AWS::ElasticLoadBalancing::LoadBalancer CanonicalHostedZoneName The name of the Amazon Route?53-hosted zone that is associated with the load balancer.

                                                                                                            Example:?mystack-myelb-15HMABG9ZCN57-1013119603.us-east-1.elb.amazonaws.com
        AWS::ElasticLoadBalancing::LoadBalancer CanonicalHostedZoneNameID The ID of the Amazon Route?53 hosted zone name that is associated with the l oad balancer.

                                                                                                            Example:?Z3DZXE0Q79N41H
        AWS::ElasticLoadBalancing::LoadBalancer DNSName The DNS name for the load balancer.

                                                                                                            Example:?mystack-myelb-15HMABG9ZCN57-1013119603.us-east-1.elb.amazonaws.com
        AWS::ElasticLoadBalancing::LoadBalancer SourceSecurityGroup.GroupName The security group that you can use as part of your inbound rules for your load balancer's back-end Amazon EC2 application instances.

                                                                                                            Example:?amazon-elb
        AWS::ElasticLoadBalancing::LoadBalancer SourceSecurityGroup.OwnerAlias The owner of the source security group.

                                                                                                            Example:?amazon-elb-sg
        AWS::ElasticLoadBalancingV2::LoadBalancer DNSName The DNS name for the application load balancer.

                                                                                                            Example:?my-load-balancer-424835706.us-west-2.elb.amazonaws.com
        AWS::ElasticLoadBalancingV2::LoadBalancer CanonicalHostedZoneID The ID of the Amazon Route?53-hosted zone name that is associated with the load balancer.

                                                                                                            Example:?Z2P70J7EXAMPLE
        AWS::ElasticLoadBalancingV2::LoadBalancer LoadBalancerFullName The full name of the application load balancer.

                                                                                                            Example:?app/my-load-balancer/50dc6c495c0c9188
        AWS::ElasticLoadBalancingV2::LoadBalancer LoadBalancerName The name of the application load balancer.

                                                                                                            Example:?my-load-balancer
        AWS::ElasticLoadBalancingV2::LoadBalancer SecurityGroups The IDs of the security groups for the application load balancer.

                                                                                                            Example:?sg-123456a
        AWS::ElasticLoadBalancingV2::TargetGroup LoadBalancerArns The Amazon Resource Names (ARNs) of the load balancers that route traffic to this target group.

                                                                                                            Example:?[ "arn:aws:elasticloadbalancing:us-west-2:123456789012:loadbalancer/app/my-load-balancer/50dc6c495c0c9188" ]
        AWS::ElasticLoadBalancingV2::TargetGroup TargetGroupFullName The full name of the target group.
                                                                                                            Example:?targetgroup/my-target-group/cbf133c568e0d028
        AWS::Elasticsearch::Domain DomainArn The Amazon Resource Name (ARN) of the domain.

                                                                                                            Example:?arn:aws:es:us-west-2:123456789012:domain/mystack-elasti-1ab2cdefghij
        AWS::Elasticsearch::Domain DomainEndpoint The domain-specific endpoint that is used to submit index, search, and data upload requests to an Amazon Elasticsearch Service domain.

                                                                                                            Example:?search-mystack-elasti-1ab2cdefghij-ab1c2deckoyb3hofw7wpqa3cm.us-west-2.es.amazonaws.com
        AWS::EMR::Cluster MasterPublicDNS The public DNS name of the master node (instance).

                                                                                                            Example:?ec2-12-123-123-123.us-west-2.compute.amazonaws.com
        AWS::Events::Rule Arn The Amazon Resource Name (ARN) of the event rule.

                                                                                                            Example:?arn:aws:events:us-east-1:123456789012:rule/example
        AWS::IAM::AccessKey SecretAccessKey The secret access key for the specified?Access Key.

                                                                                                            Example:?wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY
        AWS::IAM::Group Arn Example:?arn:aws:iam::123456789012:group/mystack-mygroup-1DZETITOWEKVO
        AWS::IAM::InstanceProfile Arn Returns the Amazon Resource Name (ARN) for the instance profile.

                                                                                                            Example:?arn:aws:iam::1234567890:instance-profile/MyProfile-ASDNSDLKJ
        AWS::IAM::Role Arn Example:?arn:aws:iam::1234567890:role/MyRole-AJJHDSKSDF
        AWS::IAM::User Arn Example:?arn:aws:iam::123456789012:user/mystack-myuser-1CCXAFG2H2U4D
        AWS::IoT::Certificate Arn Example:?arn:aws:iot:ap-southeast-2:123456789012:cert/a1234567b89c012d3e4fg567hij8k9l01mno1p23q45678901rs234567890t1u2
        AWS::Kinesis::Stream Arn The ARN of the Amazon Kinesis stream.

                                                                                                            Example:?arn:aws:kinesis:us-east-1:123456789012:stream/mystream.
        AWS::KMS::Key Arn The ARN of the AWS KMS key.

                                                                                                            Example:?arn:aws:kms:us-west-2:123456789012:key/12a34567-8c90-1defg-af84-0bf06c1747f3.
        AWS::Lambda::Function Arn Example:?arn:aws:lambda:us-west-2:123456789012:MyStack-AMILookUp-NT5EUXTNTXXD
        AWS::Lambda::Version Version The version of a Lambda function.

                                                                                                            Example:?1
        AWS::Logs::LogGroup Arn The ARN of the Amazon CloudWatch Logs log group.

                                                                                                            Example:?arn:aws:logs:us-east-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*
        AWS::OpsWorks::Instance AvailabilityZone The Availability Zone of an AWS OpsWorks instance.

                                                                                                            Example:?us-east-2a.
        AWS::OpsWorks::Instance PrivateDnsName The private DNS name of an AWS OpsWorks instance.
        AWS::OpsWorks::Instance PrivateIp The private IP address of an AWS OpsWorks instance.
        AWS::OpsWorks::Instance PublicDnsName The public DNS name of an AWS OpsWorks instance.
        AWS::OpsWorks::Instance PublicIp The public IP address of an AWS OpsWorks instance.

                                                                                                            Note
                                                                                                            To use this attribute, the AWS OpsWorks instance must be in an AWS OpsWorks layer that auto-assigns public IP addresses.

                                                                                                            Example:?192.0.2.0
        AWS::OpsWorks::UserProfile SshUserName The SSH user name of an AWS OpsWorks instance.
        AWS::Redshift::Cluster Endpoint.Address The connection endpoint for the cluster.

                                                                                                            Example:?examplecluster.cg034hpkmmjt.us-east-1.redshift.amazonaws.com
        AWS::Redshift::Cluster Endpoint.Port The connection port for the cluster.

                                                                                                            Example:?5439
        AWS::RDS::DBCluster Endpoint.Address The connection endpoint for the DB cluster.

                                                                                                            Example:?mystack-mydbcluster-1apw1j4phylrk.cg034hpkmmjt.us-east-1.rds.amazonaws.com
        AWS::RDS::DBCluster Endpoint.Port The port number on which the DB cluster accepts connections.

                                                                                                            Example:?3306
        AWS::RDS::DBInstance Endpoint.Address The connection endpoint for the database.

                                                                                                            Example:?mystack-mydb-1apw1j4phylrk.cg034hpkmmjt.us-east-1.rds.amazonaws.com
        AWS::RDS::DBInstance Endpoint.Port The port number on which the database accepts connections.

                                                                                                            Example:?3306
        AWS::Route53::HostedZone NameServers Returns the set of name servers for the specific hosted zone.

                                                                                                            Example:?ns1.example.com
        AWS::S3::Bucket DomainName The DNS name of the specified bucket.

                                                                                                            Example:?mystack-mybucket-kdwwxmddtr2g.s3.amazonaws.com
        AWS::S3::Bucket WebsiteURL The Amazon S3 website endpoint for the specified bucket.

                                                                                                            Example:?http://mystack-mybucket-kdwwxmddtr2g.s3-website-us-east-1.amazonaws.com/
        AWS::Serverless::Function No attribute. The ARN of an?AWS::Serverless::Function?resource.
        AWS::SNS::Topic TopicName The name of an Amazon SNS topic.

                                                                                                            Example:?my-sns-topic
        AWS::StepFunctions::Activity Name The name of the AWS Step Functions activity.
        AWS::StepFunctions::StateMachine Name The name of the Step Functions state machine.
        AWS::SQS::Queue Arn The ARN for the specified queue.

                                                                                                            Example:?arn:aws:sqs:us-east-1:123456789012:mystack-myqueue-15PG5C2FC1CW8
        AWS::SQS::Queue QueueName The name of an Amazon SQS queue.

                                                                                                            Example:?mystack-myqueue-1VF9BKQH5BJVI

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnGetAtt])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $LogicalNameOfResource,
        [parameter(Mandatory,Position = 1)]
        [string]
        $AttributeName
    )
    $obj = [FnGetAtt]::new($LogicalNameOfResource,$AttributeName)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnGetAtt'

function Add-FnGetAZs {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::GetAZs" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::GetAZs returns an array that lists Availability Zones for a specified region. Because customers have access to different Availability Zones, the intrinsic function Fn::GetAZs enables template authors to write templates that adapt to the calling user's access. That way you don't have to hard-code a full list of Availability Zones for a specified region.

            ** Important **
                For the EC2-Classic platform, the Fn::GetAZs function returns all Availability Zones for a region. For the EC2-VPC platform, the Fn::GetAZs function returns only Availability Zones that have a default subnet unless none of the Availability Zones has a default subnet; in that case, all Availability Zones are returned.

                Similarly to the response from the describe-availability-zones AWS CLI command, the order of the results from the Fn::GetAZs function is not guaranteed and can change when new Availability Zones are added.

        IAM permissions

            The permissions that you need in order to use the Fn::GetAZs function depend on the platform in which you're launching Amazon EC2 instances. For both platforms, you need permissions to the Amazon EC2 DescribeAvailabilityZones and DescribeAccountAttributes actions. For EC2-VPC, you also need permissions to the Amazon EC2 DescribeSubnets action.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getavailabilityzones.html

    .PARAMETER Region
        The name of the region for which you want to get the Availability Zones.

        You can use the AWS::Region pseudo parameter to specify the region in which the stack is created. Specifying an empty string is equivalent to specifying AWS::Region.

        This is not required. If you would like to default this to the stack deployment region, simply exclude this parameter and call the function by itself.

    .EXAMPLE
        Add-FnImportValue -ValueToImport (Add-FnSub -String "`${NetworkStackNameParameter}-SubnetID")

        When the template is exported, this will convert to: {"Fn::ImportValue":{"Fn::Sub":"${NetworkStackNameParameter}-SubnetID"}}

    .NOTES
        You can use the Ref function in the Fn::GetAZs function.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnGetAZs])]
    [cmdletbinding()]
    Param(
        [parameter(Position = 0)]
        [object]
        $Region = [FnRef]::Region
    )
    $obj = [FnGetAZs]::new($Region)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnGetAZs'

function Add-FnImportValue {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::ImportValue" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::ImportValue returns the value of an output exported by another stack. You typically use this function to create cross-stack references.

        Note:
            The following restrictions apply to cross-stack references:
                * For each AWS account, Export names must be unique within a region.
                * You can't create cross-stack references across regions. You can use the intrinsic function Fn::ImportValue to import only values that have been exported within the same region.
                * For outputs, the value of the Name property of an Export can't use Ref or GetAtt functions that depend on a resource.
                * Similarly, the ImportValue function can't include Ref or GetAtt functions that depend on a resource.
                * You can't delete a stack if another stack references one of its outputs.
                * You can't modify or remove an output value that is referenced by another stack.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html

    .PARAMETER ValueToImport
        The stack output value that you want to import.

    .EXAMPLE
        Add-FnImportValue -ValueToImport (Add-FnSub -String "`${NetworkStackNameParameter}-SubnetID")

        When the template is exported, this will convert to: {"Fn::ImportValue":{"Fn::Sub":"${NetworkStackNameParameter}-SubnetID"}}

    .NOTES
        You can use the following functions in the Fn::ImportValue function. The value of these functions can't depend on a resource.
            Fn::Base64
            Fn::FindInMap
            Fn::If
            Fn::Join
            Fn::Select
            Fn::Split
            Fn::Sub
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnImportValue])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [object]
        $ValueToImport
    )
    $obj = [FnImportValue]::new($ValueToImport)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnImportValue'

function Add-FnJoin {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Join" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Join appends a set of values into a single value, separated by the specified delimiter. If a delimiter is the empty string, the set of values are concatenated with no delimiter.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html

    .PARAMETER Delimiter
        The value you want to occur between fragments. The delimiter will occur between fragments only. It will not terminate the final value.

        This is not required. If you want to join without an added delimiter, simply exclude this parameter.

    .PARAMETER ListOfValues
        The list of values you want combined.

    .EXAMPLE
        Add-FnSelect -Index 2 -ListOfObjects (Add-FnSplit -Delimiter "," -SourceString (Add-FnImportValue -ValueToImport "AccountSubnetIds"))

        When the template is exported, this will convert to: {"Fn::Select":["2",{"Fn::Split":[",",{"Fn::ImportValue":"AccountSubnetIds"}]}]}

    .NOTES
        For the Fn::Join delimiter, you cannot use any functions. You must specify a string value.

        For the Fn::Join list of values, you can use the following functions:
            Fn::Base64
            Fn::FindInMap
            Fn::GetAtt
            Fn::GetAZs
            Fn::If
            Fn::ImportValue
            Fn::Join
            Fn::Split
            Fn::Select
            Fn::Sub
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnJoin])]
    [cmdletbinding()]
    Param(
        [parameter(Position = 0)]
        [System.String]
        $Delimiter = $null,
        [parameter(Mandatory,Position = 1)]
        [object[]]
        $ListOfValues
    )
    $obj = [FnJoin]::new($Delimiter,@($ListOfValues))
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnJoin'

function Add-FnRef {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Ref" to a resource property

    .DESCRIPTION
        The intrinsic function Ref returns the value of the specified parameter or resource.

            * When you specify a parameter's logical name, it returns the value of the parameter.
            * When you specify a resource's logical name, it returns a value that you can typically use to refer to that resource, such as a physical ID.

        When you are declaring a resource in a template and you need to specify another template resource by name, you can use the Ref to refer to that other resource. In general, Ref returns the name of the resource. For example, a reference to an AWS::AutoScaling::AutoScalingGroup returns the name of that Auto Scaling group resource.

        For some resources, an identifier is returned that has another significant meaning in the context of the resource. An AWS::EC2::EIP resource, for instance, returns the IP address, and an AWS::EC2::Instance returns the instance ID.

            Tip
                You can also use Ref to add values to Output messages.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html

    .PARAMETER Ref
        The logical name of the resource or parameter you want to reference.

    .EXAMPLE
        # This uses the module's included variable that maps to the AWS Pseudo Parameter, "AWS::Region"

        Add-FnRef -Ref "$_AWSRegion"

        When the template is exported, this will convert to: {"Ref":"AWS::Region"}

    .NOTES
        You cannot use any functions in the Ref function. You must specify a string that is a resource logical ID.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnRef])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $Ref
    )
    $obj = [FnRef]::new($Ref)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnRef'

function Add-FnSelect {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Select" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Select returns a single object from a list of objects by index.

        You can use Fn::Select to select an object from a CommaDelimitedList parameter. You might use a CommaDelimitedList parameter to combine the values of related parameters, which reduces the total number of parameters in your template.

            ** Important **
                Fn::Select does not check for null values or if the index is out of bounds of the array. Both conditions will result in a stack error, so you should be certain that the index you choose is valid, and that the list contains non-null values.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html

    .PARAMETER Index
        The index of the object to retrieve. This must be a value from zero to N-1, where N represents the number of elements in the array.

    .PARAMETER ListOfObjects
        The list of objects to select from. This list must not be null, nor can it have null entries.

    .EXAMPLE
        Add-FnSelect -Index 2 -ListOfObjects (Add-FnSplit -Delimiter "," -SourceString (Add-FnImportValue -ValueToImport "AccountSubnetIds"))

        When the template is exported, this will convert to: {"Fn::Select":["2",{"Fn::Split":[",",{"Fn::ImportValue":"AccountSubnetIds"}]}]}

    .NOTES
        For the Fn::Select index value, you can use the Ref and Fn::FindInMap functions.

        For the Fn::Select list of objects, you can use the following functions:
            Fn::FindInMap
            Fn::GetAtt
            Fn::GetAZs
            Fn::If
            Fn::Split
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnSelect])]
    [cmdletbinding()]
    Param(
        [parameter(Position = 0)]
        [object]
        $Index = 0,
        [parameter(Mandatory,Position = 1)]
        [object[]]
        $ListOfObjects
    )
    $obj = [FnSelect]::new($Index,$ListOfObjects)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnSelect'

function Add-FnSplit {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Split" to a resource property

    .DESCRIPTION
        To split a string into a list of string values so that you can select an element from the resulting string list, use the Fn::Split intrinsic function. Specify the location of splits with a delimiter, such as , (a comma). After you split a string, use the Fn::Select function to pick a specific element.

        For example, if a comma-delimited string of subnet IDs is imported to your stack template, you can split the string at each comma. From the list of subnet IDs, use the Fn::Select intrinsic function to specify a subnet ID for a resource.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-split.html

    .PARAMETER Delimiter
        A string value that determines where the source string is divided.

    .PARAMETER SourceString
        The string value that you want to split. This can be a string or object from an Add-Fn* function output

    .EXAMPLE
        Add-FnSplit -Delimiter "," -SourceString (Add-FnImportValue -ValueToImport "AccountSubnetIds")

        When the template is exported, this will convert to: {"Fn::Split":[",",{"Fn::ImportValue":"AccountSubnetIds"}]}

    .NOTES
        For the Fn::Split delimiter, you cannot use any functions. You must specify a string value.

        For the Fn::Split list of values (SourceString), you can use the following functions:
            Fn::Base64
            Fn::FindInMap
            Fn::GetAtt
            Fn::GetAZs
            Fn::If
            Fn::Join
            Fn::Select
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnSplit])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $Delimiter,
        [parameter(Mandatory,Position = 1)]
        [object]
        $SourceString
    )
    $obj = [FnSplit]::new($Delimiter,$SourceString)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnSplit'

function Add-FnSub {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Sub" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Sub substitutes variables in an input string with values that you specify. In your templates, you can use this function to construct commands or outputs that include values that aren't available until you create or update a stack.

        ** Important **
            As Fn::Sub uses ${Var} syntax, it's important to remember to escape the $ at the head of the subbed variable when calling the function, otherwise Powershell will attempt to convert the variable into a value when adding it to the object and break the CloudFormation function's intended use.

            Please see the example below for reference.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-sub.html

    .PARAMETER String
        Input string with the Variable names surrounded in braces, i.e. "/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region}"

        You MUST escape the dollar sign before each variable brace when using this module, otherwise Powershell will attempt to convert it to a variable and not take it as a literal string.

    .PARAMETER Mapping
        A hashtable containing mappings, with the key being the variable name and the value being what you would like to substitute for the variable. The value can be another function.

    .EXAMPLE
        Add-FnSub -String "www.`${Domain}" -Mapping @{Domain = (Add-FnRef -Ref "RootDomainName")}

        When the template is exported, this will convert to: {"Fn::Sub":["www.${Domain}",{"Domain":{"Ref":"RootDomainName"}}]}

    .NOTES
        You can use the following functions in the Fn::Sub function:
            Fn::Base64
            Fn::FindInMap
            Fn::GetAtt
            Fn::GetAZs
            Fn::If
            Fn::Join
            Fn::Select
            Ref

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType('Vaporshell.Function.Sub')]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [string]
        $String,
        [parameter(Position = 1)]
        [System.Collections.IDictionary]
        $Mapping
    )
    $obj = if ($Mapping) {
        [FnSub]::new($String,$Mapping)
    }
    else {
        [FnSub]::new($String)
    }
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnSub'

function Add-FnTransform {
    <#
    .SYNOPSIS
        Adds the intrinsic function "Fn::Transform" to a resource property

    .DESCRIPTION
        The intrinsic function Fn::Transform specifies a macro to perform custom processing on part of a stack template. Macros enable you to perform custom processing on templates, from simple actions like find-and-replace operations to extensive transformations of entire templates. For more information, see Using AWS CloudFormation Macros to Perform Custom Processing on Templates.

        You can also use Fn::Transform to call the AWS::Include Transform transform, which is a macro hosted by AWS CloudFormation.

    .LINK
        https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-transform.html

    .PARAMETER Name
        The name of the macro you want to perform the processing.

    .PARAMETER Parameters
        The list parameters, specified as a hashtable, to pass to the macro.

    .EXAMPLE
        Add-FnTransform -Name "AWS::Include" -Parameters @{Location = (Add-FnRef "InputValue")}

        This example calls the AWS::Include transform, specifying that the location to retrieve a template snippet from is passed in the InputValue parameter.

        When the template is exported, this will convert to:
            {
                "Fn::Transform" : {
                    "Name" : "AWS::Include",
                    "Parameters" : {
                        "Location" : { "Ref" : "InputValue" }
                    }
                }
            }

    .NOTES
        AWS CloudFormation passes any intrinsic function calls included in Fn::Transform to the specified macro as literal strings. For more information, see AWS CloudFormation Macro Function Interface.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([FnTransform])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [String]
        $Name,
        [parameter(Mandatory,Position = 1)]
        [System.Collections.IDictionary]
        $Parameters
    )
    $obj = [FnTransform]@{
        Name = $Name
        Parameters = $Parameters
    }
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-FnTransform'

function New-VaporCondition {
    <#
    .SYNOPSIS
        Adds a Condition object to the template

    .DESCRIPTION
        The optional Conditions section includes statements that define when a resource is created or when a property is defined. For example, you can compare whether a value is equal to another value. Based on the result of that condition, you can conditionally create resources. If you have multiple conditions, separate them with commas.

        You might use conditions when you want to reuse a template that can create resources in different contexts, such as a test environment versus a production environment. In your template, you can add an EnvironmentType input parameter, which accepts either prod or test as inputs. For the production environment, you might include Amazon EC2 instances with certain capabilities; however, for the test environment, you want to use reduced capabilities to save money. With conditions, you can define which resources are created and how they're configured for each environment type.

        Conditions are evaluated based on input parameter values that you specify when you create or update a stack. Within each condition, you can reference another condition, a parameter value, or a mapping. After you define all your conditions, you can associate them with resources and resource properties in the Resources and Outputs sections of a template.

        At stack creation or stack update, AWS CloudFormation evaluates all the conditions in your template before creating any resources. Any resources that are associated with a true condition are created. Any resources that are associated with a false condition are ignored.

        ** Important **
            During a stack update, you cannot update conditions by themselves. You can update conditions only when you include changes that add, modify, or delete resources.

        To conditionally create resources, you must include statements in at least three different sections of a template:

            Parameters section
                Define the input values that you want to evaluate in your conditions. Conditions will result in true or false based on values from these input parameter.

            Conditions section
                Define conditions by using the intrinsic condition functions. These conditions determine when AWS CloudFormation creates the associated resources.

            Resources and Outputs sections
                Associate conditions with the resources or outputs that you want to conditionally create. AWS CloudFormation creates entities that are associated with a true condition and ignores entities that are associated with a false condition. Use the Condition key and a condition's logical ID to associate it with a resource or output. To conditionally specify a property, use the Fn::If function. For more information, see Condition Functions.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html

    .PARAMETER LogicalId
        An identifier for the current condition. The logical ID must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.

    .PARAMETER Condition
        Logical ID of the condition that this resource needs to be true in order to be provisioned.

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Condition addition"
        $template.AddResource((
            New-VaporCondition -LogicalId "CreateProdResources" -Condition (Add-ConEquals -FirstValue (Add-FnRef -Ref "EnvType") -SecondValue "prod")
        ))

        When the template is exported, this will convert to:
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "Testing Condition addition",
                "Conditions": {
                    "CreateProdResources": {
                        "Fn::Equals": [
                            {
                                "Ref": "EnvType"
                            },
                            "prod"
                        ]
                    }
                }
            }

    .NOTES
        You can use the following intrinsic functions to define conditions:
            Fn::And
            Fn::Equals
            Fn::If
            Fn::Not
            Fn::Or

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSCondition])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        $LogicalId,
        [parameter(Mandatory,Position = 1)]
        [ConditionFunction]
        $Condition
    )
    $obj = [VSCondition]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporCondition'

function New-VaporMapping {
    <#
    .SYNOPSIS
        Adds a Mapping object to the template

    .DESCRIPTION
        The optional Mappings section matches a key to a corresponding set of named values. For example, if you want to set values based on a region, you can create a mapping that uses the region name as a key and contains the values you want to specify for each specific region. You use the Fn::FindInMap intrinsic function to retrieve values in a map.

        You cannot include parameters, pseudo parameters, or intrinsic functions in the Mappings section.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html

    .PARAMETER LogicalId
        An identifier for the current condition. The logical ID must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.

    .PARAMETER Map
        A 2 level collection of key/value pairs. If you would like your collection to remain ordered the same as called, use an ordered PSCustomObject, otherwise a hashtable is fine.

        You can use any of these 3 types for this parameter; "System.Collections.Hashtable","System.Management.Automation.PSCustomObject","Vaporshell.Mapping.Map"

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Mapping addition"
        $template.AddMapping((
            New-VaporMapping -LogicalId "RegionMap" -Map ([PSCustomObject][Ordered]@{
                "us-east-1" = [PSCustomObject][Ordered]@{
                    "32" = "ami-6411e20d"
                    "64" = "ami-7a11e213"
                }
                "us-west-1" = [PSCustomObject][Ordered]@{
                    "32" = "ami-c9c7978c"
                    "64" = "ami-cfc7978a"
                }
                "eu-west-1" = [PSCustomObject][Ordered]@{
                    "32" = "ami-37c2f643"
                    "64" = "ami-31c2f645"
                }
                "ap-southeast-1" = [PSCustomObject][Ordered]@{
                    "32" = "ami-66f28c34"
                    "64" = "ami-60f28c32"
                }
                "ap-northeast-1" = [PSCustomObject][Ordered]@{
                    "32" = "ami-9c03a89d"
                    "64" = "ami-a003a8a1"
                }
            })
        ))

        When the template is exported, this will convert to:
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "Testing Mapping addition",
                "Mappings": {
                    "RegionMap": {
                        "us-east-1": {
                            "32": "ami-6411e20d",
                            "64": "ami-7a11e213"
                        },
                        "us-west-1": {
                            "32": "ami-c9c7978c",
                            "64": "ami-cfc7978a"
                        },
                        "eu-west-1": {
                            "32": "ami-37c2f643",
                            "64": "ami-31c2f645"
                        },
                        "ap-southeast-1": {
                            "32": "ami-66f28c34",
                            "64": "ami-60f28c32"
                        },
                        "ap-northeast-1": {
                            "32": "ami-9c03a89d",
                            "64": "ami-a003a8a1"
                        }
                    }
                }
            }

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSMapping])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        $LogicalId,
        [parameter(Mandatory,Position = 1)]
        [System.Collections.IDictionary]
        $Map
    )
    $obj = [VSMapping]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporMapping'

function New-VaporMetadata {
    <#
    .SYNOPSIS
        Adds a Metadata object to the template

    .DESCRIPTION
        You can use the optional Metadata section to include arbitrary JSON or YAML objects that provide details about the template.

        ** Important **
            During a stack update, you cannot update the Metadata section by itself. You can update it only when you include changes that add, modify, or delete resources.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html

    .PARAMETER LogicalId
        The key of the metadata. The LogicalID must contain only alphanumeric characters or colons (a-z, A-Z, 0-9, :, ::) and unique within the template.

    .PARAMETER Metadata
        Key/Value pair.

        You can use any of these 3 types for this parameter; "System.Collections.Hashtable","System.Management.Automation.PSCustomObject","Vaporshell.Metadata.Data"

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Metadata addition"
        $template.AddMetadata(
            (New-VaporMetadata -LogicalId "Instances" -Metadata [PSCustomObject]@{"Description" = "Information about the instances"}),
            (New-VaporMetadata -LogicalId "Databases" -Metadata [PSCustomObject]@{"Description" = "Information about the databases"})
        )

        When the template is exported, this will convert to:
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "Testing Metadata addition",
                "Metadata": {
                    "Instances": {
                        "Description": "Information about the instances"
                    },
                    "Databases": {
                        "Description": "Information about the databases"
                    }
                }
            }

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSMetadata])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        [Alias('Key')]
        $LogicalId,
        [parameter(Mandatory,Position = 1)]
        [object]
        $Metadata
    )
    $obj = [VSMetadata]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporMetadata'

function New-VaporOutput {
    <#
    .SYNOPSIS
        Adds an Output object to the template

    .DESCRIPTION
        The optional Outputs section declares output values that you can import into other stacks (to create cross-stack references), return in response (to describe stack calls), or view on the AWS CloudFormation console. For example, you can output the S3 bucket name for a stack to make the bucket easier to find.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html

    .PARAMETER LogicalId
        An identifier for the current output. The logical ID must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.

    .PARAMETER Description
        A String type that describes the output value. The description can be a maximum of 4 K in length.

    .PARAMETER Value
        The value of the property returned by the aws cloudformation describe-stacks command. The value of an output can include literals, parameter references, pseudo-parameters, a mapping value, or intrinsic functions.

    .PARAMETER Export
        The name of the resource output to be exported for a cross-stack reference.

        Note
            The following restrictions apply to cross-stack references:
                * For each AWS account, Export names must be unique within a region.
                * You can't create cross-stack references across regions. You can use the intrinsic function Fn::ImportValue to import only values that have been exported within the same region.
                * For outputs, the value of the Name property of an Export can't use Ref or GetAtt functions that depend on a resource.
                * Similarly, the ImportValue function can't include Ref or GetAtt functions that depend on a resource.
                * You can't delete a stack if another stack references one of its outputs.
                * You can't modify or remove an output value that is referenced by another stack.
                * You can use intrinsic functions to customize the Name value of an export.

    .PARAMETER Condition
        Logical ID of the condition that this output needs to be true in order to be provisioned.

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Output"
        $template.AddOutput(
            (
                New-VaporOutput -LogicalId "BackupLoadBalancerDNSName" -Description "The DNSName of the backup load balancer" -Value (Add-FnGetAtt -LogicalNameOfResource "BackupLoadBalancer" -AttributeName "DNSName") -Condition "CreateProdResources"
            )
        )

        When the template is exported, this will convert to:
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "Testing Output",
                "Outputs": {
                    "BackupLoadBalancerDNSName": {
                    "Description": "The DNSName of the backup load balancer",
                    "Value": {
                        "Fn::GetAtt": [
                        "BackupLoadBalancer",
                        "DNSName"
                        ]
                    },
                    "Condition": "CreateProdResources"
                    }
                }
            }

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSOutput])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        $LogicalId,
        [parameter(Position = 1)]
        [string]
        $Description,
        [parameter(Mandatory,Position = 2)]
        $Value,
        [parameter(Position = 3)]
        [Export]
        $Export,
        [parameter(Position = 4)]
        [string]
        $Condition
    )
    $obj = [VSOutput]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporOutput'

function New-VaporParameter {
    <#
    .SYNOPSIS
        Adds a Parameter object to the template

    .DESCRIPTION
        You can use the optional Parameters section to pass values into your template when you create a stack. With parameters, you can create templates that are customized each time you create a stack. Each parameter must contain a value when you create a stack. You can specify a default value to make the parameter optional so that you don't need to pass in a value when creating a stack. AWS CloudFormation will use the default value. For more information about creating stacks, see Working with Stacks.

        The Parameters section consists of the key name Parameters. You can have a maximum of 60 parameters in an AWS CloudFormation template.

        For each parameter, you must declare a logical name, which must be alphanumeric and unique among all logical names within the template. After you declare the parameter's logical name, you can specify the parameter's properties. You must declare parameters as one of following types: String, Number, CommaDelimitedList, or an AWS-specific type. For String, Number, and AWS-specific parameter types, you can define constraints that AWS CloudFormation uses to validate the value of the parameter.

        AWS-specific parameter types are AWS values such as Amazon EC2 key pair names and VPC IDs. AWS CloudFormation validates these parameter values against existing values in users' AWS accounts. AWS-specific parameter types are helpful in catching invalid values at the start of creating or updating a stack.

        ** Important **
            For sensitive parameter values (such as passwords), set the NoEcho property to true. That way, whenever anyone describes your stack, the parameter value is shown as asterisks (*****).

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

    .PARAMETER LogicalId
        An identifier for the current condition. The logical ID must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.

    .PARAMETER Type
        The Properties type for the parameter (PropertiesType).

        Required: Yes

        You can specify the following values for the Type property:

        String
            A literal string.

            For example, users could specify "MyUserName".

        Number
            An integer or float. AWS CloudFormation validates the parameter value as a number; however, when you use the parameter elsewhere in your template (for example, by using the Ref intrinsic function), the parameter value becomes a string.

            For example, users could specify "8888".

        List<Number>
            An array of integers or floats that are separated by commas. AWS CloudFormation validates the parameter value as numbers; however, when you use the parameter elsewhere in your template (for example, by using the Ref intrinsic function), the parameter value becomes a list of strings.

            For example, users could specify "80,20", and a Ref will result in ["80","20"].

        CommaDelimitedList
            An array of literal strings that are separated by commas. The total number of strings should be one more than the total number of commas. Also, each member string is space trimmed.

            For example, users could specify "test,dev,prod", and a Ref will result in ["test","dev","prod"].

        AWS-Specific Parameter Types

            For AWS-specific parameter types, template users must specify existing AWS values that are in their account. AWS CloudFormation supports the following AWS-specific types:

                AWS::EC2::AvailabilityZone::Name
                An Availability Zone, such as us-west-2a.

                AWS::EC2::Image::Id
                An Amazon EC2 image ID, such as ami-ff527ecf. Note that the AWS CloudFormation console won't show a drop-down list of values for this parameter type.

                AWS::EC2::Instance::Id
                An Amazon EC2 instance ID, such as i-1e731a32.

                AWS::EC2::KeyPair::KeyName
                An Amazon EC2 key pair name.

                AWS::EC2::SecurityGroup::GroupName
                An EC2-Classic or default VPC security group name, such as my-sg-abc.

                AWS::EC2::SecurityGroup::Id
                A security group ID, such as sg-a123fd85.

                AWS::EC2::Subnet::Id
                A subnet ID, such as subnet-123a351e.

                AWS::EC2::Volume::Id
                An Amazon EBS volume ID, such as vol-3cdd3f56.

                AWS::EC2::VPC::Id
                A VPC ID, such as vpc-a123baa3.

                AWS::Route53::HostedZone::Id
                An Amazon Route 53 hosted zone ID, such as Z23YXV4OVPL04A.

                List<AWS::EC2::AvailabilityZone::Name>
                An array of Availability Zones for a region, such as us-west-2a, us-west-2b.

                List<AWS::EC2::Image::Id>
                An array of Amazon EC2 image IDs, such as ami-ff527ecf, ami-e7527ed7. Note that the AWS CloudFormation console won't show a drop-down list of values for this parameter type.

                List<AWS::EC2::Instance::Id>
                An array of Amazon EC2 instance IDs, such as i-1e731a32, i-1e731a34.

                List<AWS::EC2::SecurityGroup::GroupName>
                An array of EC2-Classic or default VPC security group names, such as my-sg-abc, my-sg-def.

                List<AWS::EC2::SecurityGroup::Id>
                An array of security group IDs, such as sg-a123fd85, sg-b456fd85.

                List<AWS::EC2::Subnet::Id>
                An array of subnet IDs, such as subnet-123a351e, subnet-456b351e.

                List<AWS::EC2::Volume::Id>
                An array of Amazon EBS volume IDs, such as vol-3cdd3f56, vol-4cdd3f56.

                List<AWS::EC2::VPC::Id>
                An array of VPC IDs, such as vpc-a123baa3, vpc-b456baa3.

                List<AWS::Route53::HostedZone::Id>
                An array of Amazon Route 53 hosted zone IDs, such as Z23YXV4OVPL04A, Z23YXV4OVPL04B.

                AWS CloudFormation validates input values for these types against existing values in a user's account. For example, with the AWS::EC2::VPC::Id type, a user must enter an existing VPC ID that is in her account and in the region in which she is creating the stack.

                Group and Sort Parameters in the AWS CloudFormation Console

                When you use the AWS CloudFormation console to create or update a stack, the console alphabetically lists input parameters by their logical ID. To override the default ordering, you can use the AWS::CloudFormation::interface metaProperties key. By grouping and ordering parameters, you make it easier for users to specify parameter values. For example, you could group all VPC-related parameters so that they aren't scattered throughout an alphabetical list.

                In the metaProperties key, you can specify the groups to create, the parameters to include in each group, and the order in which the console shows each parameter within its group. You can also define friendly parameter names so that the console shows descriptive names instead of logical IDs. All parameters that you reference in the metaProperties key must be declared in the Parameters section of the template.

    .PARAMETER Default
        A value of the appropriate type for the template to use if no value is specified when a stack is created. If you define constraints for the parameter, you must specify a value that adheres to those constraints.

    .PARAMETER NoEcho
        Whether to mask the parameter value whenever anyone makes a call that describes the stack. If you set the value to true, the parameter value is masked with asterisks (*****).

    .PARAMETER AllowedPattern
        A regular expression that represents the patterns you want to allow for String types.

    .PARAMETER AllowedValues
        An array containing the list of values allowed for the parameter.

    .PARAMETER ConstraintDescription
        A string that explains the constraint when the constraint is violated.

    .PARAMETER Description
        A string of up to 4000 characters that describes the parameter.

    .PARAMETER MaxLength
        An integer value that determines the largest number of characters you want to allow for String types.

    .PARAMETER MaxValue
        A numeric value that determines the largest numeric value you want to allow for Number types.

    .PARAMETER MinLength
        An integer value that determines the smallest number of characters you want to allow for String types.

    .PARAMETER MinValue
        A numeric value that determines the smallest numeric value you want to allow for Number types.

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Mapping addition"
        $template.AddParameter(
            (New-VaporParameter -LogicalId "DBPort" -Default 3306 -Description "TCP/IP port for the Propertiesbase" -Type "Number" -MinValue 1150 -MaxValue 65535),
            (New-VaporParameter -LogicalId "DBPwd" -NoEcho -Description "The Propertiesbase admin account password" -Type "String" -MinLength 1 -MaxLength 41 -AllowedPattern "^[a-zA-Z0-9]*$")
        )

        When the template is exported, this will convert to:
            {
                "AWSTemplateFormatVersion": "2010-09-09",
                "Description": "Testing Mapping addition",
                "Parameters": {
                    "DBPwd": {
                        "Type": "String",
                        "NoEcho": {
                            "IsPresent": true
                        },
                        "Description": "The Propertiesbase admin account password",
                        "MinLength": 1,
                        "MaxLength": 41,
                        "AllowedPattern": "^[a-zA-Z0-9]*$"
                    },
                    "DBPort": {
                        "Type": "Number",
                        "Default": "3306",
                        "Description": "TCP/IP port for the Propertiesbase",
                        "MinValue": 1150,
                        "MaxValue": 65535
                    }
                }
            }

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSParameter])]
    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        $LogicalId,
        [parameter(Mandatory,Position = 1)]
        [ValidateSet("String","Number","List<Number>","CommaDelimitedList","AWS::EC2::AvailabilityZone::Name","AWS::EC2::Image::Id","AWS::EC2::Instance::Id","AWS::EC2::KeyPair::KeyName","AWS::EC2::SecurityGroup::GroupName","AWS::EC2::SecurityGroup::Id","AWS::EC2::Subnet::Id","AWS::EC2::Volume::Id","AWS::EC2::VPC::Id","AWS::Route53::HostedZone::Id","List<AWS::EC2::AvailabilityZone::Name>","List<AWS::EC2::Image::Id>","List<AWS::EC2::Instance::Id>","List<AWS::EC2::SecurityGroup::GroupName>","List<AWS::EC2::SecurityGroup::Id>","List<AWS::EC2::Subnet::Id>","List<AWS::EC2::Volume::Id>","List<AWS::EC2::VPC::Id>","List<AWS::Route53::HostedZone::Id>")]
        [string]
        $Type,
        [parameter(Position = 2)]
        [string]
        $Default,
        [parameter(Position = 3)]
        [Switch]
        $NoEcho,
        [parameter(Position = 4)]
        [string]
        $AllowedPattern,
        [parameter(Position = 5)]
        [string[]]
        $AllowedValues,
        [parameter(Position = 6)]
        [string]
        $ConstraintDescription,
        [parameter(Position = 7)]
        [string]
        $Description,
        [parameter(Position = 8)]
        [int]
        $MaxLength,
        [parameter(Position = 9)]
        [int]
        $MaxValue,
        [parameter(Position = 10)]
        [int]
        $MinLength,
        [parameter(Position = 11)]
        [int]
        $MinValue
    )
    $obj = [VSParameter]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporParameter'

function New-VaporResource {
    <#
    .SYNOPSIS
        Adds a Resource object to the template

    .DESCRIPTION
        The required Resources section declares the AWS resources that you want to include in the stack, such as an Amazon EC2 instance or an Amazon S3 bucket. You must declare each resource separately; however, if you have multiple resources of the same type, you can declare them together by separating them with commas.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html

    .PARAMETER LogicalId
        The logical ID must be alphanumeric (A-Za-z0-9) and unique within the template. Use the logical name to reference the resource in other parts of the template. For example, if you want to map an Amazon Elastic Block Store volume to an Amazon EC2 instance, you reference the logical IDs to associate the block stores with the instance.

        In addition to the logical ID, certain resources also have a physical ID, which is the actual assigned name for that resource, such as an EC2 instance ID or an S3 bucket name. Use the physical IDs to identify resources outside of AWS CloudFormation templates, but only after the resources have been created. For example, you might give an EC2 instance resource a logical ID of MyEC2Instance; but when AWS CloudFormation creates the instance, AWS CloudFormation automatically generates and assigns a physical ID (such as i-28f9ba55) to the instance. You can use this physical ID to identify the instance and view its properties (such as the DNS name) by using the Amazon EC2 console. For resources that support custom names, you can assign your own names (physical IDs) to help you quickly identify resources. For example, you can name an S3 bucket that stores logs as MyPerformanceLogs.

    .PARAMETER Type
        The resource type identifies the type of resource that you are declaring. For example, AWS::EC2::Instance declares an EC2 instance. For a list of all of the resource types, see AWS Resource Types Reference.

    .PARAMETER Properties
        This is a collection of Resource properties are additional options that you can specify for a resource. For example, for each EC2 instance, you must specify an Amazon Machine Image (AMI) ID for that instance.

        You can use any of these 3 types for this parameter; "System.Collections.Hashtable","System.Management.Automation.PSCustomObject","Vaporshell.Resource.Properties"

    .PARAMETER CreationPolicy
        Use the CreationPolicy attribute when you want to wait on resource configuration actions before stack creation proceeds. For example, if you install and configure software applications on an EC2 instance, you might want those applications to be running before proceeding. In such cases, you can add a CreationPolicy attribute to the instance, and then send a success signal to the instance after the applications are installed and configured.

        You must use the "Add-CreationPolicy" function here.

    .PARAMETER DeletionPolicy
        With the DeletionPolicy attribute you can preserve or (in some cases) backup a resource when its stack is deleted. You specify a DeletionPolicy attribute for each resource that you want to control. If a resource has no DeletionPolicy attribute, AWS CloudFormation deletes the resource by default.

        To keep a resource when its stack is deleted, specify Retain for that resource. You can use retain for any resource. For example, you can retain a nested stack, S3 bucket, or EC2 instance so that you can continue to use or modify those resources after you delete their stacks.

        You must use one of the following options: "Delete","Retain","Snapshot"

    .PARAMETER DependsOn
        With the DependsOn attribute you can specify that the creation of a specific resource follows another. When you add a DependsOn attribute to a resource, that resource is created only after the creation of the resource specified in the DependsOn attribute.

        This parameter takes a string or list of strings representing Logical IDs of resources that must be created prior to this resource being created.

    .PARAMETER Metadata
        The Metadata attribute enables you to associate structured data with a resource. By adding a Metadata attribute to a resource, you can add data in JSON or YAML to the resource declaration. In addition, you can use intrinsic functions (such as GetAtt and Ref), parameters, and pseudo parameters within the Metadata attribute to add those interpreted values.

        You must use a PSCustomObject containing key/value pairs here. This will be returned when describing the resource using AWS CLI.

    .PARAMETER UpdatePolicy
        Use the UpdatePolicy attribute to specify how AWS CloudFormation handles updates to the AWS::AutoScaling::AutoScalingGroup resource. AWS CloudFormation invokes one of three update policies depending on the type of change you make or whether a scheduled action is associated with the Auto Scaling group.

        You must use the "Add-UpdatePolicy" function here.

    .PARAMETER Condition
        Logical ID of the condition that this resource needs to be true in order for this resource to be provisioned.

    .EXAMPLE
        $template = Initialize-Vaporshell -Description "Testing Resource addition"
        $template.AddResource((
            New-VaporResource -LogicalId "MyInstance" -Type "AWS::EC2::Instance" -Properties [PSCustomObject]@{
                "UserProperties" = (Add-FnBase64 -ValueToEncode (Add-FnJoin -ListOfValues "Queue=",(Add-FnRef -Ref "MyQueue")))
                "AvailabilityZone" = "us-east-1a"
                "ImageId" = "ami-20b65349"
            }
        ))

        When the template is exported, this will convert to:
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Testing Resource addition",
    "Resources": {
        "MyInstance": {
            "Type": "AWS::EC2::Instance",
            "Properties": {
                "UserProperties": {
                    "Fn::Base64": {
                        "Fn::Join": [
                            "",
                            [
                                "Queue=",
                                {
                                "Ref": "MyQueue"
                                }
                            ]
                        ]
                    }
                },
                "AvailabilityZone": "us-east-1a",
                "ImageId": "ami-20b65349"
            }
        }
    }
}
```

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSResource])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidateLogicalId()]
        [string]
        $LogicalId,
        [parameter(Mandatory,Position = 1)]
        [string]
        $Type,
        [parameter(Position = 2)]
        [VSHashtable]
        $Properties,
        [parameter(Position = 3)]
        [CreationPolicy]
        $CreationPolicy,
        [parameter(Position = 4)]
        [DeletionPolicy]
        $DeletionPolicy,
        [parameter()]
        [UpdateReplacePolicy]
        $UpdateReplacePolicy,
        [parameter(Position = 5)]
        [string[]]
        $DependsOn,
        [parameter(Position = 6)]
        [System.Collections.IDictionary]
        $Metadata,
        [parameter(Position = 7)]
        [UpdatePolicy]
        $UpdatePolicy,
        [parameter(Position = 8)]
        [object]
        $Condition
    )
    $obj = [VSResource]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'New-VaporResource'

function Add-CreationPolicy {
    <#
    .SYNOPSIS
        Adds a CreationPolicy property to a resoure on the template

    .DESCRIPTION
        Associate the CreationPolicy attribute with a resource to prevent its status from reaching create complete until AWS CloudFormation receives a specified number of success signals or the timeout period is exceeded. To signal a resource, you can use the cfn-signal helper script or SignalResource API. AWS CloudFormation publishes valid signals to the stack events so that you track the number of signals sent.

        The creation policy is invoked only when AWS CloudFormation creates the associated resource. Currently, the only AWS CloudFormation resources that support creation policies are AWS::AutoScaling::AutoScalingGroup, AWS::EC2::Instance, and AWS::CloudFormation::WaitCondition.

        Use the CreationPolicy attribute when you want to wait on resource configuration actions before stack creation proceeds. For example, if you install and configure software applications on an EC2 instance, you might want those applications to be running before proceeding. In such cases, you can add a CreationPolicy attribute to the instance, and then send a success signal to the instance after the applications are installed and configured.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-creationpolicy.html

    .PARAMETER AutoScalingCreationPolicy
        For an Auto Scaling group replacement update, specifies how many instances must signal success for the update to succeed.

        Parameter accepts a PSCustomObject. Use this if you are customizing the AutoScalingCreationPolicy properties outside of MinSuccessfulInstancesPercent.

    .PARAMETER MinSuccessfulInstancesPercent
        Specifies the percentage of instances in an Auto Scaling replacement update that must signal success for the update to succeed. You can specify a value from 0 to 100. AWS CloudFormation rounds to the nearest tenth of a percent. For example, if you update five instances with a minimum successful percentage of 50, three instances must signal success. If an instance doesn't send a signal within the time specified by the Timeout property, AWS CloudFormation assumes that the instance wasn't created.

    .PARAMETER ResourceSignal
        When AWS CloudFormation creates the associated resource, configures the number of required success signals and the length of time that AWS CloudFormation waits for those signals.

        Parameter accepts a PSCustomObject. Use this if you are customizing the ResourceSignal properties outside of Count and/or Timeout.

    .PARAMETER Count
        The number of success signals AWS CloudFormation must receive before it sets the resource status as CREATE_COMPLETE. If the resource receives a failure signal or doesn't receive the specified number of signals before the timeout period expires, the resource creation fails and AWS CloudFormation rolls the stack back.

    .PARAMETER Timeout
        The length of time that AWS CloudFormation waits for the number of signals that was specified in the Count property. The timeout period starts after AWS CloudFormation starts creating the resource, and the timeout expires no sooner than the time you specify but can occur shortly thereafter. The maximum time that you can specify is 12 hours.

        The value must be in ISO8601 duration format, in the form: "PT#H#M#S", where each # is the number of hours, minutes, and seconds, respectively. For best results, specify a period of time that gives your instances plenty of time to get up and running. A shorter timeout can cause a rollback.

    .EXAMPLE
        $templateInit = Initialize-Vaporshell -Description "Testing"
        $templateInit.AddResource(
            (
                New-VaporResource -LogicalId "AutoScalingGroup" -Type "AWS::AutoScaling::AutoScalingGroup" -Properties ([PSCustomObject][Ordered]@{
                        "AvailabilityZones" = (Add-FnGetAZs -Region "$_AWSRegion")
                        "LaunchConfigurationName" = (Add-FnRef -Ref "LaunchConfig")
                        "DesiredCapacity" = "3"
                        "MinSize" = "1"
                        "MaxSize" = "4"
                    }) -CreationPolicy (Add-CreationPolicy -Count 3 -Timeout "PT15M") -UpdatePolicy (Add-UpdatePolicy -IgnoreUnmodifiedGroupSizeProperties True -MinInstancesInService 1 -MaxBatchSize 2 -WaitOnResourceSignals True -PauseTime "PT10M")
            )
        )

        When the template is exported, this will convert to:
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Testing",
    "Resources": {
        "AutoScalingGroup": {
            "Type": "AWS::AutoScaling::AutoScalingGroup",
            "Properties": {
                "AvailabilityZones": {
                    "Fn::GetAZs": "AWS::Region"
                },
                "LaunchConfigurationName": {
                    "Ref": "LaunchConfig"
                },
                "DesiredCapacity": "3",
                "MinSize": "1",
                "MaxSize": "4"
            },
            "CreationPolicy": {
                "ResourceSignal": {
                    "Count": "3",
                    "Timeout": "PT15M"
                }
            },
            "UpdatePolicy": {
                "AutoScalingScheduledAction": {
                    "IgnoreUnmodifiedGroupSizeProperties": "true"
                },
                "AutoScalingRollingUpdate": {
                    "MinInstancesInService": "1",
                    "MaxBatchSize": "2",
                    "WaitOnResourceSignals": "true",
                    "PauseTime": "PT10M"
                }
            }
        }
    }
}
```

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([CreationPolicy])]
    [CmdletBinding(DefaultParameterSetName="CountTimeout")]
    Param(
        [parameter(Position = 0)]
        [parameter(ParameterSetName="AutoScalingCreationPolicy")]
        [parameter(ParameterSetName="ResourceSignal")]
        [parameter(ParameterSetName="CountTimeout")]
        [AutoScalingCreationPolicy]
        $AutoScalingCreationPolicy,
        [parameter()]
        [parameter(ParameterSetName="MinSuccessfulInstancesPercent")]
        [parameter(ParameterSetName="ResourceSignal")]
        [parameter(ParameterSetName="CountTimeout")]
        [ValidateRange(0,100)]
        [object]
        $MinSuccessfulInstancesPercent,
        [parameter(Position = 2)]
        [parameter(ParameterSetName="AutoScalingCreationPolicy")]
        [parameter(ParameterSetName="MinSuccessfulInstancesPercent")]
        [parameter(ParameterSetName="ResourceSignal")]
        [ResourceSignal]
        $ResourceSignal,
        [parameter(Position = 3)]
        [parameter(ParameterSetName="AutoScalingCreationPolicy")]
        [parameter(ParameterSetName="MinSuccessfulInstancesPercent")]
        [parameter(ParameterSetName="CountTimeout")]
        [object]
        $Count,
        [parameter(Position = 4)]
        [parameter(ParameterSetName="AutoScalingCreationPolicy")]
        [parameter(ParameterSetName="MinSuccessfulInstancesPercent")]
        [parameter(ParameterSetName="CountTimeout")]
        [object]
        $Timeout
    )
    Begin {
        if (!($PSBoundParameters.Keys.Count)) {
            $PSCmdlet.ThrowTerminatingError((New-VSError -String "No parameters passed! Please specify at least one parameter, otherwise exclude this call of $($MyInvocation.MyCommand)."))
        }
        $obj = [CreationPolicy]::new()
        $ASCP = [AutoScalingCreationPolicy]::new()
        $RS = [ResourceSignal]::new()
    }
    Process {
        switch ($PSBoundParameters.Keys) {
            'AutoScalingCreationPolicy' {
                $obj.AutoScalingCreationPolicy = $AutoScalingCreationPolicy
            }
            'MinSuccessfulInstancesPercent' {
                $ASCP.MinSuccessfulInstancesPercent = $MinSuccessfulInstancesPercent
                $obj.AutoScalingCreationPolicy = $ASCP
            }
            'ResourceSignal' {
                $obj.ResourceSignal = $ResourceSignal
            }
            'Count' {
                $RS.Count = $Count
            }
            'Timeout' {
                $RS.Timeout = $Timeout
            }
        }
        if ($RS.Timeout -or $RS.Count) {
            $obj.ResourceSignal = $RS
        }
    }
    End {
        Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)`n"
        $obj
    }
}

Export-ModuleMember -Function 'Add-CreationPolicy'

function Add-UpdatePolicy {
    <#
    .SYNOPSIS
        Adds an UpdatePolicy property to a resoure on the template

    .DESCRIPTION
        Use the UpdatePolicy attribute to specify how AWS CloudFormation handles updates to the AWS::AutoScaling::AutoScalingGroup resource. AWS CloudFormation invokes one of three update policies depending on the type of change you make or whether a scheduled action is associated with the Auto Scaling group.

        * The AutoScalingReplacingUpdate and AutoScalingRollingUpdate policies apply only when you do one or more of the following:
            * Change the Auto Scaling group's AWS::AutoScaling::LaunchConfiguration.
            * Change the Auto Scaling group's VPCZoneIdentifier property
            * Update an Auto Scaling group that contains instances that don't match the current LaunchConfiguration.
        * If both the AutoScalingReplacingUpdate and AutoScalingRollingUpdate policies are specified, setting the WillReplace property to true gives AutoScalingReplacingUpdate precedence.
        * The AutoScalingScheduledAction policy applies when you update a stack that includes an Auto Scaling group with an associated scheduled action.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html

    .PARAMETER AutoScalingReplacingUpdate
        To specify how AWS CloudFormation handles replacement updates for an Auto Scaling group, use the AutoScalingReplacingUpdate policy. This policy enables you to specify whether AWS CloudFormation replaces an Auto Scaling group with a new one or replaces only the instances in the Auto Scaling group.

            **Important**
                Before attempting an update, ensure that you have sufficient Amazon EC2 capacity for both your old and new Auto Scaling groups.

        Parameter accepts a PSCustomObject. Use this if you are customizing the AutoScalingReplacingUpdate properties outside of WillReplace.

    .PARAMETER WillReplace
        Specifies whether an Auto Scaling group and the instances it contains are replaced during an update. During replacement, AWS CloudFormation retains the old group until it finishes creating the new one. If the update fails, AWS CloudFormation can roll back to the old Auto Scaling group and delete the new Auto Scaling group.

        While AWS CloudFormation creates the new group, it doesn't detach or attach any instances. After successfully creating the new Auto Scaling group, AWS CloudFormation deletes the old Auto Scaling group during the cleanup process.

        When you set the WillReplace parameter, remember to specify a matching CreationPolicy. If the minimum number of instances (specified by the WillReplace property) don't signal success within the Timeout period (specified in the CreationPolicy policy), the replacement update fails and AWS CloudFormation rolls back to the old Auto Scaling group.

    .PARAMETER AutoScalingRollingUpdate
        To specify how AWS CloudFormation handles rolling updates for an Auto Scaling group, use the AutoScalingRollingUpdate policy. Rolling updates enable you to specify whether AWS CloudFormation updates instances that are in an Auto Scaling group in batches or all at once.

            **Important**
                During a rolling update, some Auto Scaling processes might make changes to the Auto Scaling group before AWS CloudFormation completes the rolling update. These changes might cause the rolling update to fail. To prevent Auto Scaling from running processes during a rolling update, use the SuspendProcesses property.

        Parameter accepts a PSCustomObject. Use this if you are customizing the AutoScalingRollingUpdate properties outside of MaxBatchSize, MinInstancesInService, WillReplace, PauseTime, SuspendProcesses, and/or WaitOnAutoScalingRollingUpdates.

    .PARAMETER MaxBatchSize
        Specifies the maximum number of instances that AWS CloudFormation updates.

    .PARAMETER MinInstancesInService
        Specifies the minimum number of instances that must be in service within the Auto Scaling group while AWS CloudFormation updates old instances.

    .PARAMETER WillReplace
        Specifies the percentage of instances in an Auto Scaling rolling update that must signal success for an update to succeed. You can specify a value from 0 to 100. AWS CloudFormation rounds to the nearest tenth of a percent. For example, if you update five instances with a minimum successful percentage of 50, three instances must signal success.

        If an instance doesn't send a signal within the time specified in the PauseTime property, AWS CloudFormation assumes that the instance wasn't updated.

        If you specify this property, you must also enable the WaitOnAutoScalingRollingUpdates and PauseTime properties.

    .PARAMETER PauseTime
        The amount of time that AWS CloudFormation pauses after making a change to a batch of instances to give those instances time to start software applications. For example, you might need to specify PauseTime when scaling up the number of instances in an Auto Scaling group.

        If you enable the WaitOnAutoScalingRollingUpdates property, PauseTime is the amount of time that AWS CloudFormation should wait for the Auto Scaling group to receive the required number of valid signals from added or replaced instances. If the PauseTime is exceeded before the Auto Scaling group receives the required number of signals, the update fails. For best results, specify a time period that gives your applications sufficient time to get started. If the update needs to be rolled back, a short PauseTime can cause the rollback to fail.

        Specify PauseTime in the ISO8601 duration format (in the format PT#H#M#S, where each # is the number of hours, minutes, and seconds, respectively). The maximum PauseTime is one hour (PT1H).

        Default: PT0S (zero seconds). If the WaitOnAutoScalingRollingUpdates property is set to true, the default is PT5M.

    .PARAMETER SuspendProcesses
        Specifies the Auto Scaling processes to suspend during a stack update. Suspending processes prevents Auto Scaling from objecterfering with a stack update. For example, you can suspend alarming so that Auto Scaling doesn't execute scaling policies associated with an alarm. For valid values, see the `ScalingProcesses.member.N parameter` for the SuspendProcesses action in the Auto Scaling API Reference.

    .PARAMETER WaitOnAutoScalingRollingUpdates
        Specifies whether the Auto Scaling group waits on signals from new instances during an update. Use this property to ensure that instances have completed installing and configuring applications before the Auto Scaling group update proceeds. AWS CloudFormation suspends the update of an Auto Scaling group after new EC2 instances are launched objecto the group. AWS CloudFormation must receive a signal from each new instance within the specified PauseTime before continuing the update. To signal the Auto Scaling group, use the cfn-signal helper script or SignalResource API.

        To have instances wait for an Elastic Load Balancing health check before they signal success, add a health-check verification by using the cfn-init helper script. For an example, see the `verify_instance_health` command in the Auto Scaling rolling updates sample template.

    .PARAMETER AutoScalingScheduledAction
        To specify how AWS CloudFormation handles updates for the MinSize, MaxSize, and DesiredCapacity properties when the AWS::AutoScaling::AutoScalingGroup resource has an associated scheduled action, use the AutoScalingScheduledAction policy.

        With scheduled actions, the group size properties of an Auto Scaling group can change at any time. When you update a stack with an Auto Scaling group and scheduled action, AWS CloudFormation always sets the group size property values of your Auto Scaling group to the values that are defined in the AWS::AutoScaling::AutoScalingGroup resource of your template, even if a scheduled action is in effect.

        If you do not want AWS CloudFormation to change any of the group size property values when you have a scheduled action in effect, use the AutoScalingScheduledAction update policy to prevent AWS CloudFormation from changing the MinSize, MaxSize, or DesiredCapacity properties unless you have modified these values in your template.

        Parameter accepts a PSCustomObject. Use this if you are customizing the AutoScalingScheduledAction properties outside of IgnoreUnmodifiedGroupSizeProperties.

    .PARAMETER IgnoreUnmodifiedGroupSizeProperties
        Specifies whether AWS CloudFormation ignores differences in group size properties between your current Auto Scaling group and the Auto Scaling group described in the AWS::AutoScaling::AutoScalingGroup resource of your template during a stack update. If you modify any of the group size property values in your template, AWS CloudFormation uses the modified values and updates your Auto Scaling group.

    .EXAMPLE
        $templateInit = Initialize-Vaporshell -Description "Testing"
        $templateInit.AddResource(
            (
                New-VaporResource -LogicalId "AutoScalingGroup" -Type "AWS::AutoScaling::AutoScalingGroup" -Properties ([PSCustomObject][Ordered]@{
                        "AvailabilityZones" = (Add-FnGetAZs -Region "$_AWSRegion")
                        "LaunchConfigurationName" = (Add-FnRef -Ref "LaunchConfig")
                        "DesiredCapacity" = "3"
                        "MinSize" = "1"
                        "MaxSize" = "4"
                    }) -CreationPolicy (Add-CreationPolicy -Count 3 -Timeout "PT15M") -UpdatePolicy (Add-UpdatePolicy -IgnoreUnmodifiedGroupSizeProperties True -MinInstancesInService 1 -MaxBatchSize 2 -WaitOnResourceSignals True -PauseTime "PT10M")
            )
        )

        When the template is exported, this will convert to:
```json
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Testing",
    "Resources": {
        "AutoScalingGroup": {
            "Type": "AWS::AutoScaling::AutoScalingGroup",
            "Properties": {
                "AvailabilityZones": {
                    "Fn::GetAZs": "AWS::Region"
                },
                "LaunchConfigurationName": {
                    "Ref": "LaunchConfig"
                },
                "DesiredCapacity": "3",
                "MinSize": "1",
                "MaxSize": "4"
            },
            "CreationPolicy": {
                "ResourceSignal": {
                    "Count": "3",
                    "Timeout": "PT15M"
                }
            },
            "UpdatePolicy": {
                "AutoScalingScheduledAction": {
                    "IgnoreUnmodifiedGroupSizeProperties": "true"
                },
                "AutoScalingRollingUpdate": {
                    "MinInstancesInService": "1",
                    "MaxBatchSize": "2",
                    "WaitOnResourceSignals": "true",
                    "PauseTime": "PT10M"
                }
            }
        }
    }
}
```
    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([UpdatePolicy])]
    [cmdletbinding(DefaultParameterSetName="AutoScalingRollingUpdateDetails")]
    Param(
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [AutoScalingReplacingUpdate]
        $AutoScalingReplacingUpdate,
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [object]
        $WillReplace,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [AutoScalingRollingUpdate]
        $AutoScalingRollingUpdate,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [object]
        $MaxBatchSize,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [object]
        $MinInstancesInService,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $MinSuccessfulInstancesPercent,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $PauseTime,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [AutoScalingProcess[]]
        $SuspendProcesses,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $WaitOnResourceSignals,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [AutoScalingScheduledAction]
        $AutoScalingScheduledAction,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $IgnoreUnmodifiedGroupSizeProperties,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdate")]
        [CodeDeployLambdaAliasUpdate]
        $CodeDeployLambdaAliasUpdate,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $AfterAllowTrafficHook,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $ApplicationName,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $BeforeAllowTrafficHook,
        [parameter(ParameterSetName="AutoScalingReplacingUpdate")]
        [parameter(ParameterSetName="AutoScalingReplacingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingRollingUpdate")]
        [parameter(ParameterSetName="AutoScalingRollingUpdateDetails")]
        [parameter(ParameterSetName="AutoScalingScheduledAction")]
        [parameter(ParameterSetName="AutoScalingScheduledActionDetails")]
        [parameter(ParameterSetName="CodeDeployLambdaAliasUpdateDetails")]
        [object]
        $DeploymentGroupName
    )
    Begin {
        if (!($PSBoundParameters.Keys.Count)) {
            $PSCmdlet.ThrowTerminatingError((New-VSError -object "No parameters passed! Please specify at least one parameter, otherwise exclude this call of $($MyInvocation.MyCommand)."))
        }
        $obj = [UpdatePolicy]::new()
        $ASRepU = [AutoScalingReplacingUpdate]::new()
        $ASRolU = [AutoScalingRollingUpdate]::new()
        $ASSA = [AutoScalingScheduledAction]::new()
        $ASSA = [AutoScalingScheduledAction]::new()
        $CDLA = [CodeDeployLambdaAliasUpdate]::new()
    }
    Process {
        switch ($PSBoundParameters.Keys) {
            'AutoScalingReplacingUpdate' {
                $obj.AutoScalingReplacingUpdate = $AutoScalingReplacingUpdate
            }
            'WillReplace' {
                $ASRepU.WillReplace = $WillReplace
                $obj.AutoScalingReplacingUpdate = $ASRepU
            }
            'AutoScalingRollingUpdate' {
                $obj.AutoScalingRollingUpdate = $AutoScalingRollingUpdate
            }
            'MaxBatchSize' {
                $ASRolU.MaxBatchSize = $MaxBatchSize
            }
            'MinInstancesInService' {
                $ASRolU.MinInstancesInService = $MinInstancesInService
            }
            'MinSuccessfulInstancesPercent' {
                $ASRolU.MinSuccessfulInstancesPercent = $MinSuccessfulInstancesPercent
            }
            'PauseTime' {
                $ASRolU.PauseTime = $PauseTime
            }
            'SuspendProcesses' {
                $ASRolU.SuspendProcesses = $SuspendProcesses
            }
            'WaitOnResourceSignals' {
                $ASRolU.WaitOnResourceSignals = $WaitOnResourceSignals
            }
            'AutoScalingScheduledAction' {
                $obj.AutoScalingScheduledAction = $AutoScalingScheduledAction
            }
            'IgnoreUnmodifiedGroupSizeProperties' {
                $ASSA.IgnoreUnmodifiedGroupSizeProperties = $IgnoreUnmodifiedGroupSizeProperties
                $obj.AutoScalingScheduledAction = $ASSA
            }
            'CodeDeployLambdaAliasUpdate' {
                $obj.CodeDeployLambdaAliasUpdate = $CodeDeployLambdaAliasUpdate
            }
            'AfterAllowTrafficHook' {
                $CDLA.AfterAllowTrafficHook = $AfterAllowTrafficHook
            }
            'ApplicationName' {
                $CDLA.ApplicationName = $ApplicationName
            }
            'BeforeAllowTrafficHook' {
                $CDLA.BeforeAllowTrafficHook = $BeforeAllowTrafficHook
            }
            'DeploymentGroupName' {
                $CDLA.DeploymentGroupName = $DeploymentGroupName
            }
        }
        foreach ($prop in @('MaxBatchSize','MinInstancesInService','MinSuccessfulInstancesPercent','PauseTime','SuspendProcesses','WaitOnResourceSignals')) {
            if ($prop -in $PSBoundParameters.Keys) {
                $obj.AutoScalingRollingUpdate = $ASRolU
                break
            }
        }
        foreach ($prop in @('AfterAllowTrafficHook','ApplicationName','BeforeAllowTrafficHook','DeploymentGroupName')) {
            if ($prop -in $PSBoundParameters.Keys) {
                $obj.CodeDeployLambdaAliasUpdate = $CDLA
                break
            }
        }
    }
    End {
        Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
        $obj
    }
}

Export-ModuleMember -Function 'Add-UpdatePolicy'

function Add-PolicyAction {
    <#
    .SYNOPSIS
    Adds a tab-completing Policy Action for an IAM policy document

    .DESCRIPTION
    Adds a tab-completing Policy Action for an IAM policy document

    .PARAMETER Action
    The tab-completing list of available actions.

    .PARAMETER Custom
    Anything custom, i.e. containing wildcards like s3:List*

    .EXAMPLE
    $policyDoc = [PSCustomObject]@{
        Version = '2012-10-17'
        Statement = @(
            @{
                Effect = 'Allow'
                Action = @(
                    Add-PolicyAction "ssm:*"
                )
                Resource = $documentArn
            }
        )
    }
    $newVSIAMPolicySplat = @{
        LogicalId = "CloudWatchSSMRunCommandPolicy"
        PolicyName = "CloudWatchSSMRunCommandPolicy"
        PolicyDocument = $policyDoc
    }
    $iamPolicy = New-VSIAMPolicy @newVSIAMPolicySplat
    #>

    [CmdletBinding(DefaultParameterSetName = "Custom")]
    Param(
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Action")]
        [ValidateSet('a4b:AssociateDeviceWithRoom','a4b:AssociateSkillGroupWithRoom','a4b:CreateProfile','a4b:CreateRoom','a4b:CreateSkillGroup','a4b:CreateUser','a4b:DeleteProfile','a4b:DeleteRoom','a4b:DeleteRoomSkillParameter','a4b:DeleteSkillGroup','a4b:DeleteUser','a4b:DisassociateDeviceFromRoom','a4b:DisassociateSkillGroupFromRoom','a4b:GetDevice','a4b:GetProfile','a4b:GetRoom','a4b:GetRoomSkillParameter','a4b:GetSkillGroup','a4b:ListSkills','a4b:ListTags','a4b:PutRoomSkillParameter','a4b:ResolveRoom','a4b:RevokeInvitation','a4b:SearchDevices','a4b:SearchProfiles','a4b:SearchRooms','a4b:SearchSkillGroups','a4b:SearchUsers','a4b:SendInvitation','a4b:StartDeviceSync','a4b:TagResource','a4b:UntagResource','a4b:UpdateDevice','a4b:UpdateProfile','a4b:UpdateRoom','a4b:UpdateSkillGroup','acm:AddTagsToCertificate','acm:DeleteCertificate','acm:DescribeCertificate','acm:GetCertificate','acm:ImportCertificate','acm:ListCertificates','acm:ListTagsForCertificate','acm:RemoveTagsFromCertificate','acm:RequestCertificate','acm:ResendValidationEmail','apigateway:DELETE','apigateway:GET','apigateway:HEAD','apigateway:OPTIONS','apigateway:PATCH','apigateway:POST','apigateway:PUT','application-autoscaling:DeleteScalingPolicy','application-autoscaling:DeleteScheduledAction','application-autoscaling:DeregisterScalableTarget','application-autoscaling:DescribeScalableTargets','application-autoscaling:DescribeScalingActivities','application-autoscaling:DescribeScalingPolicies','application-autoscaling:DescribeScheduledActions','application-autoscaling:PutScalingPolicy','application-autoscaling:PutScheduledAction','application-autoscaling:RegisterScalableTarget','appstream:AssociateFleet','appstream:CreateDirectoryConfig','appstream:CreateFleet','appstream:CreateImageBuilder','appstream:CreateImageBuilderStreamingURL','appstream:CreateStack','appstream:CreateStreamingURL','appstream:DeleteDirectoryConfig','appstream:DeleteFleet','appstream:DeleteImage','appstream:DeleteImageBuilder','appstream:DeleteStack','appstream:DescribeDirectoryConfigs','appstream:DescribeFleets','appstream:DescribeImageBuilders','appstream:DescribeImages','appstream:DescribeSessions','appstream:DescribeStacks','appstream:DisassociateFleet','appstream:ExpireSession','appstream:ListAssociatedFleets','appstream:ListAssociatedStacks','appstream:ListTagsForResource','appstream:StartFleet','appstream:StartImageBuilder','appstream:StopFleet','appstream:StopImageBuilder','appstream:Stream','appstream:TagResource','appstream:UntagResource','appstream:UpdateDirectoryConfig','appstream:UpdateFleet','appstream:UpdateStack','appsync:CreateApiKey','appsync:CreateDataSource','appsync:CreateGraphqlApi','appsync:CreateResolver','appsync:CreateType','appsync:DeleteApiKey','appsync:DeleteDataSource','appsync:DeleteGraphqlApi','appsync:DeleteResolver','appsync:DeleteType','appsync:GetDataSource','appsync:GetGraphqlApi','appsync:GetIntrospectionSchema','appsync:GetResolver','appsync:GetSchemaCreationStatus','appsync:GetType','appsync:GraphQL','appsync:ListApiKeys','appsync:ListDataSources','appsync:ListGraphqlApis','appsync:ListResolvers','appsync:ListTypes','appsync:StartSchemaCreation','appsync:UpdateApiKey','appsync:UpdateDataSource','appsync:UpdateGraphqlApi','appsync:UpdateResolver','appsync:UpdateType','artifact:AcceptAgreement','artifact:DownloadAgreement','artifact:Get','artifact:TerminateAgreement','athena:BatchGetNamedQuery','athena:BatchGetQueryExecution','athena:CancelQueryExecution','athena:CreateNamedQuery','athena:DeleteNamedQuery','athena:GetCatalogs','athena:GetExecutionEngine','athena:GetExecutionEngines','athena:GetNamedQuery','athena:GetNamespace','athena:GetNamespaces','athena:GetQueryExecution','athena:GetQueryExecutions','athena:GetQueryResults','athena:GetTable','athena:GetTables','athena:ListNamedQueries','athena:ListQueryExecutions','athena:RunQuery','athena:StartQueryExecution','athena:StopQueryExecution','autoscaling:AttachInstances','autoscaling:AttachLoadBalancers','autoscaling:AttachLoadBalancerTargetGroups','autoscaling:CompleteLifecycleAction','autoscaling:CreateAutoScalingGroup','autoscaling:CreateLaunchConfiguration','autoscaling:CreateOrUpdateTags','autoscaling:DeleteAutoScalingGroup','autoscaling:DeleteLaunchConfiguration','autoscaling:DeleteLifecycleHook','autoscaling:DeleteNotificationConfiguration','autoscaling:DeletePolicy','autoscaling:DeleteScheduledAction','autoscaling:DeleteTags','autoscaling:DescribeAccountLimits','autoscaling:DescribeAdjustmentTypes','autoscaling:DescribeAutoScalingGroups','autoscaling:DescribeAutoScalingInstances','autoscaling:DescribeAutoScalingNotificationTypes','autoscaling:DescribeLaunchConfigurations','autoscaling:DescribeLifecycleHooks','autoscaling:DescribeLifecycleHookTypes','autoscaling:DescribeLoadBalancers','autoscaling:DescribeLoadBalancerTargetGroups','autoscaling:DescribeMetricCollectionTypes','autoscaling:DescribeNotificationConfigurations','autoscaling:DescribePolicies','autoscaling:DescribeScalingActivities','autoscaling:DescribeScalingProcessTypes','autoscaling:DescribeScheduledActions','autoscaling:DescribeTags','autoscaling:DescribeTerminationPolicyTypes','autoscaling:DetachInstances','autoscaling:DetachLoadBalancers','autoscaling:DetachLoadBalancerTargetGroups','autoscaling:DisableMetricsCollection','autoscaling:EnableMetricsCollection','autoscaling:EnterStandby','autoscaling:ExecutePolicy','autoscaling:ExitStandby','autoscaling:PutLifecycleHook','autoscaling:PutNotificationConfiguration','autoscaling:PutScalingPolicy','autoscaling:PutScheduledUpdateGroupAction','autoscaling:RecordLifecycleActionHeartbeat','autoscaling:ResumeProcesses','autoscaling:SetDesiredCapacity','autoscaling:SetInstanceHealth','autoscaling:SetInstanceProtection','autoscaling:SuspendProcesses','autoscaling:TerminateInstanceInAutoScalingGroup','autoscaling:UpdateAutoScalingGroup','autoscaling-plans:CreateScalingPlan','autoscaling-plans:DeleteScalingPlan','autoscaling-plans:DescribeScalingPlanResources','autoscaling-plans:DescribeScalingPlans','aws-marketplace:BatchMeterUsage','aws-marketplace:MeterUsage','aws-marketplace:ResolveCustomer','aws-marketplace:Subscribe','aws-marketplace:Unsubscribe','aws-marketplace:ViewSubscriptions','aws-marketplace-management:uploadFiles','aws-marketplace-management:viewMarketing','aws-marketplace-management:viewReports','aws-marketplace-management:viewSupport','aws-portal:ModifyAccount','aws-portal:ModifyBilling','aws-portal:ModifyPaymentMethods','aws-portal:ViewAccount','aws-portal:ViewBilling','aws-portal:ViewPaymentMethods','aws-portal:ViewUsage','batch:CancelJob','batch:CreateComputeEnvironment','batch:CreateJobQueue','batch:DeleteComputeEnvironment','batch:DeleteJobQueue','batch:DeregisterJobDefinition','batch:DescribeComputeEnvironments','batch:DescribeJobDefinitions','batch:DescribeJobQueues','batch:DescribeJobs','batch:ListJobs','batch:RegisterJobDefinition','batch:SubmitJob','batch:TerminateJob','batch:UpdateComputeEnvironment','batch:UpdateJobQueue','budgets:ModifyBudget','budgets:ViewBudget','ce:GetCostAndUsage','ce:GetDimensionValues','ce:GetReservationUtilization','ce:GetTags','chime:AcceptDelegate','chime:ActivateUsers','chime:AddDomain','chime:AddOrUpdateGroups','chime:AuthorizeDirectory','chime:ConnectDirectory','chime:CreateAccount','chime:CreateCDRBucket','chime:DeleteAccount','chime:DeleteCDRBucket','chime:DeleteDelegate','chime:DeleteDomain','chime:DeleteGroups','chime:DisconnectDirectory','chime:GetAccount','chime:GetAccountResource','chime:GetAccountSettings','chime:GetCDRBucket','chime:GetDomain','chime:GetUser','chime:GetUserByEmail','chime:InviteDelegate','chime:InviteUsers','chime:ListAccounts','chime:ListCDRBucket','chime:ListDelegates','chime:ListDirectories','chime:ListDomains','chime:ListGroups','chime:ListUsers','chime:LogoutUser','chime:RenameAccount','chime:RenewDelegate','chime:ResetAccountResource','chime:ResetPersonalPin','chime:SubmitSupportRequest','chime:SuspendUsers','chime:UnauthorizeDirectory','chime:UpdateAccountResource','chime:UpdateAccountSettings','chime:UpdateCDRBucket','chime:UpdateSupportedLicenses','chime:UpdateUserLicenses','chime:ValidateAccountResource','chime:ValidateDelegate','cloud9:CreateEnvironmentEC2','cloud9:CreateEnvironmentMembership','cloud9:CreateEnvironmentSSH','cloud9:DeleteEnvironment','cloud9:DeleteEnvironmentMembership','cloud9:DescribeEnvironmentMemberships','cloud9:DescribeEnvironments','cloud9:DescribeEnvironmentStatus','cloud9:GetUserPublicKey','cloud9:ListEnvironments','cloud9:UpdateEnvironment','cloud9:UpdateEnvironmentMembership','cloud9:ValidateEnvironmentName','clouddirectory:AddFacetToObject','clouddirectory:ApplySchema','clouddirectory:AttachObject','clouddirectory:AttachPolicy','clouddirectory:AttachToIndex','clouddirectory:AttachTypedLink','clouddirectory:BatchRead','clouddirectory:BatchWrite','clouddirectory:CreateDirectory','clouddirectory:CreateFacet','clouddirectory:CreateIndex','clouddirectory:CreateObject','clouddirectory:CreateSchema','clouddirectory:CreateTypedLinkFacet','clouddirectory:DeleteDirectory','clouddirectory:DeleteFacet','clouddirectory:DeleteObject','clouddirectory:DeleteSchema','clouddirectory:DeleteTypedLinkFacet','clouddirectory:DetachFromIndex','clouddirectory:DetachObject','clouddirectory:DetachPolicy','clouddirectory:DetachTypedLink','clouddirectory:DisableDirectory','clouddirectory:EnableDirectory','clouddirectory:GetDirectory','clouddirectory:GetFacet','clouddirectory:GetObjectInformation','clouddirectory:GetSchemaAsJson','clouddirectory:GetTypedLinkFacetInformation','clouddirectory:ListAppliedSchemaArns','clouddirectory:ListAttachedIndices','clouddirectory:ListDevelopmentSchemaArns','clouddirectory:ListDirectories','clouddirectory:ListFacetAttributes','clouddirectory:ListFacetNames','clouddirectory:ListIncomingTypedLinks','clouddirectory:ListIndex','clouddirectory:ListObjectAttributes','clouddirectory:ListObjectChildren','clouddirectory:ListObjectParentPaths','clouddirectory:ListObjectParents','clouddirectory:ListObjectPolicies','clouddirectory:ListOutgoingTypedLinks','clouddirectory:ListPolicyAttachments','clouddirectory:ListPublishedSchemaArns','clouddirectory:ListTagsForResource','clouddirectory:ListTypedLinkFacetAttributes','clouddirectory:ListTypedLinkFacetNames','clouddirectory:LookupPolicy','clouddirectory:PublishSchema','clouddirectory:PutSchemaFromJson','clouddirectory:RemoveFacetFromObject','clouddirectory:TagResource','clouddirectory:UntagResource','clouddirectory:UpdateFacet','clouddirectory:UpdateObjectAttributes','clouddirectory:UpdateSchema','clouddirectory:UpdateTypedLinkFacet','cloudformation:CancelUpdateStack','cloudformation:ContinueUpdateRollback','cloudformation:CreateChangeSet','cloudformation:CreateStack','cloudformation:CreateUploadBucket','cloudformation:DeleteChangeSet','cloudformation:DeleteStack','cloudformation:DescribeAccountLimits','cloudformation:DescribeChangeSet','cloudformation:DescribeStackEvents','cloudformation:DescribeStackResource','cloudformation:DescribeStackResources','cloudformation:DescribeStacks','cloudformation:EstimateTemplateCost','cloudformation:ExecuteChangeSet','cloudformation:GetStackPolicy','cloudformation:GetTemplate','cloudformation:GetTemplateSummary','cloudformation:ListChangeSets','cloudformation:ListExports','cloudformation:ListImports','cloudformation:ListStackResources','cloudformation:ListStacks','cloudformation:PreviewStackUpdate','cloudformation:SetStackPolicy','cloudformation:SignalResource','cloudformation:UpdateStack','cloudformation:UpdateTerminationProtection','cloudformation:ValidateTemplate','cloudfront:CreateCloudFrontOriginAccessIdentity','cloudfront:CreateDistribution','cloudfront:CreateDistributionWithTags','cloudfront:CreateInvalidation','cloudfront:CreateStreamingDistribution','cloudfront:CreateStreamingDistributionWithTags','cloudfront:DeleteCloudFrontOriginAccessIdentity','cloudfront:DeleteDistribution','cloudfront:DeleteStreamingDistribution','cloudfront:GetCloudFrontOriginAccessIdentity','cloudfront:GetCloudFrontOriginAccessIdentityConfig','cloudfront:GetDistribution','cloudfront:GetDistributionConfig','cloudfront:GetInvalidation','cloudfront:GetStreamingDistribution','cloudfront:GetStreamingDistributionConfig','cloudfront:ListCloudFrontOriginAccessIdentities','cloudfront:ListDistributions','cloudfront:ListDistributionsByWebACLId','cloudfront:ListInvalidations','cloudfront:ListStreamingDistributions','cloudfront:ListTagsForResource','cloudfront:TagResource','cloudfront:UntagResource','cloudfront:UpdateCloudFrontOriginAccessIdentity','cloudfront:UpdateDistribution','cloudfront:UpdateStreamingDistribution','cloudhsm:AddTagsToResource','cloudhsm:CreateHapg','cloudhsm:CreateHsm','cloudhsm:CreateLunaClient','cloudhsm:DeleteHapg','cloudhsm:DeleteHsm','cloudhsm:DeleteLunaClient','cloudhsm:DescribeHapg','cloudhsm:DescribeHsm','cloudhsm:DescribeLunaClient','cloudhsm:GetConfig','cloudhsm:ListAvailableZones','cloudhsm:ListHapgs','cloudhsm:ListHsms','cloudhsm:ListLunaClients','cloudhsm:ListTagsForResource','cloudhsm:ModifyHapg','cloudhsm:ModifyHsm','cloudhsm:ModifyLunaClient','cloudhsm:RemoveTagsFromResource','cloudsearch:AddTags','cloudsearch:BuildSuggesters','cloudsearch:CreateDomain','cloudsearch:DefineAnalysisScheme','cloudsearch:DefineExpression','cloudsearch:DefineIndexField','cloudsearch:DefineSuggester','cloudsearch:DeleteAnalysisScheme','cloudsearch:DeleteDomain','cloudsearch:DeleteExpression','cloudsearch:DeleteIndexField','cloudsearch:DeleteSuggester','cloudsearch:DescribeAnalysisSchemes','cloudsearch:DescribeAvailabilityOptions','cloudsearch:DescribeDomains','cloudsearch:DescribeExpressions','cloudsearch:DescribeIndexFields','cloudsearch:DescribeScalingParameters','cloudsearch:DescribeServiceAccessPolicies','cloudsearch:DescribeSuggesters','cloudsearch:document','cloudsearch:IndexDocuments','cloudsearch:ListDomainNames','cloudsearch:ListTags','cloudsearch:RemoveTags','cloudsearch:search','cloudsearch:suggest','cloudsearch:UpdateAvailabilityOptions','cloudsearch:UpdateScalingParameters','cloudsearch:UpdateServiceAccessPolicies','cloudtrail:AddTags','cloudtrail:CreateTrail','cloudtrail:DeleteTrail','cloudtrail:DescribeTrails','cloudtrail:GetEventSelectors','cloudtrail:GetTrailStatus','cloudtrail:ListPublicKeys','cloudtrail:ListTags','cloudtrail:LookupEvents','cloudtrail:PutEventSelectors','cloudtrail:RemoveTags','cloudtrail:StartLogging','cloudtrail:StopLogging','cloudtrail:UpdateTrail','cloudwatch:DeleteAlarms','cloudwatch:DeleteDashboards','cloudwatch:DescribeAlarmHistory','cloudwatch:DescribeAlarms','cloudwatch:DescribeAlarmsForMetric','cloudwatch:DisableAlarmActions','cloudwatch:EnableAlarmActions','cloudwatch:GetDashboard','cloudwatch:GetMetricData','cloudwatch:GetMetricStatistics','cloudwatch:ListDashboards','cloudwatch:ListMetrics','cloudwatch:PutDashboard','cloudwatch:PutMetricAlarm','cloudwatch:PutMetricData','cloudwatch:SetAlarmState','codebuild:BatchDeleteBuilds','codebuild:BatchGetBuilds','codebuild:BatchGetProjects','codebuild:CreateProject','codebuild:DeleteProject','codebuild:ListBuilds','codebuild:ListBuildsForProject','codebuild:ListConnectedOAuthAccounts','codebuild:ListCuratedEnvironmentImages','codebuild:ListProjects','codebuild:ListRepositories','codebuild:PersistOAuthToken','codebuild:StartBuild','codebuild:StopBuild','codebuild:UpdateProject','codecommit:BatchGetPullRequests','codecommit:BatchGetRepositories','codecommit:CancelUploadArchive','codecommit:CreateBranch','codecommit:CreatePullRequest','codecommit:CreateRepository','codecommit:DeleteBranch','codecommit:DeleteCommentContent','codecommit:DeleteRepository','codecommit:DescribePullRequestEvents','codecommit:GetBlob','codecommit:GetBranch','codecommit:GetComment','codecommit:GetCommentsForComparedCommit','codecommit:GetCommentsForPullRequest','codecommit:GetCommit','codecommit:GetCommitHistory','codecommit:GetCommitsFromMergeBase','codecommit:GetDifferences','codecommit:GetMergeConflicts','codecommit:GetObjectIdentifier','codecommit:GetPullRequest','codecommit:GetReferences','codecommit:GetRepository','codecommit:GetRepositoryTriggers','codecommit:GetTree','codecommit:GetUploadArchiveStatus','codecommit:GitPull','codecommit:GitPush','codecommit:ListBranches','codecommit:ListPullRequests','codecommit:ListRepositories','codecommit:MergePullRequestByFastForward','codecommit:PostCommentForComparedCommit','codecommit:PostCommentForPullRequest','codecommit:PostCommentReply','codecommit:PutFile','codecommit:PutRepositoryTriggers','codecommit:TestRepositoryTriggers','codecommit:UpdateComment','codecommit:UpdateDefaultBranch','codecommit:UpdatePullRequestDescription','codecommit:UpdatePullRequestStatus','codecommit:UpdatePullRequestTitle','codecommit:UpdateRepositoryDescription','codecommit:UpdateRepositoryName','codecommit:UploadArchive','codedeploy:AddTagsToOnPremisesInstances','codedeploy:BatchGetApplicationRevisions','codedeploy:BatchGetApplications','codedeploy:BatchGetDeploymentGroups','codedeploy:BatchGetDeploymentInstances','codedeploy:BatchGetDeployments','codedeploy:BatchGetOnPremisesInstances','codedeploy:ContinueDeployment','codedeploy:CreateApplication','codedeploy:CreateDeployment','codedeploy:CreateDeploymentConfig','codedeploy:CreateDeploymentGroup','codedeploy:DeleteApplication','codedeploy:DeleteDeploymentConfig','codedeploy:DeleteDeploymentGroup','codedeploy:DeregisterOnPremisesInstance','codedeploy:GetApplication','codedeploy:GetApplicationRevision','codedeploy:GetDeployment','codedeploy:GetDeploymentConfig','codedeploy:GetDeploymentGroup','codedeploy:GetDeploymentInstance','codedeploy:GetOnPremisesInstance','codedeploy:ListApplicationRevisions','codedeploy:ListApplications','codedeploy:ListDeploymentConfigs','codedeploy:ListDeploymentGroups','codedeploy:ListDeploymentInstances','codedeploy:ListDeployments','codedeploy:ListOnPremisesInstances','codedeploy:RegisterApplicationRevision','codedeploy:RegisterOnPremisesInstance','codedeploy:RemoveTagsFromOnPremisesInstances','codedeploy:StopDeployment','codedeploy:UpdateApplication','codedeploy:UpdateDeploymentGroup','codepipeline:AcknowledgeJob','codepipeline:AcknowledgeThirdPartyJob','codepipeline:CreateCustomActionType','codepipeline:CreatePipeline','codepipeline:DeleteCustomActionType','codepipeline:DeletePipeline','codepipeline:DisableStageTransition','codepipeline:EnableStageTransition','codepipeline:GetJobDetails','codepipeline:GetPipeline','codepipeline:GetPipelineExecution','codepipeline:GetPipelineState','codepipeline:GetThirdPartyJobDetails','codepipeline:ListActionTypes','codepipeline:ListPipelineExecutions','codepipeline:ListPipelines','codepipeline:PollForJobs','codepipeline:PollForThirdPartyJobs','codepipeline:PutActionRevision','codepipeline:PutApprovalResult','codepipeline:PutJobFailureResult','codepipeline:PutJobSuccessResult','codepipeline:PutThirdPartyJobFailureResult','codepipeline:PutThirdPartyJobSuccessResult','codepipeline:RetryStageExecution','codepipeline:StartPipelineExecution','codepipeline:UpdatePipeline','codestar:AssociateTeamMember','codestar:CreateProject','codestar:CreateUserProfile','codestar:DeleteExtendedAccess','codestar:DeleteProject','codestar:DeleteUserProfile','codestar:DescribeProject','codestar:DescribeUserProfile','codestar:DisassociateTeamMember','codestar:GetExtendedAccess','codestar:ListProjects','codestar:ListResources','codestar:ListTeamMembers','codestar:ListUserProfiles','codestar:PutExtendedAccess','codestar:UpdateProject','codestar:UpdateTeamMember','codestar:UpdateUserProfile','codestar:VerifyServiceRole','cognito-identity:CreateIdentityPool','cognito-identity:DeleteIdentities','cognito-identity:DeleteIdentityPool','cognito-identity:DescribeIdentity','cognito-identity:DescribeIdentityPool','cognito-identity:GetCredentialsForIdentity','cognito-identity:GetId','cognito-identity:GetIdentityPoolRoles','cognito-identity:GetOpenIdToken','cognito-identity:GetOpenIdTokenForDeveloperIdentity','cognito-identity:ListIdentities','cognito-identity:ListIdentityPools','cognito-identity:LookupDeveloperIdentity','cognito-identity:MergeDeveloperIdentities','cognito-identity:SetIdentityPoolRoles','cognito-identity:UnlinkDeveloperIdentity','cognito-identity:UnlinkIdentity','cognito-identity:UpdateIdentityPool','cognito-idp:AddCustomAttributes','cognito-idp:AdminAddUserToGroup','cognito-idp:AdminConfirmSignUp','cognito-idp:AdminCreateUser','cognito-idp:AdminDeleteUser','cognito-idp:AdminDeleteUserAttributes','cognito-idp:AdminDisableUser','cognito-idp:AdminEnableUser','cognito-idp:AdminForgetDevice','cognito-idp:AdminGetDevice','cognito-idp:AdminGetUser','cognito-idp:AdminInitiateAuth','cognito-idp:AdminListDevices','cognito-idp:AdminListGroupsForUser','cognito-idp:AdminListUserAuthEvents','cognito-idp:AdminRemoveUserFromGroup','cognito-idp:AdminResetUserPassword','cognito-idp:AdminRespondToAuthChallenge','cognito-idp:AdminSetUserMFAPreference','cognito-idp:AdminSetUserSettings','cognito-idp:AdminUpdateAuthEventFeedback','cognito-idp:AdminUpdateDeviceStatus','cognito-idp:AdminUpdateUserAttributes','cognito-idp:AdminUserGlobalSignOut','cognito-idp:ChangePassword','cognito-idp:ConfirmDevice','cognito-idp:ConfirmForgotPassword','cognito-idp:ConfirmSignUp','cognito-idp:CreateGroup','cognito-idp:CreateUserImportJob','cognito-idp:CreateUserPool','cognito-idp:CreateUserPoolClient','cognito-idp:DeleteGroup','cognito-idp:DeleteUser','cognito-idp:DeleteUserAttributes','cognito-idp:DeleteUserPool','cognito-idp:DeleteUserPoolClient','cognito-idp:DescribeRiskConfiguration','cognito-idp:DescribeUserImportJob','cognito-idp:DescribeUserPool','cognito-idp:DescribeUserPoolClient','cognito-idp:ForgetDevice','cognito-idp:ForgotPassword','cognito-idp:GetCSVHeader','cognito-idp:GetDevice','cognito-idp:GetGroup','cognito-idp:GetUser','cognito-idp:GetUserAttributeVerificationCode','cognito-idp:GetUserPoolMfaConfig','cognito-idp:GlobalSignOut','cognito-idp:InitiateAuth','cognito-idp:ListDevices','cognito-idp:ListGroups','cognito-idp:ListUserImportJobs','cognito-idp:ListUserPoolClients','cognito-idp:ListUsersInGroup','cognito-idp:ResendConfirmationCode','cognito-idp:RespondToAuthChallenge','cognito-idp:SetRiskConfiguration','cognito-idp:SetUserMFAPreference','cognito-idp:SetUserPoolMfaConfig','cognito-idp:SetUserSettings','cognito-idp:SignUp','cognito-idp:StartUserImportJob','cognito-idp:StopUserImportJob','cognito-idp:UpdateAuthEventFeedback','cognito-idp:UpdateDeviceStatus','cognito-idp:UpdateGroup','cognito-idp:UpdateUserAttributes','cognito-idp:UpdateUserPool','cognito-idp:UpdateUserPoolClient','cognito-idp:VerifyUserAttribute','cognito-sync:BulkPublish','cognito-sync:DeleteDataset','cognito-sync:DescribeDataset','cognito-sync:DescribeIdentityPoolUsage','cognito-sync:DescribeIdentityUsage','cognito-sync:GetBulkPublishDetails','cognito-sync:GetCognitoEvents','cognito-sync:GetIdentityPoolConfiguration','cognito-sync:ListDatasets','cognito-sync:ListIdentityPoolUsage','cognito-sync:ListRecords','cognito-sync:QueryRecords','cognito-sync:RegisterDevice','cognito-sync:SetCognitoEvents','cognito-sync:SetDatasetConfiguration','cognito-sync:SetIdentityPoolConfiguration','cognito-sync:SubscribeToDataset','cognito-sync:UnsubscribeFromDataset','cognito-sync:UpdateRecords','comprehend:BatchDetectDominantLanguage','comprehend:BatchDetectEntities','comprehend:BatchDetectKeyPhrases','comprehend:BatchDetectSentiment','comprehend:DescribeTopicsDetectionJob','comprehend:DetectDominantLanguage','comprehend:DetectEntities','comprehend:DetectKeyPhrases','comprehend:DetectSentiment','comprehend:ListTopicsDetectionJobs','comprehend:StartTopicsDetectionJob','config:DeleteConfigRule','config:DeleteConfigurationRecorder','config:DeleteDeliveryChannel','config:DeleteEvaluationResults','config:DeliverConfigSnapshot','config:DescribeComplianceByConfigRule','config:DescribeComplianceByResource','config:DescribeConfigRuleEvaluationStatus','config:DescribeConfigRules','config:DescribeConfigurationRecorders','config:DescribeConfigurationRecorderStatus','config:DescribeDeliveryChannels','config:DescribeDeliveryChannelStatus','config:GetComplianceDetailsByConfigRule','config:GetComplianceDetailsByResource','config:GetComplianceSummaryByConfigRule','config:GetComplianceSummaryByResourceType','config:GetResourceConfigHistory','config:GetResources','config:GetTagKeys','config:ListDiscoveredResources','config:PutConfigRule','config:PutConfigurationRecorder','config:PutDeliveryChannel','config:PutEvaluations','config:StartConfigRulesEvaluation','config:StartConfigurationRecorder','config:StopConfigurationRecorder','connect:CreateInstance','connect:DescribeInstance','connect:DestroyInstance','connect:GetFederationToken','connect:GetFederationTokens','connect:ListInstances','connect:ModifyInstance','crowd:GetTask','crowd:PutTask','cur:DeleteReportDefinition','cur:DescribeReportDefinitions','cur:PutReportDefinition','datapipeline:ActivatePipeline','datapipeline:AddTags','datapipeline:CreatePipeline','datapipeline:DeactivatePipeline','datapipeline:DeletePipeline','datapipeline:DescribeObjects','datapipeline:DescribePipelines','datapipeline:EvaluateExpression','datapipeline:GetAccountLimits','datapipeline:GetPipelineDefinition','datapipeline:ListPipelines','datapipeline:PollForTask','datapipeline:PutAccountLimits','datapipeline:PutPipelineDefinition','datapipeline:QueryObjects','datapipeline:RemoveTags','datapipeline:ReportTaskProgress','datapipeline:ReportTaskRunnerHeartbeat','datapipeline:SetStatus','datapipeline:SetTaskStatus','datapipeline:ValidatePipelineDefinition','dax:BatchGetItem','dax:BatchWriteItem','dax:CreateCluster','dax:CreateParameterGroup','dax:CreateSubnetGroup','dax:DecreaseReplicationFactor','dax:DeleteCluster','dax:DeleteItem','dax:DeleteParameterGroup','dax:DeleteSubnetGroup','dax:DescribeClusters','dax:DescribeDefaultParameters','dax:DescribeEvents','dax:DescribeParameterGroups','dax:DescribeParameters','dax:DescribeSubnetGroups','dax:DescribeTable','dax:GetItem','dax:IncreaseReplicationFactor','dax:ListTables','dax:ListTags','dax:PutItem','dax:Query','dax:RebootNode','dax:Scan','dax:TagResource','dax:UntagResource','dax:UpdateCluster','dax:UpdateItem','dax:UpdateParameterGroup','dax:UpdateSubnetGroup','devicefarm:CreateDevicePool','devicefarm:CreateNetworkProfile','devicefarm:CreateProject','devicefarm:CreateRemoteAccessSession','devicefarm:CreateUpload','devicefarm:DeleteDevicePool','devicefarm:DeleteNetworkProfile','devicefarm:DeleteProject','devicefarm:DeleteRemoteAccessSession','devicefarm:DeleteRun','devicefarm:DeleteUpload','devicefarm:GetAccountSettings','devicefarm:GetDevice','devicefarm:GetDevicePool','devicefarm:GetDevicePoolCompatibility','devicefarm:GetJob','devicefarm:GetNetworkProfile','devicefarm:GetOfferingStatus','devicefarm:GetProject','devicefarm:GetRemoteAccessSession','devicefarm:GetRun','devicefarm:GetSuite','devicefarm:GetTest','devicefarm:GetUpload','devicefarm:InstallToRemoteAccessSession','devicefarm:ListArtifacts','devicefarm:ListDevicePools','devicefarm:ListDevices','devicefarm:ListJobs','devicefarm:ListNetworkProfiles','devicefarm:ListOfferings','devicefarm:ListOfferingTransactions','devicefarm:ListProjects','devicefarm:ListRemoteAccessSessions','devicefarm:ListRuns','devicefarm:ListSamples','devicefarm:ListSuites','devicefarm:ListTests','devicefarm:ListUniqueProblems','devicefarm:ListUploads','devicefarm:PurchaseOffering','devicefarm:RenewOffering','devicefarm:ScheduleRun','devicefarm:StopRemoteAccessSession','devicefarm:StopRun','devicefarm:UpdateDevicePool','devicefarm:UpdateNetworkProfile','devicefarm:UpdateProject','directconnect:AllocateConnectionOnInterconnect','directconnect:AllocatePrivateVirtualInterface','directconnect:AllocatePublicVirtualInterface','directconnect:ConfirmConnection','directconnect:ConfirmPrivateVirtualInterface','directconnect:ConfirmPublicVirtualInterface','directconnect:CreateConnection','directconnect:CreateInterconnect','directconnect:CreatePrivateVirtualInterface','directconnect:CreatePublicVirtualInterface','directconnect:DeleteConnection','directconnect:DeleteInterconnect','directconnect:DeleteVirtualInterface','directconnect:DescribeConnectionLoa','directconnect:DescribeConnections','directconnect:DescribeConnectionsOnInterconnect','directconnect:DescribeInterconnectLoa','directconnect:DescribeInterconnects','directconnect:DescribeLocations','directconnect:DescribeVirtualGateways','directconnect:DescribeVirtualInterfaces','discovery:AssociateConfigurationItemsToApplication','discovery:CreateApplication','discovery:CreateTags','discovery:DeleteApplications','discovery:DeleteTags','discovery:DescribeAgents','discovery:DescribeConfigurations','discovery:DescribeExportConfigurations','discovery:DescribeTags','discovery:DisassociateConfigurationItemsFromApplication','discovery:ExportConfigurations','discovery:GetDiscoverySummary','discovery:ListConfigurations','discovery:ListServerNeighbors','discovery:StartDataCollectionByAgentIds','discovery:StartExportTask','discovery:StopDataCollectionByAgentIds','discovery:UpdateApplication','dms:AddTagsToResource','dms:CreateEndpoint','dms:CreateReplicationInstance','dms:CreateReplicationSubnetGroup','dms:CreateReplicationTask','dms:DeleteEndpoint','dms:DeleteEventSubscription','dms:DeleteReplicationInstance','dms:DeleteReplicationSubnetGroup','dms:DeleteReplicationTask','dms:DescribeAccountAttributes','dms:DescribeCertificates','dms:DescribeConnections','dms:DescribeEndpoints','dms:DescribeEndpointTypes','dms:DescribeEventCategories','dms:DescribeEvents','dms:DescribeEventSubscriptions','dms:DescribeOrderableReplicationInstances','dms:DescribeRefreshSchemasStatus','dms:DescribeReplicationInstances','dms:DescribeReplicationSubnetGroups','dms:DescribeReplicationTasks','dms:DescribeSchemas','dms:DescribeTableStatistics','dms:ListTagsForResource','dms:ModifyEndpoint','dms:ModifyEventSubscription','dms:ModifyReplicationInstance','dms:ModifyReplicationSubnetGroup','dms:ModifyReplicationTask','dms:RefreshSchemas','dms:RemoveTagsFromResource','dms:StartReplicationTask','dms:StopReplicationTask','dms:TestConnection','ds:AddIpRoutes','ds:AddTagsToResource','ds:AuthorizeApplication','ds:CancelSchemaExtension','ds:ConnectDirectory','ds:CreateAlias','ds:CreateComputer','ds:CreateConditionalForwarder','ds:CreateDirectory','ds:CreateMicrosoftAD','ds:CreateSnapshot','ds:CreateTrust','ds:DeleteConditionalForwarder','ds:DeleteDirectory','ds:DeleteSnapshot','ds:DeleteTrust','ds:DeregisterEventTopic','ds:DescribeConditionalForwarders','ds:DescribeDirectories','ds:DescribeEventTopics','ds:DescribeSnapshots','ds:DescribeTrusts','ds:DisableRadius','ds:DisableSso','ds:EnableRadius','ds:EnableSso','ds:GetDirectoryLimits','ds:GetSnapshotLimits','ds:ListAuthorizedApplications','ds:ListIpRoutes','ds:ListSchemaExtensions','ds:ListTagsForResource','ds:RegisterEventTopic','ds:RemoveIpRoutes','ds:RemoveTagsFromResource','ds:RestoreFromSnapshot','ds:StartSchemaExtension','ds:UnauthorizeApplication','ds:UpdateConditionalForwarder','ds:UpdateRadius','ds:VerifyTrust','dynamodb:BatchGetItem','dynamodb:BatchWriteItem','dynamodb:CreateBackup','dynamodb:CreateTable','dynamodb:DeleteBackup','dynamodb:DeleteItem','dynamodb:DeleteTable','dynamodb:DescribeBackup','dynamodb:DescribeContinuousBackups','dynamodb:DescribeLimits','dynamodb:DescribeReservedCapacity','dynamodb:DescribeReservedCapacityOfferings','dynamodb:DescribeStream','dynamodb:DescribeTable','dynamodb:DescribeTimeToLive','dynamodb:GetItem','dynamodb:GetRecords','dynamodb:GetShardIterator','dynamodb:ListBackups','dynamodb:ListStreams','dynamodb:ListTables','dynamodb:ListTagsOfResource','dynamodb:PurchaseReservedCapacityOfferings','dynamodb:PutItem','dynamodb:Query','dynamodb:RestoreTableFromBackup','dynamodb:RestoreTableToPointInTime','dynamodb:Scan','dynamodb:TagResource','dynamodb:UntagResource','dynamodb:UpdateContinuousBackups','dynamodb:UpdateItem','dynamodb:UpdateTable','dynamodb:UpdateTimeToLive','ec2:AcceptReservedInstancesExchangeQuote','ec2:AcceptVpcEndpointConnections','ec2:AcceptVpcPeeringConnection','ec2:AllocateAddress','ec2:AllocateHosts','ec2:AssignIpv6Addresses','ec2:AssignPrivateIpAddresses','ec2:AssociateAddress','ec2:AssociateDhcpOptions','ec2:AssociateIamInstanceProfile','ec2:AssociateRouteTable','ec2:AssociateSubnetCidrBlock','ec2:AssociateVpcCidrBlock','ec2:AttachClassicLinkVpc','ec2:AttachInternetGateway','ec2:AttachNetworkInterface','ec2:AttachVolume','ec2:AttachVpnGateway','ec2:AuthorizeSecurityGroupEgress','ec2:AuthorizeSecurityGroupIngress','ec2:BundleInstance','ec2:CancelBundleTask','ec2:CancelConversionTask','ec2:CancelExportTask','ec2:CancelImportTask','ec2:CancelReservedInstancesListing','ec2:CancelSpotFleetRequests','ec2:CancelSpotInstanceRequests','ec2:ConfirmProductInstance','ec2:CopyFpgaImage','ec2:CopyImage','ec2:CopySnapshot','ec2:CreateCustomerGateway','ec2:CreateDefaultSubnet','ec2:CreateDefaultVpc','ec2:CreateDhcpOptions','ec2:CreateEgressOnlyInternetGateway','ec2:CreateFlowLogs','ec2:CreateFpgaImage','ec2:CreateImage','ec2:CreateInstanceExportTask','ec2:CreateInternetGateway','ec2:CreateKeyPair','ec2:CreateLaunchTemplate','ec2:CreateLaunchTemplateVersion','ec2:CreateNatGateway','ec2:CreateNetworkAcl','ec2:CreateNetworkAclEntry','ec2:CreateNetworkInterface','ec2:CreateNetworkInterfacePermission','ec2:CreatePlacementGroup','ec2:CreateReservedInstancesListing','ec2:CreateRoute','ec2:CreateRouteTable','ec2:CreateSecurityGroup','ec2:CreateSnapshot','ec2:CreateSpotDatafeedSubscription','ec2:CreateSubnet','ec2:CreateTags','ec2:CreateVolume','ec2:CreateVpc','ec2:CreateVpcEndpoint','ec2:CreateVpcEndpointConnectionNotification','ec2:CreateVpcEndpointServiceConfiguration','ec2:CreateVpcPeeringConnection','ec2:CreateVpnConnection','ec2:CreateVpnConnectionRoute','ec2:CreateVpnGateway','ec2:DeleteCustomerGateway','ec2:DeleteDhcpOptions','ec2:DeleteEgressOnlyInternetGateway','ec2:DeleteFlowLogs','ec2:DeleteFpgaImage','ec2:DeleteInternetGateway','ec2:DeleteKeyPair','ec2:DeleteLaunchTemplate','ec2:DeleteLaunchTemplateVersions','ec2:DeleteNatGateway','ec2:DeleteNetworkAcl','ec2:DeleteNetworkAclEntry','ec2:DeleteNetworkInterface','ec2:DeleteNetworkInterfacePermission','ec2:DeletePlacementGroup','ec2:DeleteRoute','ec2:DeleteRouteTable','ec2:DeleteSecurityGroup','ec2:DeleteSnapshot','ec2:DeleteSpotDatafeedSubscription','ec2:DeleteSubnet','ec2:DeleteTags','ec2:DeleteVolume','ec2:DeleteVpc','ec2:DeleteVpcEndpointConnectionNotifications','ec2:DeleteVpcEndpoints','ec2:DeleteVpcEndpointServiceConfigurations','ec2:DeleteVpcPeeringConnection','ec2:DeleteVpnConnection','ec2:DeleteVpnConnectionRoute','ec2:DeleteVpnGateway','ec2:DeregisterImage','ec2:DescribeAccountAttributes','ec2:DescribeAddresses','ec2:DescribeAvailabilityZones','ec2:DescribeBundleTasks','ec2:DescribeClassicLinkInstances','ec2:DescribeConversionTasks','ec2:DescribeCustomerGateways','ec2:DescribeDhcpOptions','ec2:DescribeEgressOnlyInternetGateways','ec2:DescribeElasticGpus','ec2:DescribeExportTasks','ec2:DescribeFlowLogs','ec2:DescribeFpgaImageAttribute','ec2:DescribeFpgaImages','ec2:DescribeHostReservationOfferings','ec2:DescribeHostReservations','ec2:DescribeHosts','ec2:DescribeIamInstanceProfileAssociations','ec2:DescribeIdentityIdFormat','ec2:DescribeIdFormat','ec2:DescribeImageAttribute','ec2:DescribeImages','ec2:DescribeImportImageTasks','ec2:DescribeImportSnapshotTasks','ec2:DescribeInstanceAttribute','ec2:DescribeInstanceCreditSpecifications','ec2:DescribeInstances','ec2:DescribeInstanceStatus','ec2:DescribeInternetGateways','ec2:DescribeKeyPairs','ec2:DescribeLaunchTemplates','ec2:DescribeLaunchTemplateVersions','ec2:DescribeMovingAddresses','ec2:DescribeNatGateways','ec2:DescribeNetworkAcls','ec2:DescribeNetworkInterfaceAttribute','ec2:DescribeNetworkInterfacePermissions','ec2:DescribeNetworkInterfaces','ec2:DescribePlacementGroups','ec2:DescribePrefixLists','ec2:DescribeRegions','ec2:DescribeReservedInstances','ec2:DescribeReservedInstancesListings','ec2:DescribeReservedInstancesModifications','ec2:DescribeReservedInstancesOfferings','ec2:DescribeRouteTables','ec2:DescribeScheduledInstanceAvailability','ec2:DescribeScheduledInstances','ec2:DescribeSecurityGroupReferences','ec2:DescribeSecurityGroups','ec2:DescribeSnapshotAttribute','ec2:DescribeSnapshots','ec2:DescribeSpotDatafeedSubscription','ec2:DescribeSpotFleetInstances','ec2:DescribeSpotFleetRequestHistory','ec2:DescribeSpotFleetRequests','ec2:DescribeSpotInstanceRequests','ec2:DescribeSpotPriceHistory','ec2:DescribeStaleSecurityGroups','ec2:DescribeSubnets','ec2:DescribeTags','ec2:DescribeVolumeAttribute','ec2:DescribeVolumes','ec2:DescribeVolumesModifications','ec2:DescribeVolumeStatus','ec2:DescribeVpcAttribute','ec2:DescribeVpcClassicLink','ec2:DescribeVpcClassicLinkDnsSupport','ec2:DescribeVpcEndpointConnectionNotifications','ec2:DescribeVpcEndpointConnections','ec2:DescribeVpcEndpoints','ec2:DescribeVpcEndpointServiceConfigurations','ec2:DescribeVpcEndpointServicePermissions','ec2:DescribeVpcEndpointServices','ec2:DescribeVpcPeeringConnections','ec2:DescribeVpcs','ec2:DescribeVpnConnections','ec2:DescribeVpnGateways','ec2:DetachClassicLinkVpc','ec2:DetachInternetGateway','ec2:DetachNetworkInterface','ec2:DetachVolume','ec2:DetachVpnGateway','ec2:DisableVgwRoutePropagation','ec2:DisableVpcClassicLink','ec2:DisableVpcClassicLinkDnsSupport','ec2:DisassociateAddress','ec2:DisassociateIamInstanceProfile','ec2:DisassociateRouteTable','ec2:DisassociateSubnetCidrBlock','ec2:DisassociateVpcCidrBlock','ec2:EnableVgwRoutePropagation','ec2:EnableVolumeIO','ec2:EnableVpcClassicLink','ec2:EnableVpcClassicLinkDnsSupport','ec2:GetConsoleOutput','ec2:GetConsoleScreenshot','ec2:GetHostReservationPurchasePreview','ec2:GetLaunchTemplateData','ec2:GetPasswordData','ec2:GetReservedInstancesExchangeQuote','ec2:ImportImage','ec2:ImportInstance','ec2:ImportKeyPair','ec2:ImportSnapshot','ec2:ImportVolume','ec2:ModifyFpgaImageAttribute','ec2:ModifyHosts','ec2:ModifyIdentityIdFormat','ec2:ModifyIdFormat','ec2:ModifyImageAttribute','ec2:ModifyInstanceAttribute','ec2:ModifyInstanceCreditSpecification','ec2:ModifyInstancePlacement','ec2:ModifyLaunchTemplate','ec2:ModifyNetworkInterfaceAttribute','ec2:ModifyReservedInstances','ec2:ModifySnapshotAttribute','ec2:ModifySpotFleetRequest','ec2:ModifySubnetAttribute','ec2:ModifyVolume','ec2:ModifyVolumeAttribute','ec2:ModifyVpcAttribute','ec2:ModifyVpcEndpoint','ec2:ModifyVpcEndpointConnectionNotification','ec2:ModifyVpcEndpointServiceConfiguration','ec2:ModifyVpcEndpointServicePermissions','ec2:ModifyVpcPeeringConnectionOptions','ec2:ModifyVpcTenancy','ec2:MonitorInstances','ec2:MoveAddressToVpc','ec2:PurchaseHostReservation','ec2:PurchaseReservedInstancesOffering','ec2:PurchaseScheduledInstances','ec2:RebootInstances','ec2:RegisterImage','ec2:RejectVpcEndpointConnections','ec2:RejectVpcPeeringConnection','ec2:ReleaseAddress','ec2:ReleaseHosts','ec2:ReplaceIamInstanceProfileAssociation','ec2:ReplaceNetworkAclAssociation','ec2:ReplaceNetworkAclEntry','ec2:ReplaceRoute','ec2:ReplaceRouteTableAssociation','ec2:ReportInstanceStatus','ec2:RequestSpotFleet','ec2:RequestSpotInstances','ec2:ResetFpgaImageAttribute','ec2:ResetImageAttribute','ec2:ResetInstanceAttribute','ec2:ResetNetworkInterfaceAttribute','ec2:ResetSnapshotAttribute','ec2:RestoreAddressToClassic','ec2:RevokeSecurityGroupEgress','ec2:RevokeSecurityGroupIngress','ec2:RunInstances','ec2:RunScheduledInstances','ec2:StartInstances','ec2:StopInstances','ec2:TerminateInstances','ec2:UnassignIpv6Addresses','ec2:UnassignPrivateIpAddresses','ec2:UnmonitorInstances','ec2:UpdateSecurityGroupRuleDescriptionsEgress','ec2:UpdateSecurityGroupRuleDescriptionsIngress','ec2messages:AcknowledgeMessage','ec2messages:DeleteMessage','ec2messages:FailMessage','ec2messages:GetEndpoint','ec2messages:GetMessages','ec2messages:SendReply','ecr:BatchCheckLayerAvailability','ecr:BatchDeleteImage','ecr:BatchGetImage','ecr:CompleteLayerUpload','ecr:CreateRepository','ecr:DeleteRepository','ecr:DeleteRepositoryPolicy','ecr:DescribeImages','ecr:DescribeRepositories','ecr:GetAuthorizationToken','ecr:GetDownloadUrlForLayer','ecr:GetRepositoryPolicy','ecr:InitiateLayerUpload','ecr:ListImages','ecr:PutImage','ecr:SetRepositoryPolicy','ecr:UploadLayerPart','ecs:CreateCluster','ecs:CreateService','ecs:DeleteCluster','ecs:DeleteService','ecs:DeregisterContainerInstance','ecs:DeregisterTaskDefinition','ecs:DescribeClusters','ecs:DescribeContainerInstances','ecs:DescribeServices','ecs:DescribeTaskDefinition','ecs:DescribeTasks','ecs:DiscoverPollEndpoint','ecs:ListClusters','ecs:ListContainerInstances','ecs:ListServices','ecs:ListTaskDefinitionFamilies','ecs:ListTaskDefinitions','ecs:ListTasks','ecs:Poll','ecs:RegisterContainerInstance','ecs:RegisterTaskDefinition','ecs:RunTask','ecs:StartTask','ecs:StartTelemetrySession','ecs:StopTask','ecs:SubmitContainerStateChange','ecs:SubmitTaskStateChange','ecs:UpdateContainerAgent','ecs:UpdateContainerInstancesState','ecs:UpdateService','elasticache:AddTagsToResource','elasticache:AuthorizeCacheSecurityGroupIngress','elasticache:CopySnapshot','elasticache:CreateCacheCluster','elasticache:CreateCacheParameterGroup','elasticache:CreateCacheSecurityGroup','elasticache:CreateCacheSubnetGroup','elasticache:CreateReplicationGroup','elasticache:CreateSnapshot','elasticache:DeleteCacheCluster','elasticache:DeleteCacheParameterGroup','elasticache:DeleteCacheSecurityGroup','elasticache:DeleteCacheSubnetGroup','elasticache:DeleteReplicationGroup','elasticache:DeleteSnapshot','elasticache:DescribeCacheClusters','elasticache:DescribeCacheEngineVersions','elasticache:DescribeCacheParameterGroups','elasticache:DescribeCacheParameters','elasticache:DescribeCacheSecurityGroups','elasticache:DescribeCacheSubnetGroups','elasticache:DescribeEngineDefaultParameters','elasticache:DescribeEvents','elasticache:DescribeReplicationGroups','elasticache:DescribeReservedCacheNodes','elasticache:DescribeReservedCacheNodesOfferings','elasticache:DescribeSnapshots','elasticache:ListAllowedNodeTypeModifications','elasticache:ListTagsForResource','elasticache:ModifyCacheCluster','elasticache:ModifyCacheParameterGroup','elasticache:ModifyCacheSubnetGroup','elasticache:ModifyReplicationGroup','elasticache:PurchaseReservedCacheNodesOffering','elasticache:RebootCacheCluster','elasticache:RemoveTagsFromResource','elasticache:ResetCacheParameterGroup','elasticache:RevokeCacheSecurityGroupIngress','elasticbeanstalk:AbortEnvironmentUpdate','elasticbeanstalk:ApplyEnvironmentManagedAction','elasticbeanstalk:CheckDNSAvailability','elasticbeanstalk:ComposeEnvironments','elasticbeanstalk:CreateApplication','elasticbeanstalk:CreateApplicationVersion','elasticbeanstalk:CreateConfigurationTemplate','elasticbeanstalk:CreateEnvironment','elasticbeanstalk:CreatePlatformVersion','elasticbeanstalk:CreateStorageLocation','elasticbeanstalk:DeleteApplication','elasticbeanstalk:DeleteApplicationVersion','elasticbeanstalk:DeleteConfigurationTemplate','elasticbeanstalk:DeleteEnvironmentConfiguration','elasticbeanstalk:DeletePlatformVersion','elasticbeanstalk:DescribeApplications','elasticbeanstalk:DescribeApplicationVersions','elasticbeanstalk:DescribeConfigurationOptions','elasticbeanstalk:DescribeConfigurationSettings','elasticbeanstalk:DescribeEnvironmentHealth','elasticbeanstalk:DescribeEnvironmentManagedActionHistory','elasticbeanstalk:DescribeEnvironmentManagedActions','elasticbeanstalk:DescribeEnvironmentResources','elasticbeanstalk:DescribeEnvironments','elasticbeanstalk:DescribeEvents','elasticbeanstalk:DescribeInstancesHealth','elasticbeanstalk:DescribePlatformVersion','elasticbeanstalk:ListAvailableSolutionStacks','elasticbeanstalk:ListPlatformVersions','elasticbeanstalk:RebuildEnvironment','elasticbeanstalk:RequestEnvironmentInfo','elasticbeanstalk:RestartAppServer','elasticbeanstalk:RetrieveEnvironmentInfo','elasticbeanstalk:SwapEnvironmentCNAMEs','elasticbeanstalk:TerminateEnvironment','elasticbeanstalk:UpdateApplication','elasticbeanstalk:UpdateApplicationResourceLifecycle','elasticbeanstalk:UpdateApplicationVersion','elasticbeanstalk:UpdateConfigurationTemplate','elasticbeanstalk:UpdateEnvironment','elasticbeanstalk:ValidateConfigurationSettings','elasticfilesystem:CreateFileSystem','elasticfilesystem:CreateMountTarget','elasticfilesystem:CreateTags','elasticfilesystem:DeleteFileSystem','elasticfilesystem:DeleteMountTarget','elasticfilesystem:DeleteTags','elasticfilesystem:DescribeFileSystems','elasticfilesystem:DescribeMountTargets','elasticfilesystem:DescribeMountTargetSecurityGroups','elasticfilesystem:DescribeTags','elasticfilesystem:ModifyMountTargetSecurityGroups','elasticloadbalancing:AddTags','elasticloadbalancing:ApplySecurityGroupsToLoadBalancer','elasticloadbalancing:AttachLoadBalancerToSubnets','elasticloadbalancing:ConfigureHealthCheck','elasticloadbalancing:CreateAppCookieStickinessPolicy','elasticloadbalancing:CreateLBCookieStickinessPolicy','elasticloadbalancing:CreateListener','elasticloadbalancing:CreateLoadBalancer','elasticloadbalancing:CreateLoadBalancerListeners','elasticloadbalancing:CreateLoadBalancerPolicy','elasticloadbalancing:CreateRule','elasticloadbalancing:CreateTargetGroup','elasticloadbalancing:DeleteListener','elasticloadbalancing:DeleteLoadBalancer','elasticloadbalancing:DeleteLoadBalancerListeners','elasticloadbalancing:DeleteLoadBalancerPolicy','elasticloadbalancing:DeleteRule','elasticloadbalancing:DeleteTargetGroup','elasticloadbalancing:DeregisterInstancesFromLoadBalancer','elasticloadbalancing:DeregisterTargets','elasticloadbalancing:DescribeInstanceHealth','elasticloadbalancing:DescribeListeners','elasticloadbalancing:DescribeLoadBalancerAttributes','elasticloadbalancing:DescribeLoadBalancerPolicies','elasticloadbalancing:DescribeLoadBalancerPolicyTypes','elasticloadbalancing:DescribeLoadBalancers','elasticloadbalancing:DescribeRules','elasticloadbalancing:DescribeSSLPolicies','elasticloadbalancing:DescribeTags','elasticloadbalancing:DescribeTargetGroupAttributes','elasticloadbalancing:DescribeTargetGroups','elasticloadbalancing:DescribeTargetHealth','elasticloadbalancing:DetachLoadBalancerFromSubnets','elasticloadbalancing:DisableAvailabilityZonesForLoadBalancer','elasticloadbalancing:EnableAvailabilityZonesForLoadBalancer','elasticloadbalancing:ModifyListener','elasticloadbalancing:ModifyLoadBalancerAttributes','elasticloadbalancing:ModifyRule','elasticloadbalancing:ModifyTargetGroup','elasticloadbalancing:ModifyTargetGroupAttributes','elasticloadbalancing:RegisterInstancesWithLoadBalancer','elasticloadbalancing:RegisterTargets','elasticloadbalancing:RemoveTags','elasticloadbalancing:SetIpAddressType','elasticloadbalancing:SetLoadBalancerListenerSSLCertificate','elasticloadbalancing:SetLoadBalancerPoliciesForBackendServer','elasticloadbalancing:SetLoadBalancerPoliciesOfListener','elasticloadbalancing:SetRulePriorities','elasticloadbalancing:SetSecurityGroups','elasticloadbalancing:SetSubnets','elasticmapreduce:AddInstanceGroups','elasticmapreduce:AddJobFlowSteps','elasticmapreduce:AddTags','elasticmapreduce:CancelSteps','elasticmapreduce:CreateSecurityConfiguration','elasticmapreduce:DeleteSecurityConfiguration','elasticmapreduce:DescribeCluster','elasticmapreduce:DescribeJobFlows','elasticmapreduce:DescribeSecurityConfiguration','elasticmapreduce:DescribeStep','elasticmapreduce:ListBootstrapActions','elasticmapreduce:ListClusters','elasticmapreduce:ListInstanceGroups','elasticmapreduce:ListInstances','elasticmapreduce:ListSecurityConfigurations','elasticmapreduce:ListSteps','elasticmapreduce:ModifyInstanceGroups','elasticmapreduce:PutAutoScalingPolicy','elasticmapreduce:RemoveAutoScalingPolicy','elasticmapreduce:RemoveTags','elasticmapreduce:RunJobFlow','elasticmapreduce:SetTerminationProtection','elasticmapreduce:SetVisibleToAllUsers','elasticmapreduce:TerminateJobFlows','elasticmapreduce:ViewEventsFromAllClustersInConsole','elastictranscoder:CancelJob','elastictranscoder:CreateJob','elastictranscoder:CreatePipeline','elastictranscoder:CreatePreset','elastictranscoder:DeletePipeline','elastictranscoder:DeletePreset','elastictranscoder:ListJobsByPipeline','elastictranscoder:ListJobsByStatus','elastictranscoder:ListPipelines','elastictranscoder:ListPresets','elastictranscoder:ReadJob','elastictranscoder:ReadPipeline','elastictranscoder:ReadPreset','elastictranscoder:TestRole','elastictranscoder:UpdatePipeline','elastictranscoder:UpdatePipelineNotifications','elastictranscoder:UpdatePipelineStatus','es:AddTags','es:CreateElasticsearchDomain','es:DeleteElasticsearchDomain','es:DescribeElasticsearchDomain','es:DescribeElasticsearchDomainConfig','es:DescribeElasticsearchDomains','es:ESHttpDelete','es:ESHttpGet','es:ESHttpHead','es:ESHttpPost','es:ESHttpPut','es:ListDomainNames','es:ListTags','es:RemoveTags','es:UpdateElasticsearchDomainConfig','events:DeleteRule','events:DescribeRule','events:DisableRule','events:EnableRule','events:ListRuleNamesByTarget','events:ListRules','events:ListTargetsByRule','events:PutEvents','events:PutRule','events:PutTargets','events:RemoveTargets','events:TestEventPattern','execute-api:InvalidateCache','execute-api:Invoke','firehose:CreateDeliveryStream','firehose:DeleteDeliveryStream','firehose:DescribeDeliveryStream','firehose:ListDeliveryStreams','firehose:PutRecord','firehose:PutRecordBatch','firehose:UpdateDestination','freertos:CreateSoftwareConfiguration','freertos:DeleteSoftwareConfiguration','freertos:DescribeHardwarePlatform','freertos:DescribeSoftwareConfiguration','freertos:GetSoftwareURL','freertos:GetSoftwareURLForConfiguration','freertos:ListFreeRTOSVersions','freertos:ListHardwarePlatforms','freertos:ListHardwareVendors','freertos:ListSoftwareConfigurations','freertos:UpdateSoftwareConfiguration','gamelift:CreateAlias','gamelift:CreateBuild','gamelift:CreateFleet','gamelift:CreateGameSession','gamelift:CreatePlayerSession','gamelift:CreatePlayerSessions','gamelift:DeleteAlias','gamelift:DeleteBuild','gamelift:DeleteFleet','gamelift:DeleteScalingPolicy','gamelift:DescribeAlias','gamelift:DescribeBuild','gamelift:DescribeEC2InstanceLimits','gamelift:DescribeFleetAttributes','gamelift:DescribeFleetCapacity','gamelift:DescribeFleetEvents','gamelift:DescribeFleetPortSettings','gamelift:DescribeFleetUtilization','gamelift:DescribeGameSessionDetails','gamelift:DescribeGameSessions','gamelift:DescribeInstances','gamelift:DescribePlayerSessions','gamelift:DescribeRuntimeConfiguration','gamelift:DescribeScalingPolicies','gamelift:GetGameSessionLogUrl','gamelift:GetInstanceAccess','gamelift:ListAliases','gamelift:ListBuilds','gamelift:ListFleets','gamelift:PutScalingPolicy','gamelift:RequestUploadCredentials','gamelift:ResolveAlias','gamelift:SearchGameSessions','gamelift:UpdateAlias','gamelift:UpdateBuild','gamelift:UpdateFleetAttributes','gamelift:UpdateFleetCapacity','gamelift:UpdateFleetPortSettings','gamelift:UpdateGameSession','gamelift:UpdateRuntimeConfiguration','glacier:AbortMultipartUpload','glacier:AbortVaultLock','glacier:AddTagsToVault','glacier:CompleteMultipartUpload','glacier:CompleteVaultLock','glacier:CreateVault','glacier:DeleteArchive','glacier:DeleteVault','glacier:DeleteVaultAccessPolicy','glacier:DeleteVaultNotifications','glacier:DescribeJob','glacier:DescribeVault','glacier:GetDataRetrievalPolicy','glacier:GetJobOutput','glacier:GetVaultAccessPolicy','glacier:GetVaultLock','glacier:GetVaultNotifications','glacier:InitiateJob','glacier:InitiateMultipartUpload','glacier:InitiateVaultLock','glacier:ListJobs','glacier:ListMultipartUploads','glacier:ListParts','glacier:ListProvisionedCapacity','glacier:ListTagsForVault','glacier:ListVaults','glacier:PurchaseProvisionedCapacity','glacier:RemoveTagsFromVault','glacier:SetDataRetrievalPolicy','glacier:SetVaultAccessPolicy','glacier:SetVaultNotifications','glacier:UploadArchive','glacier:UploadMultipartPart','glue:BatchCreatePartition','glue:BatchDeleteConnection','glue:BatchDeletePartition','glue:BatchDeleteTable','glue:BatchGetPartition','glue:CreateClassifier','glue:CreateConnection','glue:CreateCrawler','glue:CreateDatabase','glue:CreateDevEndpoint','glue:CreateJob','glue:CreatePartition','glue:CreateScript','glue:CreateTable','glue:CreateTrigger','glue:CreateUserDefinedFunction','glue:DeleteClassifier','glue:DeleteConnection','glue:DeleteCrawler','glue:DeleteDatabase','glue:DeleteDevEndpoint','glue:DeleteJob','glue:DeletePartition','glue:DeleteTable','glue:DeleteTrigger','glue:DeleteUserDefinedFunction','glue:GetCatalogImportStatus','glue:GetClassifier','glue:GetClassifiers','glue:GetConnection','glue:GetConnections','glue:GetCrawler','glue:GetCrawlerMetrics','glue:GetCrawlers','glue:GetDatabase','glue:GetDatabases','glue:GetDataflowGraph','glue:GetDevEndpoint','glue:GetDevEndpoints','glue:GetJob','glue:GetJobRun','glue:GetJobRuns','glue:GetJobs','glue:GetMapping','glue:GetPartition','glue:GetPartitions','glue:GetPlan','glue:GetTable','glue:GetTables','glue:GetTableVersions','glue:GetTrigger','glue:GetTriggers','glue:GetUserDefinedFunction','glue:GetUserDefinedFunctions','glue:ImportCatalogToGlue','glue:ResetJobBookmark','glue:StartCrawler','glue:StartCrawlerSchedule','glue:StartJobRun','glue:StartTrigger','glue:StopCrawler','glue:StopCrawlerSchedule','glue:StopTrigger','glue:UpdateClassifier','glue:UpdateConnection','glue:UpdateCrawler','glue:UpdateDatabase','glue:UpdateDevEndpoint','glue:UpdateJob','glue:UpdatePartition','glue:UpdateTable','glue:UpdateTrigger','glue:UpdateUserDefinedFunction','greengrass:AssociateRoleToGroup','greengrass:AssociateServiceRoleToAccount','greengrass:CreateCoreDefinition','greengrass:CreateCoreDefinitionVersion','greengrass:CreateDeployment','greengrass:CreateDeviceDefinition','greengrass:CreateDeviceDefinitionVersion','greengrass:CreateFunctionDefinition','greengrass:CreateFunctionDefinitionVersion','greengrass:CreateGroup','greengrass:CreateGroupCertificateAuthority','greengrass:CreateGroupVersion','greengrass:CreateLoggerDefinition','greengrass:CreateLoggerDefinitionVersion','greengrass:CreateResourceDefinition','greengrass:CreateResourceDefinitionVersion','greengrass:CreateSoftwareUpdateJob','greengrass:CreateSubscriptionDefinition','greengrass:CreateSubscriptionDefinitionVersion','greengrass:DeleteCoreDefinition','greengrass:DeleteDeviceDefinition','greengrass:DeleteFunctionDefinition','greengrass:DeleteGroup','greengrass:DeleteLoggerDefinition','greengrass:DeleteResourceDefinition','greengrass:DeleteSubscriptionDefinition','greengrass:DisassociateRoleFromGroup','greengrass:DisassociateServiceRoleFromAccount','greengrass:GetAssociatedRole','greengrass:GetConnectivityInfo','greengrass:GetCoreDefinition','greengrass:GetCoreDefinitionVersion','greengrass:GetDeploymentStatus','greengrass:GetDeviceDefinition','greengrass:GetDeviceDefinitionVersion','greengrass:GetFunctionDefinition','greengrass:GetFunctionDefinitionVersion','greengrass:GetGroup','greengrass:GetGroupCertificateAuthority','greengrass:GetGroupCertificateConfiguration','greengrass:GetGroupVersion','greengrass:GetLoggerDefinition','greengrass:GetLoggerDefinitionVersion','greengrass:GetResourceDefinition','greengrass:GetResourceDefinitionVersion','greengrass:GetServiceRoleForAccount','greengrass:GetSubscriptionDefinition','greengrass:GetSubscriptionDefinitionVersion','greengrass:ListCoreDefinitions','greengrass:ListCoreDefinitionVersions','greengrass:ListDeployments','greengrass:ListDeviceDefinitions','greengrass:ListDeviceDefinitionVersions','greengrass:ListFunctionDefinitions','greengrass:ListFunctionDefinitionVersions','greengrass:ListGroupCertificateAuthorities','greengrass:ListGroups','greengrass:ListGroupVersions','greengrass:ListLoggerDefinitions','greengrass:ListLoggerDefinitionVersions','greengrass:ListResourceDefinitions','greengrass:ListResourceDefinitionVersions','greengrass:ListSubscriptionDefinitions','greengrass:ListSubscriptionDefinitionVersions','greengrass:ResetDeployments','greengrass:UpdateConnectivityInfo','greengrass:UpdateCoreDefinition','greengrass:UpdateDeviceDefinition','greengrass:UpdateFunctionDefinition','greengrass:UpdateGroup','greengrass:UpdateGroupCertificateConfiguration','greengrass:UpdateLoggerDefinition','greengrass:UpdateResourceDefinition','greengrass:UpdateSubscriptionDefinition','guardduty:AcceptInvitation','guardduty:ArchiveFindings','guardduty:CreateDetector','guardduty:CreateIPSet','guardduty:CreateMembers','guardduty:CreateSampleFindings','guardduty:CreateThreatIntelSet','guardduty:DeclineInvitations','guardduty:DeleteDetector','guardduty:DeleteInvitations','guardduty:DeleteIPSet','guardduty:DeleteMembers','guardduty:DeleteThreatIntelSet','guardduty:DisassociateFromMasterAccount','guardduty:DisassociateMembers','guardduty:GetDetector','guardduty:GetFindings','guardduty:GetFindingsStatistics','guardduty:GetInvitationsCount','guardduty:GetIPSet','guardduty:GetMasterAccount','guardduty:GetMembers','guardduty:GetThreatIntelSet','guardduty:InviteMembers','guardduty:ListDetectors','guardduty:ListFindings','guardduty:ListInvitations','guardduty:ListIPSets','guardduty:ListMembers','guardduty:ListThreatIntelSets','guardduty:StartMonitoringMembers','guardduty:StopMonitoringMembers','guardduty:UnarchiveFindings','guardduty:UpdateDetector','guardduty:UpdateFindingsFeedback','guardduty:UpdateIPSet','guardduty:UpdateThreatIntelSet','health:DescribeAffectedEntities','health:DescribeEntityAggregates','health:DescribeEventAggregates','health:DescribeEventDetails','health:DescribeEvents','health:DescribeEventTypes','iam:AddClientIDToOpenIDConnectProvider','iam:AddRoleToInstanceProfile','iam:AddUserToGroup','iam:AttachGroupPolicy','iam:AttachRolePolicy','iam:AttachUserPolicy','iam:ChangePassword','iam:CreateAccessKey','iam:CreateAccountAlias','iam:CreateGroup','iam:CreateInstanceProfile','iam:CreateLoginProfile','iam:CreateOpenIDConnectProvider','iam:CreatePolicy','iam:CreatePolicyVersion','iam:CreateRole','iam:CreateSAMLProvider','iam:CreateServiceLinkedRole','iam:CreateServiceSpecificCredential','iam:CreateUser','iam:CreateVirtualMFADevice','iam:DeactivateMFADevice','iam:DeleteAccessKey','iam:DeleteAccountAlias','iam:DeleteAccountPasswordPolicy','iam:DeleteGroup','iam:DeleteGroupPolicy','iam:DeleteInstanceProfile','iam:DeleteLoginProfile','iam:DeleteOpenIDConnectProvider','iam:DeletePolicy','iam:DeletePolicyVersion','iam:DeleteRole','iam:DeleteRolePolicy','iam:DeleteSAMLProvider','iam:DeleteServerCertificate','iam:DeleteServiceLinkedRole','iam:DeleteServiceSpecificCredential','iam:DeleteSigningCertificate','iam:DeleteSSHPublicKey','iam:DeleteUser','iam:DeleteUserPolicy','iam:DeleteVirtualMFADevice','iam:DetachGroupPolicy','iam:DetachRolePolicy','iam:DetachUserPolicy','iam:EnableMFADevice','iam:GenerateCredentialReport','iam:GenerateServiceLastAccessedDetails','iam:GetAccessKeyLastUsed','iam:GetAccountAuthorizationDetails','iam:GetAccountPasswordPolicy','iam:GetAccountSummary','iam:GetContextKeysForCustomPolicy','iam:GetContextKeysForPrincipalPolicy','iam:GetCredentialReport','iam:GetGroup','iam:GetGroupPolicy','iam:GetInstanceProfile','iam:GetLoginProfile','iam:GetOpenIDConnectProvider','iam:GetPolicy','iam:GetPolicyVersion','iam:GetRole','iam:GetRolePolicy','iam:GetSAMLProvider','iam:GetServerCertificate','iam:GetServiceLastAccessedDetails','iam:GetServiceLastAccessedDetailsWithEntities','iam:GetServiceLinkedRoleDeletionStatus','iam:GetSSHPublicKey','iam:GetUser','iam:GetUserPolicy','iam:ListAccessKeys','iam:ListAccountAliases','iam:ListAttachedGroupPolicies','iam:ListAttachedRolePolicies','iam:ListAttachedUserPolicies','iam:ListEntitiesForPolicy','iam:ListGroupPolicies','iam:ListGroups','iam:ListGroupsForUser','iam:ListInstanceProfiles','iam:ListInstanceProfilesForRole','iam:ListMFADevices','iam:ListOpenIDConnectProviders','iam:ListPolicies','iam:ListPoliciesGrantingServiceAccess','iam:ListPolicyVersions','iam:ListRolePolicies','iam:ListRoles','iam:ListSAMLProviders','iam:ListServerCertificates','iam:ListServiceSpecificCredentials','iam:ListSigningCertificates','iam:ListSSHPublicKeys','iam:ListUserPolicies','iam:ListUsers','iam:ListVirtualMFADevices','iam:PassRole','iam:PutGroupPolicy','iam:PutRolePolicy','iam:PutUserPolicy','iam:RemoveClientIDFromOpenIDConnectProvider','iam:RemoveRoleFromInstanceProfile','iam:RemoveUserFromGroup','iam:ResetServiceSpecificCredential','iam:ResyncMFADevice','iam:SetDefaultPolicyVersion','iam:SimulateCustomPolicy','iam:SimulatePrincipalPolicy','iam:UpdateAccessKey','iam:UpdateAccountPasswordPolicy','iam:UpdateAssumeRolePolicy','iam:UpdateGroup','iam:UpdateLoginProfile','iam:UpdateOpenIDConnectProviderThumbprint','iam:UpdateRoleDescription','iam:UpdateSAMLProvider','iam:UpdateServerCertificate','iam:UpdateServiceSpecificCredential','iam:UpdateSigningCertificate','iam:UpdateSSHPublicKey','iam:UpdateUser','iam:UploadServerCertificate','iam:UploadSigningCertificate','iam:UploadSSHPublicKey','importexport:CancelJob','importexport:CreateJob','importexport:GetShippingLabel','importexport:GetStatus','importexport:ListJobs','importexport:UpdateJob','inspector:AddAttributesToFindings','inspector:CreateAssessmentTarget','inspector:CreateAssessmentTemplate','inspector:CreateResourceGroup','inspector:DeleteAssessmentRun','inspector:DeleteAssessmentTarget','inspector:DeleteAssessmentTemplate','inspector:DescribeAssessmentRuns','inspector:DescribeAssessmentTargets','inspector:DescribeAssessmentTemplates','inspector:DescribeCrossAccountAccessRole','inspector:DescribeFindings','inspector:DescribeResourceGroups','inspector:DescribeRulesPackages','inspector:GetTelemetryMetadata','inspector:ListAssessmentRunAgents','inspector:ListAssessmentRuns','inspector:ListAssessmentTargets','inspector:ListAssessmentTemplates','inspector:ListEventSubscriptions','inspector:ListFindings','inspector:ListRulesPackages','inspector:ListTagsForResource','inspector:PreviewAgents','inspector:RegisterCrossAccountAccessRole','inspector:RemoveAttributesFromFindings','inspector:SetTagsForResource','inspector:StartAssessmentRun','inspector:StopAssessmentRun','inspector:SubscribeToEvent','inspector:UnsubscribeFromEvent','inspector:UpdateAssessmentTarget','iot:AcceptCertificateTransfer','iot:AssociateTargetsWithJob','iot:AttachPolicy','iot:AttachPrincipalPolicy','iot:AttachThingPrincipal','iot:CancelCertificateTransfer','iot:CancelJob','iot:ClearDefaultAuthorizer','iot:Connect','iot:CreateAuthorizer','iot:CreateCertificateFromCsr','iot:CreateJob','iot:CreateKeysAndCertificate','iot:CreateOTAUpdateJob','iot:CreatePolicy','iot:CreatePolicyVersion','iot:CreateRoleAlias','iot:CreateStream','iot:CreateThing','iot:CreateThingType','iot:CreateTopicRule','iot:DeleteAuthorizer','iot:DeleteCACertificate','iot:DeleteCertificate','iot:DeleteOTAUpdateJob','iot:DeletePolicy','iot:DeletePolicyVersion','iot:DeleteRegistrationCode','iot:DeleteRoleAlias','iot:DeleteStream','iot:DeleteThing','iot:DeleteThingShadow','iot:DeleteThingType','iot:DeleteTopicRule','iot:DeprecateThingType','iot:DescribeAuthorizer','iot:DescribeCACertificate','iot:DescribeCertificate','iot:DescribeDefaultAuthorizer','iot:DescribeEndpoint','iot:DescribeIndex','iot:DescribeJob','iot:DescribeJobExecution','iot:DescribeRoleAlias','iot:DescribeStream','iot:DescribeThing','iot:DescribeThingType','iot:DetachPolicy','iot:DetachPrincipalPolicy','iot:DetachThingPrincipal','iot:DisableTopicRule','iot:EnableTopicRule','iot:GetEffectivePolicies','iot:GetIndexingConfiguration','iot:GetJobDocument','iot:GetLoggingOptions','iot:GetOTAUpdateJob','iot:GetPolicy','iot:GetPolicyVersion','iot:GetRegistrationCode','iot:GetThingShadow','iot:GetTopicRule','iot:ListAttachedPolicies','iot:ListAuthorizers','iot:ListCACertificates','iot:ListCertificates','iot:ListCertificatesByCA','iot:ListIndices','iot:ListJobExecutionsForJob','iot:ListJobExecutionsForThing','iot:ListJobs','iot:ListOTAUpdateJobs','iot:ListOutgoingCertificates','iot:ListPolicies','iot:ListPolicyPrincipals','iot:ListPolicyVersions','iot:ListPrincipalPolicies','iot:ListPrincipalThings','iot:ListRoleAliases','iot:ListStreams','iot:ListTargetsForPolicy','iot:ListThingPrincipals','iot:ListThings','iot:ListThingTypes','iot:ListTopicRules','iot:Publish','iot:Receive','iot:RegisterCACertificate','iot:RegisterCertificate','iot:RejectCertificateTransfer','iot:ReplaceTopicRule','iot:SearchIndex','iot:SetDefaultAuthorizer','iot:SetDefaultPolicyVersion','iot:SetLoggingOptions','iot:Subscribe','iot:TestAuthorization','iot:TestInvokeAuthorizer','iot:TransferCertificate','iot:UpdateAuthorizer','iot:UpdateCACertificate','iot:UpdateCertificate','iot:UpdateIndexingConfiguration','iot:UpdateRoleAlias','iot:UpdateStream','iot:UpdateThing','iot:UpdateThingShadow','iotanalytics:CreateChannel','iotanalytics:CreateDataset','iotanalytics:CreateDatasetContent','iotanalytics:CreateDatastore','iotanalytics:CreatePipeline','iotanalytics:DeleteChannel','iotanalytics:DeleteDataset','iotanalytics:DeleteDatasetContent','iotanalytics:DeleteDatastore','iotanalytics:DeletePipeline','iotanalytics:DescribeChannel','iotanalytics:DescribeDataset','iotanalytics:DescribeDatastore','iotanalytics:DescribePipeline','iotanalytics:GetDatasetContent','iotanalytics:ListChannels','iotanalytics:ListDatasets','iotanalytics:ListDatastores','iotanalytics:ListPipelines','iotanalytics:UpdateDataset','iotanalytics:UpdatePipeline','kinesis:AddTagsToStream','kinesis:CreateStream','kinesis:DecreaseStreamRetentionPeriod','kinesis:DeleteStream','kinesis:DescribeLimits','kinesis:DescribeStream','kinesis:DisableEnhancedMonitoring','kinesis:EnableEnhancedMonitoring','kinesis:GetRecords','kinesis:GetShardIterator','kinesis:IncreaseStreamRetentionPeriod','kinesis:ListStreams','kinesis:ListTagsForStream','kinesis:MergeShards','kinesis:PutRecord','kinesis:PutRecords','kinesis:RemoveTagsFromStream','kinesis:SplitShard','kinesis:UpdateShardCount','kinesisanalytics:AddApplicationInput','kinesisanalytics:AddApplicationOutput','kinesisanalytics:AddApplicationReferenceDataSource','kinesisanalytics:CreateApplication','kinesisanalytics:DeleteApplication','kinesisanalytics:DeleteApplicationOutput','kinesisanalytics:DeleteApplicationReferenceDataSource','kinesisanalytics:DescribeApplication','kinesisanalytics:DiscoverInputSchema','kinesisanalytics:ListApplications','kinesisanalytics:StartApplication','kinesisanalytics:StopApplication','kinesisanalytics:UpdateApplication','kinesisvideo:CreateStream','kinesisvideo:DeleteStream','kinesisvideo:DescribeStream','kinesisvideo:GetDataEndpoint','kinesisvideo:GetMedia','kinesisvideo:GetMediaForFragmentList','kinesisvideo:ListFragments','kinesisvideo:ListStreams','kinesisvideo:ListTagsForStream','kinesisvideo:PutMedia','kinesisvideo:TagStream','kinesisvideo:UntagStream','kinesisvideo:UpdateDataRetention','kinesisvideo:UpdateStream','kms:CancelKeyDeletion','kms:CreateAlias','kms:CreateGrant','kms:CreateKey','kms:Decrypt','kms:DeleteAlias','kms:DeleteImportedKeyMaterial','kms:DescribeKey','kms:DisableKey','kms:DisableKeyRotation','kms:EnableKey','kms:EnableKeyRotation','kms:Encrypt','kms:GenerateDataKey','kms:GenerateDataKeyWithoutPlaintext','kms:GenerateRandom','kms:GetKeyPolicy','kms:GetKeyRotationStatus','kms:GetParametersForImport','kms:ImportKeyMaterial','kms:ListAliases','kms:ListGrants','kms:ListKeyPolicies','kms:ListKeys','kms:ListResourceTags','kms:ListRetirableGrants','kms:PutKeyPolicy','kms:ReEncryptFrom','kms:ReEncryptTo','kms:RetireGrant','kms:RevokeGrant','kms:ScheduleKeyDeletion','kms:TagResource','kms:UntagResource','kms:UpdateAlias','kms:UpdateKeyDescription','lambda:AddPermission','lambda:CreateAlias','lambda:CreateEventSourceMapping','lambda:CreateFunction','lambda:DeleteAlias','lambda:DeleteEventSourceMapping','lambda:DeleteFunction','lambda:DeleteFunctionConcurrency','lambda:EnableReplication','lambda:GetAccountSettings','lambda:GetAlias','lambda:GetEventSourceMapping','lambda:GetFunction','lambda:GetFunctionConfiguration','lambda:GetPolicy','lambda:InvokeAsync','lambda:InvokeFunction','lambda:ListAliases','lambda:ListEventSourceMappings','lambda:ListFunctions','lambda:ListTags','lambda:ListVersionsByFunction','lambda:PublishVersion','lambda:PutFunctionConcurrency','lambda:RemovePermission','lambda:TagResource','lambda:UntagResource','lambda:UpdateAlias','lambda:UpdateEventSourceMapping','lambda:UpdateFunctionCode','lambda:UpdateFunctionConfiguration','lex:CreateBotVersion','lex:CreateIntentVersion','lex:CreateSlotTypeVersion','lex:DeleteBot','lex:DeleteBotAlias','lex:DeleteBotChannelAssociation','lex:DeleteBotVersion','lex:DeleteIntent','lex:DeleteIntentVersion','lex:DeleteSlotType','lex:DeleteSlotTypeVersion','lex:DeleteUtterances','lex:GetBot','lex:GetBotAlias','lex:GetBotAliases','lex:GetBotChannelAssociation','lex:GetBotChannelAssociations','lex:GetBots','lex:GetBotVersions','lex:GetBuiltinIntent','lex:GetBuiltinIntents','lex:GetBuiltinSlotTypes','lex:GetIntent','lex:GetIntents','lex:GetIntentVersions','lex:GetSlotType','lex:GetSlotTypes','lex:GetSlotTypeVersions','lex:GetUtterancesView','lex:PostContent','lex:PostText','lex:PutBot','lex:PutBotAlias','lex:PutIntent','lex:PutSlotType','lightsail:AllocateStaticIp','lightsail:AttachStaticIp','lightsail:CloseInstancePublicPorts','lightsail:CreateDomain','lightsail:CreateDomainEntry','lightsail:CreateInstances','lightsail:CreateInstancesFromSnapshot','lightsail:CreateInstanceSnapshot','lightsail:CreateKeyPair','lightsail:DeleteDomain','lightsail:DeleteDomainEntry','lightsail:DeleteInstance','lightsail:DeleteInstanceSnapshot','lightsail:DeleteKeyPair','lightsail:DetachStaticIp','lightsail:DownloadDefaultKeyPair','lightsail:GetActiveNames','lightsail:GetBlueprints','lightsail:GetBundles','lightsail:GetDomain','lightsail:GetDomains','lightsail:GetInstance','lightsail:GetInstanceAccessDetails','lightsail:GetInstanceMetricData','lightsail:GetInstancePortStates','lightsail:GetInstances','lightsail:GetInstanceSnapshot','lightsail:GetInstanceSnapshots','lightsail:GetInstanceState','lightsail:GetKeyPair','lightsail:GetKeyPairs','lightsail:GetOperation','lightsail:GetOperations','lightsail:GetOperationsForResource','lightsail:GetRegions','lightsail:GetStaticIp','lightsail:GetStaticIps','lightsail:ImportKeyPair','lightsail:IsVpcPeered','lightsail:OpenInstancePublicPorts','lightsail:PeerVpc','lightsail:RebootInstance','lightsail:ReleaseStaticIp','lightsail:StartInstance','lightsail:StopInstance','lightsail:UnpeerVpc','lightsail:UpdateDomainEntry','logs:AssociateKmsKey','logs:CancelExportTask','logs:CreateExportTask','logs:CreateLogGroup','logs:CreateLogStream','logs:DeleteDestination','logs:DeleteLogGroup','logs:DeleteLogStream','logs:DeleteMetricFilter','logs:DeleteResourcePolicy','logs:DeleteRetentionPolicy','logs:DeleteSubscriptionFilter','logs:DescribeDestinations','logs:DescribeExportTasks','logs:DescribeLogGroups','logs:DescribeLogStreams','logs:DescribeMetricFilters','logs:DescribeResourcePolicies','logs:DescribeSubscriptionFilters','logs:DisassociateKmsKey','logs:FilterLogEvents','logs:GetLogEvents','logs:ListTagsLogGroup','logs:PutDestination','logs:PutDestinationPolicy','logs:PutLogEvents','logs:PutMetricFilter','logs:PutResourcePolicy','logs:PutRetentionPolicy','logs:PutSubscriptionFilter','logs:TagLogGroup','logs:TestMetricFilter','logs:UntagLogGroup','machinelearning:AddTags','machinelearning:CreateBatchPrediction','machinelearning:CreateDataSourceFromRDS','machinelearning:CreateDataSourceFromRedshift','machinelearning:CreateDataSourceFromS3','machinelearning:CreateEvaluation','machinelearning:CreateMLModel','machinelearning:CreateRealtimeEndpoint','machinelearning:DeleteBatchPrediction','machinelearning:DeleteDataSource','machinelearning:DeleteEvaluation','machinelearning:DeleteMLModel','machinelearning:DeleteRealtimeEndpoint','machinelearning:DeleteTags','machinelearning:DescribeBatchPredictions','machinelearning:DescribeDataSources','machinelearning:DescribeEvaluations','machinelearning:DescribeMLModels','machinelearning:DescribeTags','machinelearning:GetBatchPrediction','machinelearning:GetDataSource','machinelearning:GetEvaluation','machinelearning:GetMLModel','machinelearning:Predict','machinelearning:UpdateBatchPrediction','machinelearning:UpdateDataSource','machinelearning:UpdateEvaluation','machinelearning:UpdateMLModel','mechanicalturk:ApproveAssignment','mechanicalturk:ApproveRejectedAssignment','mechanicalturk:AssignQualification','mechanicalturk:BlockWorker','mechanicalturk:ChangeHITTypeOfHIT','mechanicalturk:CreateHIT','mechanicalturk:CreateQualificationType','mechanicalturk:DisableHIT','mechanicalturk:DisposeHIT','mechanicalturk:DisposeQualificationType','mechanicalturk:ExtendHIT','mechanicalturk:ForceExpireHIT','mechanicalturk:GetAccountBalance','mechanicalturk:GetAssignment','mechanicalturk:GetAssignmentsForHIT','mechanicalturk:GetBlockedWorkers','mechanicalturk:GetBonusPayments','mechanicalturk:GetFileUploadURL','mechanicalturk:GetHIT','mechanicalturk:GetHITsForQualificationType','mechanicalturk:GetQualificationRequests','mechanicalturk:GetQualificationScore','mechanicalturk:GetQualificationsForQualificationType','mechanicalturk:GetQualificationType','mechanicalturk:GetRequesterStatistic','mechanicalturk:GetRequesterWorkerStatistic','mechanicalturk:GetReviewableHITs','mechanicalturk:GetReviewResultsForHIT','mechanicalturk:GrantBonus','mechanicalturk:GrantQualification','mechanicalturk:NotifyWorkers','mechanicalturk:RegisterHITType','mechanicalturk:RejectAssignment','mechanicalturk:RejectQualificationRequest','mechanicalturk:RevokeQualification','mechanicalturk:SearchHITs','mechanicalturk:SearchQualificationTypes','mechanicalturk:SendTestEventNotification','mechanicalturk:SetHITAsReviewing','mechanicalturk:SetHITTypeNotification','mechanicalturk:UnblockWorker','mechanicalturk:UpdateQualificationScore','mechanicalturk:UpdateQualificationType','mediaconvert:CancelJob','mediaconvert:CreateJob','mediaconvert:CreateJobTemplate','mediaconvert:CreatePreset','mediaconvert:CreateQueue','mediaconvert:DeleteJobTemplate','mediaconvert:DeletePreset','mediaconvert:DeleteQueue','mediaconvert:DescribeEndpoint','mediaconvert:GetJob','mediaconvert:GetJobTemplate','mediaconvert:GetPreset','mediaconvert:GetQueue','mediaconvert:ListJobs','mediaconvert:ListJobTemplates','mediaconvert:ListPresets','mediaconvert:ListQueues','mediaconvert:UpdateJobTemplate','mediaconvert:UpdatePreset','mediaconvert:UpdateQueue','medialive:CreateChannel','medialive:CreateInput','medialive:CreateInputSecurityGroup','medialive:DeleteChannel','medialive:DeleteInput','medialive:DeleteInputSecurityGroup','medialive:DescribeChannel','medialive:DescribeInput','medialive:DescribeInputSecurityGroup','medialive:ListChannels','medialive:ListInputs','medialive:ListInputSecurityGroups','medialive:StartChannel','medialive:StopChannel','mediapackage:CreateChannel','mediapackage:CreateOriginEndpoint','mediapackage:DeleteChannel','mediapackage:DeleteOriginEndpoint','mediapackage:DescribeChannel','mediapackage:DescribeOriginEndpoint','mediapackage:ListChannels','mediapackage:ListOriginEndpoints','mediapackage:UpdateChannel','mediapackage:UpdateOriginEndpoint','mediastore:CreateContainer','mediastore:DeleteContainer','mediastore:DeleteContainerPolicy','mediastore:DeleteObject','mediastore:DescribeContainer','mediastore:DescribeObject','mediastore:GetContainerPolicy','mediastore:GetObject','mediastore:ListContainers','mediastore:ListItems','mediastore:PutContainerPolicy','mediastore:PutObject','mgh:AssociateCreatedArtifact','mgh:AssociateDiscoveredResource','mgh:CreateProgressUpdateStream','mgh:DeleteProgressUpdateStream','mgh:DescribeApplicationState','mgh:DescribeMigrationTask','mgh:DisassociateCreatedArtifact','mgh:DisassociateDiscoveredResource','mgh:ImportMigrationTask','mgh:ListCreatedArtifacts','mgh:ListDiscoveredResources','mgh:ListMigrationTasks','mgh:ListProgressUpdateStreams','mgh:NotifyApplicationState','mgh:NotifyMigrationTaskState','mgh:PutResourceAttributes','mobileanalytics:GetFinancialReports','mobileanalytics:GetReports','mobileanalytics:PutEvents','mobilehub:CreateProject','mobilehub:CreateServiceRole','mobilehub:DeleteProject','mobilehub:DeployToStage','mobilehub:DescribeBundle','mobilehub:ExportBundle','mobilehub:ExportProject','mobilehub:GenerateProjectParameters','mobilehub:GetProject','mobilehub:GetProjectSnapshot','mobilehub:ImportProject','mobilehub:ListAvailableConnectors','mobilehub:ListAvailableFeatures','mobilehub:ListAvailableRegions','mobilehub:ListBundles','mobilehub:ListProjects','mobilehub:SynchronizeProject','mobilehub:UpdateProject','mobilehub:VerifyServiceRole','mobiletargeting:CreateCampaign','mobiletargeting:CreateImportJob','mobiletargeting:CreateSegment','mobiletargeting:DeleteApnsChannel','mobiletargeting:DeleteCampaign','mobiletargeting:DeleteGcmChannel','mobiletargeting:DeleteSegment','mobiletargeting:GetApnsChannel','mobiletargeting:GetApplicationSettings','mobiletargeting:GetCampaign','mobiletargeting:GetCampaignActivities','mobiletargeting:GetCampaigns','mobiletargeting:GetCampaignVersion','mobiletargeting:GetCampaignVersions','mobiletargeting:GetEndpoint','mobiletargeting:GetGcmChannel','mobiletargeting:GetImportJob','mobiletargeting:GetImportJobs','mobiletargeting:GetReports','mobiletargeting:GetSegment','mobiletargeting:GetSegmentImportJobs','mobiletargeting:GetSegments','mobiletargeting:GetSegmentVersion','mobiletargeting:GetSegmentVersions','mobiletargeting:UpdateApnsChannel','mobiletargeting:UpdateApplicationSettings','mobiletargeting:UpdateCampaign','mobiletargeting:UpdateEndpoint','mobiletargeting:UpdateEndpointsBatch','mobiletargeting:UpdateGcmChannel','mobiletargeting:UpdateSegment','mq:CreateBroker','mq:CreateConfiguration','mq:CreateUser','mq:DeleteBroker','mq:DeleteUser','mq:DescribeBroker','mq:DescribeConfiguration','mq:DescribeConfigurationRevision','mq:DescribeUser','mq:ListBrokers','mq:ListConfigurationRevisions','mq:ListConfigurations','mq:ListUsers','mq:RebootBroker','mq:UpdateBroker','mq:UpdateConfiguration','mq:UpdateUser','opsworks:AssignInstance','opsworks:AssignVolume','opsworks:AssociateElasticIp','opsworks:AttachElasticLoadBalancer','opsworks:CloneStack','opsworks:CreateApp','opsworks:CreateDeployment','opsworks:CreateInstance','opsworks:CreateLayer','opsworks:CreateStack','opsworks:CreateUserProfile','opsworks:DeleteApp','opsworks:DeleteInstance','opsworks:DeleteLayer','opsworks:DeleteStack','opsworks:DeleteUserProfile','opsworks:DeregisterEcsCluster','opsworks:DeregisterElasticIp','opsworks:DeregisterInstance','opsworks:DeregisterRdsDbInstance','opsworks:DeregisterVolume','opsworks:DescribeAgentVersions','opsworks:DescribeApps','opsworks:DescribeCommands','opsworks:DescribeDeployments','opsworks:DescribeEcsClusters','opsworks:DescribeElasticIps','opsworks:DescribeElasticLoadBalancers','opsworks:DescribeInstances','opsworks:DescribeLayers','opsworks:DescribeLoadBasedAutoScaling','opsworks:DescribeMyUserProfile','opsworks:DescribePermissions','opsworks:DescribeRaidArrays','opsworks:DescribeRdsDbInstances','opsworks:DescribeServiceErrors','opsworks:DescribeStackProvisioningParameters','opsworks:DescribeStacks','opsworks:DescribeStackSummary','opsworks:DescribeTimeBasedAutoScaling','opsworks:DescribeUserProfiles','opsworks:DescribeVolumes','opsworks:DetachElasticLoadBalancer','opsworks:DisassociateElasticIp','opsworks:GetHostnameSuggestion','opsworks:GrantAccess','opsworks:ListTags','opsworks:RebootInstance','opsworks:RegisterEcsCluster','opsworks:RegisterElasticIp','opsworks:RegisterInstance','opsworks:RegisterRdsDbInstance','opsworks:RegisterVolume','opsworks:SetLoadBasedAutoScaling','opsworks:SetPermission','opsworks:SetTimeBasedAutoScaling','opsworks:StartInstance','opsworks:StartStack','opsworks:StopInstance','opsworks:StopStack','opsworks:TagResource','opsworks:UnassignInstance','opsworks:UnassignVolume','opsworks:UntagResource','opsworks:UpdateApp','opsworks:UpdateElasticIp','opsworks:UpdateInstance','opsworks:UpdateLayer','opsworks:UpdateMyUserProfile','opsworks:UpdateRdsDbInstance','opsworks:UpdateStack','opsworks:UpdateUserProfile','opsworks:UpdateVolume','opsworks-cm:AssociateNode','opsworks-cm:CreateBackup','opsworks-cm:CreateServer','opsworks-cm:DeleteBackup','opsworks-cm:DeleteServer','opsworks-cm:DescribeAccountAttributes','opsworks-cm:DescribeBackups','opsworks-cm:DescribeEvents','opsworks-cm:DescribeNodeAssociationStatus','opsworks-cm:DescribeServers','opsworks-cm:DisassociateNode','opsworks-cm:RestoreServer','opsworks-cm:StartMaintenance','opsworks-cm:UpdateServer','opsworks-cm:UpdateServerEngineAttributes','organizations:AcceptHandshake','organizations:AttachPolicy','organizations:CancelHandshake','organizations:CreateAccount','organizations:CreateOrganization','organizations:CreateOrganizationalUnit','organizations:CreatePolicy','organizations:DeclineHandshake','organizations:DeleteOrganization','organizations:DeleteOrganizationalUnit','organizations:DeletePolicy','organizations:DescribeAccount','organizations:DescribeCreateAccountStatus','organizations:DescribeHandshake','organizations:DescribeOrganization','organizations:DescribeOrganizationalUnit','organizations:DescribePolicy','organizations:DetachPolicy','organizations:DisablePolicyType','organizations:EnableAllFeatures','organizations:EnablePolicyType','organizations:InviteAccountToOrganization','organizations:LeaveOrganization','organizations:ListAccounts','organizations:ListAccountsForParent','organizations:ListChildren','organizations:ListCreateAccountStatus','organizations:ListHandshakesForAccount','organizations:ListHandshakesForOrganization','organizations:ListOrganizationalUnitsForParent','organizations:ListParents','organizations:ListPolicies','organizations:ListPoliciesForTarget','organizations:ListRoots','organizations:ListTargetsForPolicy','organizations:MoveAccount','organizations:RemoveAccountFromOrganization','organizations:UpdateOrganizationalUnit','organizations:UpdatePolicy','polly:DeleteLexicon','polly:DescribeVoices','polly:GetLexicon','polly:ListLexicons','polly:PutLexicon','polly:SynthesizeSpeech','pricing:DescribeServices','pricing:GetAttributeValues','pricing:GetProducts','rds:AddRoleToDBCluster','rds:AddSourceIdentifierToSubscription','rds:AddTagsToResource','rds:ApplyPendingMaintenanceAction','rds:AuthorizeDBSecurityGroupIngress','rds:CopyDBClusterSnapshot','rds:CopyDBParameterGroup','rds:CopyDBSnapshot','rds:CopyOptionGroup','rds:CreateDBCluster','rds:CreateDBClusterParameterGroup','rds:CreateDBClusterSnapshot','rds:CreateDBInstance','rds:CreateDBInstanceReadReplica','rds:CreateDBParameterGroup','rds:CreateDBSecurityGroup','rds:CreateDBSnapshot','rds:CreateDBSubnetGroup','rds:CreateEventSubscription','rds:CreateOptionGroup','rds:DeleteDBCluster','rds:DeleteDBClusterParameterGroup','rds:DeleteDBClusterSnapshot','rds:DeleteDBInstance','rds:DeleteDBParameterGroup','rds:DeleteDBSecurityGroup','rds:DeleteDBSnapshot','rds:DeleteDBSubnetGroup','rds:DeleteEventSubscription','rds:DeleteOptionGroup','rds:DescribeAccountAttributes','rds:DescribeCertificates','rds:DescribeDBClusterParameterGroups','rds:DescribeDBClusterParameters','rds:DescribeDBClusters','rds:DescribeDBClusterSnapshotAttributes','rds:DescribeDBClusterSnapshots','rds:DescribeDBEngineVersions','rds:DescribeDBInstances','rds:DescribeDBLogFiles','rds:DescribeDBParameterGroups','rds:DescribeDBParameters','rds:DescribeDBSecurityGroups','rds:DescribeDBSnapshotAttributes','rds:DescribeDBSnapshots','rds:DescribeDBSubnetGroups','rds:DescribeEngineDefaultClusterParameters','rds:DescribeEngineDefaultParameters','rds:DescribeEventCategories','rds:DescribeEvents','rds:DescribeEventSubscriptions','rds:DescribeOptionGroupOptions','rds:DescribeOptionGroups','rds:DescribeOrderableDBInstanceOptions','rds:DescribePendingMaintenanceActions','rds:DescribeReservedDBInstances','rds:DescribeReservedDBInstancesOfferings','rds:DownloadCompleteDBLogFile','rds:DownloadDBLogFilePortion','rds:FailoverDBCluster','rds:ListTagsForResource','rds:ModifyDBCluster','rds:ModifyDBClusterParameterGroup','rds:ModifyDBClusterSnapshotAttribute','rds:ModifyDBInstance','rds:ModifyDBParameterGroup','rds:ModifyDBSnapshotAttribute','rds:ModifyDBSubnetGroup','rds:ModifyEventSubscription','rds:ModifyOptionGroup','rds:PromoteReadReplica','rds:PurchaseReservedDBInstancesOffering','rds:RebootDBInstance','rds:RemoveSourceIdentifierFromSubscription','rds:RemoveTagsFromResource','rds:ResetDBClusterParameterGroup','rds:ResetDBParameterGroup','rds:RestoreDBClusterFromSnapshot','rds:RestoreDBClusterToPointInTime','rds:RestoreDBInstanceFromDBSnapshot','rds:RestoreDBInstanceToPointInTime','rds:RevokeDBSecurityGroupIngress','rds:StartDBInstance','rds:StopDBInstance','redshift:AuthorizeClusterSecurityGroupIngress','redshift:AuthorizeSnapshotAccess','redshift:CancelQuerySession','redshift:CopyClusterSnapshot','redshift:CreateCluster','redshift:CreateClusterParameterGroup','redshift:CreateClusterSecurityGroup','redshift:CreateClusterSnapshot','redshift:CreateClusterSubnetGroup','redshift:CreateClusterUser','redshift:CreateEventSubscription','redshift:CreateHsmClientCertificate','redshift:CreateHsmConfiguration','redshift:CreateSnapshotCopyGrant','redshift:CreateTags','redshift:DeleteCluster','redshift:DeleteClusterParameterGroup','redshift:DeleteClusterSecurityGroup','redshift:DeleteClusterSnapshot','redshift:DeleteClusterSubnetGroup','redshift:DeleteEventSubscription','redshift:DeleteHsmClientCertificate','redshift:DeleteHsmConfiguration','redshift:DeleteSnapshotCopyGrant','redshift:DeleteTags','redshift:DescribeClusterParameterGroups','redshift:DescribeClusterParameters','redshift:DescribeClusters','redshift:DescribeClusterSecurityGroups','redshift:DescribeClusterSnapshots','redshift:DescribeClusterSubnetGroups','redshift:DescribeClusterVersions','redshift:DescribeDefaultClusterParameters','redshift:DescribeEventCategories','redshift:DescribeEvents','redshift:DescribeEventSubscriptions','redshift:DescribeHsmClientCertificates','redshift:DescribeHsmConfigurations','redshift:DescribeLoggingStatus','redshift:DescribeOrderableClusterOptions','redshift:DescribeReservedNodeOfferings','redshift:DescribeReservedNodes','redshift:DescribeResize','redshift:DescribeSnapshotCopyGrants','redshift:DescribeTableRestoreStatus','redshift:DescribeTags','redshift:DisableLogging','redshift:DisableSnapshotCopy','redshift:EnableLogging','redshift:EnableSnapshotCopy','redshift:GetClusterCredentials','redshift:JoinGroup','redshift:ModifyCluster','redshift:ModifyClusterIamRoles','redshift:ModifyClusterParameterGroup','redshift:ModifyClusterSubnetGroup','redshift:ModifyEventSubscription','redshift:ModifySnapshotCopyRetentionPeriod','redshift:PurchaseReservedNodeOffering','redshift:RebootCluster','redshift:ResetClusterParameterGroup','redshift:RestoreFromClusterSnapshot','redshift:RestoreTableFromClusterSnapshot','redshift:RevokeClusterSecurityGroupIngress','redshift:RevokeSnapshotAccess','redshift:RotateEncryptionKey','redshift:ViewQueriesInConsole','rekognition:CompareFaces','rekognition:CreateCollection','rekognition:CreateStreamProcessor','rekognition:DeleteCollection','rekognition:DeleteFaces','rekognition:DeleteStreamProcessor','rekognition:DescribeStreamProcessor','rekognition:DetectFaces','rekognition:DetectLabels','rekognition:DetectModerationLabels','rekognition:DetectText','rekognition:GetCelebrityInfo','rekognition:GetCelebrityRecognition','rekognition:GetContentModeration','rekognition:GetFaceDetection','rekognition:GetFaceSearch','rekognition:GetLabelDetection','rekognition:GetPersonTracking','rekognition:IndexFaces','rekognition:ListCollections','rekognition:ListFaces','rekognition:ListStreamProcessors','rekognition:RecognizeCelebrities','rekognition:SearchFaces','rekognition:SearchFacesByImage','rekognition:StartCelebrityRecognition','rekognition:StartContentModeration','rekognition:StartFaceDetection','rekognition:StartFaceSearch','rekognition:StartLabelDetection','rekognition:StartPersonTracking','rekognition:StartStreamProcessor','rekognition:StopStreamProcessor','resource-groups:CreateGroup','resource-groups:DeleteGroup','resource-groups:GetGroup','resource-groups:GetGroupQuery','resource-groups:GetTags','resource-groups:ListGroupResources','resource-groups:ListGroups','resource-groups:SearchResources','resource-groups:Tag','resource-groups:Untag','resource-groups:UpdateGroup','resource-groups:UpdateGroupQuery','route53:AssociateVPCWithHostedZone','route53:ChangeResourceRecordSets','route53:ChangeTagsForResource','route53:CreateHealthCheck','route53:CreateHostedZone','route53:CreateReusableDelegationSet','route53:CreateTrafficPolicy','route53:CreateTrafficPolicyInstance','route53:CreateTrafficPolicyVersion','route53:DeleteHealthCheck','route53:DeleteHostedZone','route53:DeleteReusableDelegationSet','route53:DeleteTrafficPolicy','route53:DeleteTrafficPolicyInstance','route53:DisableDomainAutoRenew','route53:DisassociateVPCFromHostedZone','route53:EnableDomainAutoRenew','route53:GetChange','route53:GetCheckerIpRanges','route53:GetGeoLocation','route53:GetHealthCheck','route53:GetHealthCheckCount','route53:GetHealthCheckLastFailureReason','route53:GetHealthCheckStatus','route53:GetHostedZone','route53:GetHostedZoneCount','route53:GetReusableDelegationSet','route53:GetTrafficPolicy','route53:GetTrafficPolicyInstance','route53:GetTrafficPolicyInstanceCount','route53:ListGeoLocations','route53:ListHealthChecks','route53:ListHostedZones','route53:ListHostedZonesByName','route53:ListResourceRecordSets','route53:ListReusableDelegationSets','route53:ListTagsForResource','route53:ListTagsForResources','route53:ListTrafficPolicies','route53:ListTrafficPolicyInstances','route53:ListTrafficPolicyInstancesByHostedZone','route53:ListTrafficPolicyInstancesByPolicy','route53:ListTrafficPolicyVersions','route53:TestDNSAnswer','route53:UpdateHealthCheck','route53:UpdateHostedZoneComment','route53:UpdateTrafficPolicyComment','route53:UpdateTrafficPolicyInstance','route53domains:CheckDomainAvailability','route53domains:DeleteTagsForDomain','route53domains:DisableDomainAutoRenew','route53domains:DisableDomainTransferLock','route53domains:EnableDomainAutoRenew','route53domains:EnableDomainTransferLock','route53domains:GetContactReachabilityStatus','route53domains:GetDomainDetail','route53domains:GetDomainSuggestions','route53domains:GetOperationDetail','route53domains:ListDomains','route53domains:ListOperations','route53domains:ListTagsForDomain','route53domains:RegisterDomain','route53domains:RenewDomain','route53domains:ResendContactReachabilityEmail','route53domains:RetrieveDomainAuthCode','route53domains:TransferDomain','route53domains:UpdateDomainContact','route53domains:UpdateDomainContactPrivacy','route53domains:UpdateDomainNameservers','route53domains:UpdateTagsForDomain','route53domains:ViewBilling','s3:AbortMultipartUpload','s3:CreateBucket','s3:DeleteBucket','s3:DeleteBucketPolicy','s3:DeleteBucketWebsite','s3:DeleteObject','s3:DeleteObjectTagging','s3:DeleteObjectVersion','s3:DeleteObjectVersionTagging','s3:GetAccelerateConfiguration','s3:GetAnalyticsConfiguration','s3:GetBucketAcl','s3:GetBucketCORS','s3:GetBucketLocation','s3:GetBucketLogging','s3:GetBucketNotification','s3:GetBucketPolicy','s3:GetBucketRequestPayment','s3:GetBucketTagging','s3:GetBucketVersioning','s3:GetBucketWebsite','s3:GetInventoryConfiguration','s3:GetIpConfiguration','s3:GetLifecycleConfiguration','s3:GetMetricsConfiguration','s3:GetObject','s3:GetObjectAcl','s3:GetObjectTagging','s3:GetObjectTorrent','s3:GetObjectVersion','s3:GetObjectVersionAcl','s3:GetObjectVersionForReplication','s3:GetObjectVersionTagging','s3:GetObjectVersionTorrent','s3:GetReplicationConfiguration','s3:HeadBucket','s3:ListAllMyBuckets','s3:ListBucket','s3:ListBucketByTags','s3:ListBucketMultipartUploads','s3:ListBucketVersions','s3:ListMultipartUploadParts','s3:ListObjects','s3:ObjectOwnerOverrideToBucketOwner','s3:PutAccelerateConfiguration','s3:PutAnalyticsConfiguration','s3:PutBucketAcl','s3:PutBucketCORS','s3:PutBucketLogging','s3:PutBucketNotification','s3:PutBucketPolicy','s3:PutBucketRequestPayment','s3:PutBucketTagging','s3:PutBucketVersioning','s3:PutBucketWebsite','s3:PutInventoryConfiguration','s3:PutIpConfiguration','s3:PutLifecycleConfiguration','s3:PutMetricsConfiguration','s3:PutObject','s3:PutObjectAcl','s3:PutObjectTagging','s3:PutObjectVersionAcl','s3:PutObjectVersionTagging','s3:PutReplicationConfiguration','s3:ReplicateDelete','s3:ReplicateObject','s3:ReplicateTags','s3:RestoreObject','sagemaker:AddTags','sagemaker:CreateEndpoint','sagemaker:CreateEndpointConfig','sagemaker:CreateModel','sagemaker:CreateNotebookInstance','sagemaker:CreatePresignedNotebookInstanceUrl','sagemaker:CreateTrainingJob','sagemaker:DeleteEndpoint','sagemaker:DeleteEndpointConfig','sagemaker:DeleteModel','sagemaker:DeleteNotebookInstance','sagemaker:DeleteTags','sagemaker:DescribeEndpoint','sagemaker:DescribeEndpointConfig','sagemaker:DescribeModel','sagemaker:DescribeNotebookInstance','sagemaker:DescribeTrainingJob','sagemaker:InvokeEndpoint','sagemaker:ListEndpointConfigs','sagemaker:ListEndpoints','sagemaker:ListModels','sagemaker:ListNotebookInstances','sagemaker:ListTags','sagemaker:ListTrainingJobs','sagemaker:StartNotebookInstance','sagemaker:StopNotebookInstance','sagemaker:StopTrainingJob','sagemaker:UpdateEndpoint','sagemaker:UpdateEndpointWeightsAndCapacities','sagemaker:UpdateNotebookInstance','sdb:BatchDeleteAttributes','sdb:BatchPutAttributes','sdb:CreateDomain','sdb:DeleteAttributes','sdb:DeleteDomain','sdb:DomainMetadata','sdb:GetAttributes','sdb:ListDomains','sdb:PutAttributes','sdb:Select','serverlessrepo:CreateApplication','serverlessrepo:CreateApplicationVersion','serverlessrepo:CreateCloudFormationChangeSet','serverlessrepo:DeleteApplication','serverlessrepo:GetApplication','serverlessrepo:GetApplicationPolicy','serverlessrepo:ListApplications','serverlessrepo:ListApplicationVersions','serverlessrepo:PutApplicationPolicy','serverlessrepo:SearchApplications','serverlessrepo:UpdateApplication','servicecatalog:AcceptPortfolioShare','servicecatalog:AssociatePrincipalWithPortfolio','servicecatalog:AssociateProductWithPortfolio','servicecatalog:CreateConstraint','servicecatalog:CreatePortfolio','servicecatalog:CreatePortfolioShare','servicecatalog:CreateProduct','servicecatalog:CreateProvisioningArtifact','servicecatalog:DeleteConstraint','servicecatalog:DeletePortfolio','servicecatalog:DeletePortfolioShare','servicecatalog:DeleteProduct','servicecatalog:DeleteProvisioningArtifact','servicecatalog:DescribeConstraint','servicecatalog:DescribePortfolio','servicecatalog:DescribeProduct','servicecatalog:DescribeProductAsAdmin','servicecatalog:DescribeProductView','servicecatalog:DescribeProvisioningArtifact','servicecatalog:DescribeProvisioningParameters','servicecatalog:DescribeRecord','servicecatalog:DisassociatePrincipalFromPortfolio','servicecatalog:DisassociateProductFromPortfolio','servicecatalog:ListAcceptedPortfolioShares','servicecatalog:ListConstraintsForPortfolio','servicecatalog:ListLaunchPaths','servicecatalog:ListPortfolioAccess','servicecatalog:ListPortfolios','servicecatalog:ListPortfoliosForProduct','servicecatalog:ListPrincipalsForPortfolio','servicecatalog:ListProvisioningArtifacts','servicecatalog:ListRecordHistory','servicecatalog:ProvisionProduct','servicecatalog:RejectPortfolioShare','servicecatalog:ScanProvisionedProducts','servicecatalog:SearchProducts','servicecatalog:SearchProductsAsAdmin','servicecatalog:TerminateProvisionedProduct','servicecatalog:UpdateConstraint','servicecatalog:UpdatePortfolio','servicecatalog:UpdateProduct','servicecatalog:UpdateProvisionedProduct','servicecatalog:UpdateProvisioningArtifact','servicediscovery:CreatePrivateDnsNamespace','servicediscovery:CreatePublicDnsNamespace','servicediscovery:CreateService','servicediscovery:DeleteNamespace','servicediscovery:DeleteService','servicediscovery:DeregisterInstance','servicediscovery:GetInstance','servicediscovery:GetInstancesHealthStatus','servicediscovery:GetNamespace','servicediscovery:GetOperation','servicediscovery:GetService','servicediscovery:ListInstances','servicediscovery:ListNamespaces','servicediscovery:ListOperations','servicediscovery:ListServices','servicediscovery:RegisterInstance','servicediscovery:UpdateInstanceHeartbeatStatus','servicediscovery:UpdateService','ses:CloneReceiptRuleSet','ses:CreateConfigurationSet','ses:CreateConfigurationSetEventDestination','ses:CreateConfigurationSetTrackingOptions','ses:CreateCustomVerificationEmailTemplate','ses:CreateReceiptFilter','ses:CreateReceiptRule','ses:CreateReceiptRuleSet','ses:CreateTemplate','ses:DeleteConfigurationSet','ses:DeleteConfigurationSetEventDestination','ses:DeleteConfigurationSetTrackingOptions','ses:DeleteCustomVerificationEmailTemplate','ses:DeleteIdentity','ses:DeleteIdentityPolicy','ses:DeleteReceiptFilter','ses:DeleteReceiptRule','ses:DeleteReceiptRuleSet','ses:DeleteTemplate','ses:DeleteVerifiedEmailAddress','ses:DescribeActiveReceiptRuleSet','ses:DescribeConfigurationSet','ses:DescribeReceiptRule','ses:DescribeReceiptRuleSet','ses:GetAccountSendingEnabled','ses:GetCustomVerificationEmailTemplate','ses:GetIdentityDkimAttributes','ses:GetIdentityMailFromDomainAttributes','ses:GetIdentityNotificationAttributes','ses:GetIdentityPolicies','ses:GetIdentityVerificationAttributes','ses:GetSendQuota','ses:GetSendStatistics','ses:GetTemplate','ses:ListConfigurationSets','ses:ListCustomVerificationEmailTemplates','ses:ListIdentities','ses:ListIdentityPolicies','ses:ListReceiptFilters','ses:ListReceiptRuleSets','ses:ListTemplates','ses:ListVerifiedEmailAddresses','ses:PutIdentityPolicy','ses:ReorderReceiptRuleSet','ses:SendBounce','ses:SendBulkTemplatedEmail','ses:SendCustomVerificationEmail','ses:SendEmail','ses:SendRawEmail','ses:SendTemplatedEmail','ses:SetActiveReceiptRuleSet','ses:SetIdentityDkimEnabled','ses:SetIdentityFeedbackForwardingEnabled','ses:SetIdentityHeadersInNotificationsEnabled','ses:SetIdentityMailFromDomain','ses:SetIdentityNotificationTopic','ses:SetReceiptRulePosition','ses:TestRenderTemplate','ses:UpdateAccountSendingEnabled','ses:UpdateConfigurationSetEventDestination','ses:UpdateConfigurationSetReputationMetricsEnabled','ses:UpdateConfigurationSetSendingEnabled','ses:UpdateConfigurationSetTrackingOptions','ses:UpdateCustomVerificationEmailTemplate','ses:UpdateReceiptRule','ses:UpdateTemplate','ses:VerifyDomainDkim','ses:VerifyDomainIdentity','ses:VerifyEmailAddress','ses:VerifyEmailIdentity','shield:CreateProtection','shield:CreateSubscription','shield:DeleteProtection','shield:DeleteSubscription','shield:DescribeAttack','shield:DescribeProtection','shield:DescribeSubscription','shield:ListAttacks','shield:ListProtections','signer:DescribeSigningJob','signer:ListSigningJobs','signer:StartSigningJob','snowball:CancelCluster','snowball:CancelJob','snowball:CreateAddress','snowball:CreateCluster','snowball:CreateJob','snowball:DescribeAddress','snowball:DescribeAddresses','snowball:DescribeCluster','snowball:DescribeJob','snowball:GetJobManifest','snowball:GetJobUnlockCode','snowball:GetSnowballUsage','snowball:ListClusterJobs','snowball:ListClusters','snowball:ListJobs','snowball:UpdateCluster','snowball:UpdateJob','sns:AddPermission','sns:CheckIfPhoneNumberIsOptedOut','sns:ConfirmSubscription','sns:CreatePlatformApplication','sns:CreatePlatformEndpoint','sns:CreateTopic','sns:DeleteEndpoint','sns:DeletePlatformApplication','sns:DeleteTopic','sns:GetEndpointAttributes','sns:GetPlatformApplicationAttributes','sns:GetSMSAttributes','sns:GetSubscriptionAttributes','sns:GetTopicAttributes','sns:ListEndpointsByPlatformApplication','sns:ListPhoneNumbersOptedOut','sns:ListPlatformApplications','sns:ListSubscriptions','sns:ListSubscriptionsByTopic','sns:ListTopics','sns:OptInPhoneNumber','sns:Publish','sns:RemovePermission','sns:SetEndpointAttributes','sns:SetPlatformApplicationAttributes','sns:SetSMSAttributes','sns:SetSubscriptionAttributes','sns:SetTopicAttributes','sns:Subscribe','sns:Unsubscribe','sqs:AddPermission','sqs:ChangeMessageVisibility','sqs:ChangeMessageVisibilityBatch','sqs:CreateQueue','sqs:DeleteMessage','sqs:DeleteMessageBatch','sqs:DeleteQueue','sqs:GetQueueAttributes','sqs:GetQueueUrl','sqs:ListDeadLetterSourceQueues','sqs:ListQueues','sqs:ListQueueTags','sqs:PurgeQueue','sqs:ReceiveMessage','sqs:RemovePermission','sqs:SendMessage','sqs:SendMessageBatch','sqs:SetQueueAttributes','sqs:TagQueue','sqs:UntagQueue','ssm:AddTagsToResource','ssm:CancelCommand','ssm:CreateActivation','ssm:CreateAssociation','ssm:CreateAssociationBatch','ssm:CreateDocument','ssm:CreateMaintenanceWindow','ssm:CreatePatchBaseline','ssm:CreateResourceDataSync','ssm:DeleteActivation','ssm:DeleteAssociation','ssm:DeleteDocument','ssm:DeleteMaintenanceWindow','ssm:DeleteParameter','ssm:DeleteParameters','ssm:DeletePatchBaseline','ssm:DeleteResourceDataSync','ssm:DeregisterManagedInstance','ssm:DeregisterPatchBaselineForPatchGroup','ssm:DeregisterTargetFromMaintenanceWindow','ssm:DeregisterTaskFromMaintenanceWindow','ssm:DescribeActivations','ssm:DescribeAssociation','ssm:DescribeAutomationExecutions','ssm:DescribeAutomationStepExecutions','ssm:DescribeAvailablePatches','ssm:DescribeDocument','ssm:DescribeDocumentParameters','ssm:DescribeDocumentPermission','ssm:DescribeEffectiveInstanceAssociations','ssm:DescribeEffectivePatchesForPatchBaseline','ssm:DescribeInstanceAssociationsStatus','ssm:DescribeInstanceInformation','ssm:DescribeInstancePatches','ssm:DescribeInstancePatchStates','ssm:DescribeInstancePatchStatesForPatchGroup','ssm:DescribeInstanceProperties','ssm:DescribeMaintenanceWindowExecutions','ssm:DescribeMaintenanceWindowExecutionTaskInvocations','ssm:DescribeMaintenanceWindowExecutionTasks','ssm:DescribeMaintenanceWindows','ssm:DescribeMaintenanceWindowTargets','ssm:DescribeMaintenanceWindowTasks','ssm:DescribeParameters','ssm:DescribePatchBaselines','ssm:DescribePatchGroups','ssm:DescribePatchGroupState','ssm:GetAutomationExecution','ssm:GetCommandInvocation','ssm:GetDefaultPatchBaseline','ssm:GetDeployablePatchSnapshotForInstance','ssm:GetDocument','ssm:GetInventory','ssm:GetInventorySchema','ssm:GetMaintenanceWindow','ssm:GetMaintenanceWindowExecution','ssm:GetMaintenanceWindowExecutionTask','ssm:GetMaintenanceWindowExecutionTaskInvocation','ssm:GetMaintenanceWindowTask','ssm:GetManifest','ssm:GetParameter','ssm:GetParameterHistory','ssm:GetParameters','ssm:GetParametersByPath','ssm:GetPatchBaseline','ssm:GetPatchBaselineForPatchGroup','ssm:ListAssociations','ssm:ListAssociationVersions','ssm:ListCommandInvocations','ssm:ListCommands','ssm:ListDocuments','ssm:ListDocumentVersions','ssm:ListInstanceAssociations','ssm:ListInventoryEntries','ssm:ListResourceDataSync','ssm:ListTagsForResource','ssm:ModifyDocumentPermission','ssm:PutComplianceItems','ssm:PutConfigurePackageResult','ssm:PutInventory','ssm:PutParameter','ssm:RegisterDefaultPatchBaseline','ssm:RegisterPatchBaselineForPatchGroup','ssm:RegisterTargetWithMaintenanceWindow','ssm:RegisterTaskWithMaintenanceWindow','ssm:RemoveTagsFromResource','ssm:SendAutomationSignal','ssm:SendCommand','ssm:StartAssociationsOnce','ssm:StartAutomationExecution','ssm:StopAutomationExecution','ssm:UpdateAssociation','ssm:UpdateAssociationStatus','ssm:UpdateDocument','ssm:UpdateDocumentDefaultVersion','ssm:UpdateInstanceAssociationStatus','ssm:UpdateInstanceInformation','ssm:UpdateMaintenanceWindow','ssm:UpdateMaintenanceWindowTarget','ssm:UpdateMaintenanceWindowTask','ssm:UpdateManagedInstanceRole','ssm:UpdatePatchBaseline','sso:AssociateDirectory','sso:AssociateProfile','sso:CreateApplicationInstance','sso:CreateApplicationInstanceCertificate','sso:CreatePermissionSet','sso:CreateProfile','sso:CreateTrust','sso:DeleteApplicationInstance','sso:DeleteApplicationInstanceCertificate','sso:DeletePermissionSet','sso:DeletePermissionsPolicy','sso:DeleteProfile','sso:DescribePermissionsPolicies','sso:DisassociateDirectory','sso:DisassociateProfile','sso:GetApplicationInstance','sso:GetApplicationTemplate','sso:GetPermissionSet','sso:GetProfile','sso:GetSSOStatus','sso:GetTrust','sso:ImportApplicationInstanceServiceProviderMetadata','sso:ListApplicationInstanceCertificates','sso:ListApplicationInstances','sso:ListApplicationTemplates','sso:ListDirectoryAssociations','sso:ListPermissionSets','sso:ListProfileAssociations','sso:ListProfiles','sso:PutPermissionsPolicy','sso:StartSSO','sso:UpdateApplicationInstanceActiveCertificate','sso:UpdateApplicationInstanceDisplayData','sso:UpdateApplicationInstanceResponseConfiguration','sso:UpdateApplicationInstanceResponseSchemaConfiguration','sso:UpdateApplicationInstanceSecurityConfiguration','sso:UpdateApplicationInstanceServiceProviderConfiguration','sso:UpdateApplicationInstanceStatus','sso:UpdateDirectoryAssociation','sso:UpdateProfile','sso:UpdateTrust','states:CreateActivity','states:CreateStateMachine','states:DeleteActivity','states:DeleteStateMachine','states:DescribeActivity','states:DescribeExecution','states:DescribeStateMachine','states:DescribeStateMachineForExecution','states:GetActivityTask','states:GetExecutionHistory','states:ListActivities','states:ListExecutions','states:ListStateMachines','states:SendTaskFailure','states:SendTaskHeartbeat','states:SendTaskSuccess','states:StartExecution','states:StopExecution','states:UpdateStateMachine','storagegateway:ActivateGateway','storagegateway:AddCache','storagegateway:AddTagsToResource','storagegateway:AddUploadBuffer','storagegateway:AddWorkingStorage','storagegateway:CancelArchival','storagegateway:CancelRetrieval','storagegateway:CreateCachediSCSIVolume','storagegateway:CreateNFSFileShare','storagegateway:CreateSnapshot','storagegateway:CreateSnapshotFromVolumeRecoveryPoint','storagegateway:CreateStorediSCSIVolume','storagegateway:CreateTapes','storagegateway:CreateTapeWithBarcode','storagegateway:DeleteBandwidthRateLimit','storagegateway:DeleteChapCredentials','storagegateway:DeleteFileShare','storagegateway:DeleteGateway','storagegateway:DeleteSnapshotSchedule','storagegateway:DeleteTape','storagegateway:DeleteTapeArchive','storagegateway:DeleteVolume','storagegateway:DescribeBandwidthRateLimit','storagegateway:DescribeCache','storagegateway:DescribeCachediSCSIVolumes','storagegateway:DescribeChapCredentials','storagegateway:DescribeGatewayInformation','storagegateway:DescribeMaintenanceStartTime','storagegateway:DescribeNFSFileShares','storagegateway:DescribeSnapshotSchedule','storagegateway:DescribeStorediSCSIVolumes','storagegateway:DescribeTapeArchives','storagegateway:DescribeTapeRecoveryPoints','storagegateway:DescribeTapes','storagegateway:DescribeUploadBuffer','storagegateway:DescribeVTLDevices','storagegateway:DescribeWorkingStorage','storagegateway:DisableGateway','storagegateway:ListFileShares','storagegateway:ListGateways','storagegateway:ListLocalDisks','storagegateway:ListTagsForResource','storagegateway:ListTapes','storagegateway:ListVolumeInitiators','storagegateway:ListVolumeRecoveryPoints','storagegateway:ListVolumes','storagegateway:RefreshCache','storagegateway:RemoveTagsFromResource','storagegateway:ResetCache','storagegateway:RetrieveTapeArchive','storagegateway:RetrieveTapeRecoveryPoint','storagegateway:SetLocalConsolePassword','storagegateway:ShutdownGateway','storagegateway:StartGateway','storagegateway:UpdateBandwidthRateLimit','storagegateway:UpdateChapCredentials','storagegateway:UpdateGatewayInformation','storagegateway:UpdateGatewaySoftwareNow','storagegateway:UpdateMaintenanceStartTime','storagegateway:UpdateNFSFileShare','storagegateway:UpdateSnapshotSchedule','storagegateway:UpdateVTLDeviceType','sts:AssumeRole','sts:AssumeRoleWithSAML','sts:AssumeRoleWithWebIdentity','sts:DecodeAuthorizationMessage','sts:GetCallerIdentity','sts:GetFederationToken','sts:GetSessionToken','support:AddAttachmentsToSet','support:AddCommunicationToCase','support:CreateCase','support:DescribeAttachment','support:DescribeCases','support:DescribeCommunications','support:DescribeServices','support:DescribeSeverityLevels','support:DescribeTrustedAdvisorCheckRefreshStatuses','support:DescribeTrustedAdvisorCheckResult','support:DescribeTrustedAdvisorChecks','support:DescribeTrustedAdvisorCheckSummaries','support:RefreshTrustedAdvisorCheck','support:ResolveCase','swf:CancelTimer','swf:CancelWorkflowExecution','swf:CompleteWorkflowExecution','swf:ContinueAsNewWorkflowExecution','swf:CountClosedWorkflowExecutions','swf:CountOpenWorkflowExecutions','swf:CountPendingActivityTasks','swf:CountPendingDecisionTasks','swf:DeprecateActivityType','swf:DeprecateDomain','swf:DeprecateWorkflowType','swf:DescribeActivityType','swf:DescribeDomain','swf:DescribeWorkflowExecution','swf:DescribeWorkflowType','swf:FailWorkflowExecution','swf:GetWorkflowExecutionHistory','swf:ListActivityTypes','swf:ListClosedWorkflowExecutions','swf:ListDomains','swf:ListOpenWorkflowExecutions','swf:ListWorkflowTypes','swf:PollForActivityTask','swf:PollForDecisionTask','swf:RecordActivityTaskHeartbeat','swf:RecordMarker','swf:RegisterActivityType','swf:RegisterDomain','swf:RegisterWorkflowType','swf:RequestCancelActivityTask','swf:RequestCancelExternalWorkflowExecution','swf:RequestCancelWorkflowExecution','swf:RespondActivityTaskCanceled','swf:RespondActivityTaskCompleted','swf:RespondActivityTaskFailed','swf:RespondDecisionTaskCompleted','swf:ScheduleActivityTask','swf:SignalExternalWorkflowExecution','swf:SignalWorkflowExecution','swf:StartChildWorkflowExecution','swf:StartTimer','swf:StartWorkflowExecution','swf:TerminateWorkflowExecution','tag:GetResources','tag:GetTagKeys','tag:GetTagValues','tag:TagResources','tag:UntagResources','transcribe:GetTranscriptionJob','transcribe:ListTranscriptionJobs','transcribe:StartTranscriptionJob','translate:TranslateText','trustedadvisor:DescribeCheckItems','trustedadvisor:DescribeCheckRefreshStatuses','trustedadvisor:DescribeCheckSummaries','trustedadvisor:DescribeNotificationPreferences','trustedadvisor:ExcludeCheckItems','trustedadvisor:IncludeCheckItems','trustedadvisor:RefreshCheck','trustedadvisor:UpdateNotificationPreferences','waf:CreateByteMatchSet','waf:CreateGeoMatchSet','waf:CreateIPSet','waf:CreateRateBasedRule','waf:CreateRegexMatchSet','waf:CreateRegexPatternSet','waf:CreateRule','waf:CreateSizeConstraintSet','waf:CreateSqlInjectionMatchSet','waf:CreateWebACL','waf:CreateXssMatchSet','waf:DeleteByteMatchSet','waf:DeleteGeoMatchSet','waf:DeleteIPSet','waf:DeleteRateBasedRule','waf:DeleteRegexMatchSet','waf:DeleteRegexPatternSet','waf:DeleteRule','waf:DeleteSizeConstraintSet','waf:DeleteSqlInjectionMatchSet','waf:DeleteWebACL','waf:DeleteXssMatchSet','waf:GetByteMatchSet','waf:GetChangeToken','waf:GetChangeTokenStatus','waf:GetGeoMatchSet','waf:GetIPSet','waf:GetRateBasedRule','waf:GetRateBasedRuleManagedKeys','waf:GetRegexMatchSet','waf:GetRegexPatternSet','waf:GetRule','waf:GetSampledRequests','waf:GetSizeConstraintSet','waf:GetSqlInjectionMatchSet','waf:GetWebACL','waf:GetXssMatchSet','waf:ListByteMatchSets','waf:ListGeoMatchSets','waf:ListIPSets','waf:ListRateBasedRules','waf:ListRegexMatchSets','waf:ListRegexPatternSets','waf:ListRules','waf:ListSizeConstraintSets','waf:ListSqlInjectionMatchSets','waf:ListWebACLs','waf:ListXssMatchSets','waf:UpdateByteMatchSet','waf:UpdateGeoMatchSet','waf:UpdateIPSet','waf:UpdateRateBasedRule','waf:UpdateRegexMatchSet','waf:UpdateRegexPatternSet','waf:UpdateRule','waf:UpdateSizeConstraintSet','waf:UpdateSqlInjectionMatchSet','waf:UpdateWebACL','waf:UpdateXssMatchSet','waf-regional:AssociateWebACL','waf-regional:CreateByteMatchSet','waf-regional:CreateGeoMatchSet','waf-regional:CreateIPSet','waf-regional:CreateRateBasedRule','waf-regional:CreateRegexMatchSet','waf-regional:CreateRegexPatternSet','waf-regional:CreateRule','waf-regional:CreateSizeConstraintSet','waf-regional:CreateSqlInjectionMatchSet','waf-regional:CreateWebACL','waf-regional:CreateXssMatchSet','waf-regional:DeleteByteMatchSet','waf-regional:DeleteGeoMatchSet','waf-regional:DeleteIPSet','waf-regional:DeleteRateBasedRule','waf-regional:DeleteRegexMatchSet','waf-regional:DeleteRegexPatternSet','waf-regional:DeleteRule','waf-regional:DeleteSizeConstraintSet','waf-regional:DeleteSqlInjectionMatchSet','waf-regional:DeleteWebACL','waf-regional:DeleteXssMatchSet','waf-regional:DisassociateWebACL','waf-regional:GetByteMatchSet','waf-regional:GetChangeToken','waf-regional:GetChangeTokenStatus','waf-regional:GetGeoMatchSet','waf-regional:GetIPSet','waf-regional:GetRateBasedRule','waf-regional:GetRateBasedRuleManagedKeys','waf-regional:GetRegexMatchSet','waf-regional:GetRegexPatternSet','waf-regional:GetRule','waf-regional:GetSampledRequests','waf-regional:GetSizeConstraintSet','waf-regional:GetSqlInjectionMatchSet','waf-regional:GetWebACL','waf-regional:GetWebACLForResource','waf-regional:GetXssMatchSet','waf-regional:ListByteMatchSets','waf-regional:ListGeoMatchSets','waf-regional:ListIPSets','waf-regional:ListRateBasedRules','waf-regional:ListRegexMatchSets','waf-regional:ListRegexPatternSets','waf-regional:ListResourcesForWebACL','waf-regional:ListRules','waf-regional:ListSizeConstraintSets','waf-regional:ListSqlInjectionMatchSets','waf-regional:ListWebACLs','waf-regional:ListXssMatchSets','waf-regional:UpdateByteMatchSet','waf-regional:UpdateGeoMatchSet','waf-regional:UpdateIPSet','waf-regional:UpdateRateBasedRule','waf-regional:UpdateRegexMatchSet','waf-regional:UpdateRegexPatternSet','waf-regional:UpdateRule','waf-regional:UpdateSizeConstraintSet','waf-regional:UpdateSqlInjectionMatchSet','waf-regional:UpdateWebACL','waf-regional:UpdateXssMatchSet','wam:AuthenticatePackager','workdocs:AbortDocumentVersionUpload','workdocs:ActivateUser','workdocs:AddResourcePermissions','workdocs:AddUserToGroup','workdocs:CheckAlias','workdocs:CreateFolder','workdocs:CreateInstance','workdocs:CreateNotificationSubscription','workdocs:CreateUser','workdocs:DeactivateUser','workdocs:DeleteDocument','workdocs:DeleteFolder','workdocs:DeleteFolderContents','workdocs:DeleteInstance','workdocs:DeleteNotificationSubscription','workdocs:DeleteUser','workdocs:DeregisterDirectory','workdocs:DescribeAvailableDirectories','workdocs:DescribeDocumentVersions','workdocs:DescribeFolderContents','workdocs:DescribeInstances','workdocs:DescribeNotificationSubscriptions','workdocs:DescribeResourcePermissions','workdocs:DescribeUsers','workdocs:GetDocument','workdocs:GetDocumentPath','workdocs:GetDocumentVersion','workdocs:GetFolder','workdocs:GetFolderPath','workdocs:InitiateDocumentVersionUpload','workdocs:RegisterDirectory','workdocs:RemoveAllResourcePermissions','workdocs:RemoveResourcePermission','workdocs:RemoveUserFromGroup','workdocs:UpdateDocument','workdocs:UpdateDocumentVersion','workdocs:UpdateFolder','workdocs:UpdateInstanceAlias','workdocs:UpdateUser','workmail:AddMembersToGroup','workmail:CreateGroup','workmail:CreateMailDomain','workmail:CreateMailUser','workmail:CreateOrganization','workmail:CreateResource','workmail:DeleteMailDomain','workmail:DeleteMobileDevice','workmail:DeleteOrganization','workmail:DescribeDirectories','workmail:DescribeKmsKeys','workmail:DescribeMailDomains','workmail:DescribeMailGroups','workmail:DescribeMailUsers','workmail:DescribeOrganizations','workmail:DisableMailGroups','workmail:DisableMailUsers','workmail:EnableMailDomain','workmail:EnableMailGroups','workmail:EnableMailUsers','workmail:GetMailDomainDetails','workmail:GetMailGroupDetails','workmail:GetMailUserDetails','workmail:GetMobileDeviceDetails','workmail:GetMobileDevicesForUser','workmail:GetMobilePolicyDetails','workmail:ListMembersInMailGroup','workmail:RemoveMembersFromGroup','workmail:ResetUserPassword','workmail:SearchMembers','workmail:SetAdmin','workmail:SetDefaultMailDomain','workmail:SetMailGroupDetails','workmail:SetMailUserDetails','workmail:SetMobilePolicyDetails','workmail:WipeMobileDevice','workspaces:CreateTags','workspaces:CreateWorkspaces','workspaces:DeleteTags','workspaces:DescribeTags','workspaces:DescribeWorkspaceBundles','workspaces:DescribeWorkspaceDirectories','workspaces:DescribeWorkspaces','workspaces:DescribeWorkspacesConnectionStatus','workspaces:ModifyWorkspaceProperties','workspaces:RebootWorkspaces','workspaces:RebuildWorkspaces','workspaces:StartWorkspaces','workspaces:StopWorkspaces','workspaces:TerminateWorkspaces','xray:BatchGetTraces','xray:GetServiceGraph','xray:GetTraceGraph','xray:GetTraceSummaries','xray:PutTelemetryRecords','xray:PutTraceSegments')]
        [String[]]
        $Action,
        [parameter(Mandatory,Position = 0,ParameterSetName = "Custom")]
        [String[]]
        $Custom
    )
    Process {
        foreach ($item in $PSBoundParameters[$PSCmdlet.ParameterSetName]) {
            $item
        }
    }
}

Export-ModuleMember -Function 'Add-PolicyAction'

function Add-UserData {
    <#
    .SYNOPSIS
        Adds UserData to a resource on the template. For single values (i.e. in AutoScaling Launch Configurations), it adds the single For multiple values, it automatically adds it as {"Fn::Base64": {"Fn::Join": ["",[VALUES...] ] } } to reduce the amount of scripting needed.

    .PARAMETER String
        An array of strings and/or Instrinsic Functions.

        IMPORTANT: You must specify new lines in Powershell syntax so it identifies it as a new line when converting to JSON via Export-Vaporshell. This will convert `n [backtick n] into \n [backslash n] in the resulting JSON template.

    .PARAMETER File
        The path of the script file to convert to UserData. This cannot contain any Intrinsic functions such as Ref in it. Use the String parameter if you'd like to include functions in the array.

    .PARAMETER Replace
        A hashtable of keys to replace in your UserData file with the corresponding values.

    .PARAMETER Persist
        If true and the UserData file does not already include it, adds the <persist>true</persist> tag to the end of the UserData file.

    .PARAMETER UseJoin
        If true, uses Fn::Join to add the UserData contents as an array of strings. If false or excluded, content is

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([UserData])]
    [cmdletbinding(DefaultParameterSetName="String")]
    Param(
        [parameter(Mandatory = $true,Position = 0,ParameterSetName="String")]
        [object]
        $String,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName="File")]
        [ValidateScript( {
                if (Test-Path $_) {
                    $true
                }
                else {
                    $PSCmdlet.ThrowTerminatingError((New-VSError -String "You must specify a valid file path -- unable to find the path $_"))
                }
            })]
        [string]
        $File,
        [parameter(Mandatory = $false)]
        [System.Collections.IDictionary]
        $Replace = @{},
        [parameter(Mandatory = $false)]
        [switch]
        $Persist,
        [parameter(Mandatory = $false)]
        [switch]
        $UseJoin
    )
    Process {
        $joined = switch ($PSBoundParameters.Keys) {
            'String' {
                if ($String -is [string]) {
                    [UserData]::Transform($Persist,$UseJoin,$Replace,$String)
                }
                else {
                    [UserData]::Transform($String)
                }
            }
            'File' {
                [UserData]::Transform($Persist,$UseJoin,$Replace,$File)
            }
        }
        $obj = [UserData]::new($joined)
        Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
        $obj
    }
}

Export-ModuleMember -Function 'Add-UserData'

function Add-VSTag {
    <#
    .SYNOPSIS
        Adds an Tag resource property to the template. You can use the Resource Tags property to apply tags to resources, which can help you identify and categorize those resources. You can tag only resources for which AWS CloudFormation supports tagging. For information about which resources you can tag with AWS CloudFormation, see the individual resources in AWS Resource and Property Types Reference: aws-template-resource-type-ref.md.

    .DESCRIPTION
        Adds an Tag resource property to the template.
You can use the Resource Tags property to apply tags to resources, which can help you identify and categorize those resources. You can tag only resources for which AWS CloudFormation supports tagging. For information about which resources you can tag with AWS CloudFormation, see the individual resources in AWS Resource and Property Types Reference: aws-template-resource-type-ref.md.

**Note**

Tagging implementations might vary by resource. For example, AWS::AutoScaling::AutoScalingGroup provides an additional, required PropagateAtLaunch property as part of its tagging scheme.

In addition to any tags you define, AWS CloudFormation automatically creates the following stack-level tags with the prefix aws::

+ aws:cloudformation:logical-id

+ aws:cloudformation:stack-id

+ aws:cloudformation:stack-name

The aws: prefix is reserved for AWS use. This prefix is case-insensitive. If you use this prefix in the Key or Value property, you cannot update or delete the tag. Tags with this prefix don't count toward the number of tags per resource.

All stack-level tags, including automatically created tags, are propagated to resources that AWS CloudFormation supports. Currently, tags are not propagated to Amazon EBS volumes that are created from block device mappings.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html

    .PARAMETER Key
        The key name of the tag. You can specify a value that is 1 to 128 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.

        Documentation: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html#cfn-resource-tags-key
        PrimitiveType: String
        UpdateType: Mutable

    .PARAMETER Value
        The value for the tag. You can specify a value that is 0 to 256 Unicode characters in length and cannot be prefixed with aws:. You can use any of the following characters: the set of Unicode letters, digits, whitespace, _, ., /, =, +, and -.

        Documentation: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-resource-tags.html#cfn-resource-tags-value
        PrimitiveType: String
        UpdateType: Mutable

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([VSTag])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory)]
        [object]
        $Key,
        [parameter(Mandatory)]
        [object]
        $Value
    )
    $obj = [VSTag]::new($PSBoundParameters)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n$($obj.ToJson() | Format-Json)"
    $obj
}

Export-ModuleMember -Function 'Add-VSTag'

function Add-VSChangeSetResourceToImport {
    <#
    .SYNOPSIS
    Creates a `Amazon.CloudFormation.Model.ResourceToImport` object to use for the value of parameter `ResourcesToImport` on `New-VSChangeSet`.

    .DESCRIPTION
    Creates a `Amazon.CloudFormation.Model.ResourceToImport` object to use for the value of parameter `ResourcesToImport` on `New-VSChangeSet`.

    .PARAMETER LogicalResourceId
    The LogicalId of the resource on the CloudFormation template in the Change Set.

    .PARAMETER ResourceIdentifier
    A hashtable containing information identifying the resource to import, e.g. `@{TableName = 'Games'}`.

    .PARAMETER ResourceType
    The CloudFormation resource type of the resource you would like to import.

    .EXAMPLE
    Add-VSChangeSetResourceToImport -LogicalResourceId GamesTable -ResourceIdentifier @{TableName = 'Games'} -ResourceType 'AWS::DynamoDB::Table'
    #>

    [CmdletBinding()]
    Param (
        [parameter(Mandatory)]
        [ValidateScript( {
            if ($_ -match "^[a-zA-Z0-9]*$") {
                $true
            }
            else {
                $PSCmdlet.ThrowTerminatingError((New-VSError -String 'The LogicalResourceId must be alphanumeric (a-z, A-Z, 0-9) and unique within the template.'))
            }
        })]
        [string]
        $LogicalResourceId,
        [parameter(Mandatory)]
        [hashtable]
        $ResourceIdentifier,
        [parameter(Mandatory)]
        [string]
        $ResourceType
    )
    Begin {
        Import-AWSSDK
        $identifier = New-Object 'System.Collections.Generic.Dictionary[string,string]'
        $resource = New-Object Amazon.CloudFormation.Model.ResourceToImport
        $ResourceIdentifier.GetEnumerator() | ForEach-Object {
            $identifier.Add($_.Key,$_.Value)
        }
    }
    Process {
        $resource.LogicalResourceId = $LogicalResourceId
        $resource.ResourceIdentifier = $identifier
        $resource.ResourceType = $ResourceType
        $resource
    }
}

Export-ModuleMember -Function 'Add-VSChangeSetResourceToImport'

function Get-TemplateValidation {
    <#
    .SYNOPSIS
    Confirms if a template is valid, throws an error if not.

    .PARAMETER TemplateBody
    String formatted body in either JSON or YAML.

    .PARAMETER Path
    The path to a local template file.

    .PARAMETER TemplateUrl
    Location of file containing the template body. The URL must point to a template (max size: 460,800 bytes) that is located in an Amazon S3 bucket.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody", ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "ValidateTemplate"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        switch ($PSCmdlet.ParameterSetName) {
            Path {
                $resolvedPath = (Resolve-Path $Path).Path
                $request.TemplateBody = [System.IO.File]::ReadAllText($resolvedPath)
            }
            TemplateBody {
                $request.TemplateBody = $TemplateBody
            }
            TemplateUrl {
                $request.TemplateUrl = $TemplateUrl
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-TemplateValidation'

function Get-VSAccountLimits {
    <#
    .SYNOPSIS
    Retrieves your account's AWS CloudFormation limits, such as the maximum number of stacks that you can create in your account.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = 'DescribeAccountLimits'
        $expand = 'AccountLimits'
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSAccountLimits'

function Get-VSChangeSet {
    <#
    .SYNOPSIS
    Gets the list of change sets for a stack or describes a specific change set.

    .PARAMETER Description
    Switch to get a change set description.

    .PARAMETER List
    Switch to get the list of change sets for a specific stack.

    .PARAMETER ChangeSetName
    The name or ID of the change set that you are trying to describe.

    .PARAMETER StackName
    The name or ID of the stack that you are trying to get change set info for.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Description")]
    Param
    (
        [parameter(ParameterSetName = "Description")]
        [Switch]
        $Description,
        [parameter(ParameterSetName = "ListChangeSets")]
        [Switch]
        $List,
        [parameter(Mandatory = $false,ParameterSetName = "Description")]
        [String]
        $ChangeSetName,
        [parameter(Mandatory = $true)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = switch ($PSCmdlet.ParameterSetName) {
            Description {
                'DescribeChangeSet'
            }
            ListChangeSets {
                'ListChangeSets'
            }
        }
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSChangeSet'

function Get-VSStack {
    <#
    .SYNOPSIS
    Gets info about a stack or list of stacks

    .DESCRIPTION
    Gets the description, policy, template, event list, resource info, resource list, exports info, imports info, summary or even estimated cost of a particular stack. This can also be used to list your stacks.

    .PARAMETER Description
    Returns the description for the specified stack; if no stack name was specified, then it returns the description for all the stacks created. If the stack does not exist, an AmazonCloudFormationException is returned.

    .PARAMETER List
    Returns the summary information for stacks whose status matches the specified StackStatusFilter. Summary information for stacks that have been deleted is kept for 90 days after the stack is deleted. If no StackStatusFilter is specified, summary information for all stacks is returned (including existing stacks and stacks that have been deleted).

    .PARAMETER Policy
    Returns the stack policy for a specified stack. If a stack doesn't have a policy, a null value is returned.

    .PARAMETER Template
    Returns the template body for a specified stack. You can get the template for running or deleted stacks.

    .PARAMETER Events
    Returns all stack related events for a specified stack in reverse chronological order.

    .PARAMETER Resource
    Returns a description of the specified resource in the specified stack.

    .PARAMETER Resources
    Returns AWS resource descriptions for running and deleted stacks. If StackName is specified, all the associated resources that are part of the stack are returned. If PhysicalResourceId is specified, the associated resources of the stack that the resource belongs to are returned.

    .PARAMETER ResourceList
    Returns descriptions of all resources of the specified stack.

    .PARAMETER ExportList
    Lists all exported output values in the account and region in which you call this action. Use this action to see the exported output values that you can import into other stacks. To import values, use the Fn::ImportValue function.

    .PARAMETER ImportList
    Lists all stacks that are importing an exported output value. To modify or remove an exported output value, first use this action to see which stacks are using it. To see the exported output values in your account, see ListExports.

    .PARAMETER Summary
    Returns information about a new or existing template. The GetTemplateSummary action is useful for viewing parameter information, such as default parameter values and parameter types, before you create or update a stack or stack set. You can use the GetTemplateSummary action when you submit a template, or you can get template information for a stack set, or a running or deleted stack. For deleted stacks, GetTemplateSummary returns the template information for up to 90 days after the stack has been deleted. If the template does not exist, a ValidationError is returned.

    .PARAMETER EstimatedCost
    Returns the estimated monthly cost of a template. The return value is an AWS Simple Monthly Calculator URL with a query string that describes the resources required to run the template.

    .PARAMETER StackId
    The Stack ID or Stack Name of the stack you are trying to get info for.

    .PARAMETER LogicalResourceId
    The logical name of the resource as specified in the template.

    .PARAMETER PhysicalResourceId
    The name or unique identifier that corresponds to a physical instance ID of a resource supported by AWS CloudFormation. For example, for an Amazon Elastic Compute Cloud (EC2) instance, PhysicalResourceId corresponds to the InstanceId. You can pass the EC2 InstanceId to DescribeStackResources to find which stack the instance belongs to and what other resources are part of the stack.

    .PARAMETER StackSetName
    The name or unique ID of the stack set from which the stack was created.

    .PARAMETER ExportName
    The name of the exported output value. AWS CloudFormation returns the stack names that are importing this value.

    .PARAMETER ChangeSetName
    The name or Amazon Resource Name (ARN) of a change set for which AWS CloudFormation returns the associated template. If you specify a name, you must also specify the StackName.

    .PARAMETER TemplateStage
    For templates that include transforms, the stage of the template that AWS CloudFormation returns. To get the user-submitted template, specify Original. To get the template after AWS CloudFormation has processed all transforms, specify Processed. If the template doesn't include transforms, Original and Processed return the same template. By default, AWS CloudFormation specifies Original.

    .PARAMETER TemplateBody
    Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes.

    .PARAMETER TemplateUrl
    Location of file containing the template body. The URL must point to a template that is located in an Amazon S3 bucket.

    .PARAMETER Path
    Absolute or relative file path to the template file you would like to summarize.

    .PARAMETER Parameters
    A list of Parameter structures that specify input parameters.

    .PARAMETER StackStatusFilter
    Stack status to use as a filter. Specify one or more stack status codes to list only stacks with the specified status codes. For a complete list of stack status codes, see the StackStatus parameter of the Stack data type.

    .PARAMETER Colorize
    Used with -Events to output colorized events.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .EXAMPLE
    # This gets the list of stacks.
    Get-VSStack -List

    .EXAMPLE
    # This gets the list of stacks and shows the events for each in color.
    Get-VSStack -List | Get-VSStack -Events -ColorizeEvents

    .EXAMPLE
    # This gets the description for a stack named 'testSAMdeployment'
    Get-VSStack -StackId testSAMdeployment

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Description")]
    Param
    (
        [parameter(ParameterSetName = "Description")]
        [Switch]
        $Description,
        [parameter(ParameterSetName = "ListStacks")]
        [Switch]
        $List,
        [parameter(ParameterSetName = "Policy")]
        [Switch]
        $Policy,
        [parameter(ParameterSetName = "Template")]
        [Switch]
        $Template,
        [parameter(ParameterSetName = "Events")]
        [Switch]
        $Events,
        [parameter(ParameterSetName = "Resource")]
        [Switch]
        $Resource,
        [parameter(ParameterSetName = "ResourcesPhysicalId")]
        [parameter(ParameterSetName = "ResourcesStackId")]
        [Switch]
        $Resources,
        [parameter(ParameterSetName = "ResourceList")]
        [Switch]
        $ResourceList,
        [parameter(ParameterSetName = "ExportList")]
        [Switch]
        $ExportList,
        [parameter(ParameterSetName = "ImportList")]
        [Switch]
        $ImportList,
        [parameter(ParameterSetName = "SummaryPath")]
        [parameter(ParameterSetName = "SummaryBody")]
        [parameter(ParameterSetName = "SummaryUrl")]
        [Switch]
        $Summary,
        [parameter(ParameterSetName = "EstimatedCostBody")]
        [parameter(ParameterSetName = "EstimatedCostUrl")]
        [parameter(ParameterSetName = "EstimatedCostPath")]
        [Switch]
        $EstimatedCost,
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "Events")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "Description")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "Policy")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "Template")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "SummaryPath")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "SummaryBody")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "SummaryUrl")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "Resource")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "ResourcesStackId")]
        [parameter(ValueFromPipelineByPropertyName = $true,ParameterSetName = "ResourceList")]
        [Alias("StackName")]
        [String]
        $StackId,
        [parameter(ParameterSetName = "Resource")]
        [parameter(ParameterSetName = "ResourcesPhysicalId")]
        [parameter(ParameterSetName = "ResourcesStackId")]
        [String]
        $LogicalResourceId,
        [parameter(ParameterSetName = "ResourcesPhysicalId")]
        [String]
        $PhysicalResourceId,
        [parameter(ParameterSetName = "SummaryPath")]
        [parameter(ParameterSetName = "SummaryBody")]
        [parameter(ParameterSetName = "SummaryUrl")]
        [String]
        $StackSetName,
        [parameter(ParameterSetName = "ImportList")]
        [String]
        $ExportName,
        [parameter(ParameterSetName = "Template")]
        [String]
        $ChangeSetName,
        [parameter(ParameterSetName = "Template")]
        [ValidateSet("Original","Processed")]
        [String]
        $TemplateStage,
        [parameter(ValueFromPipeline = $true,ParameterSetName = "EstimatedCostBody")]
        [parameter(ParameterSetName = "SummaryBody")]
        [String]
        $TemplateBody,
        [parameter(ParameterSetName = "EstimatedCostUrl")]
        [parameter(ParameterSetName = "SummaryUrl")]
        [String]
        $TemplateUrl,
        [parameter(ParameterSetName = "EstimatedCostPath")]
        [parameter(ParameterSetName = "SummaryPath")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(ParameterSetName = "EstimatedCostBody")]
        [parameter(ParameterSetName = "EstimatedCostUrl")]
        [parameter(ParameterSetName = "EstimatedCostPath")]
        [Amazon.CloudFormation.Model.Parameter[]]
        $Parameters,
        [parameter(ParameterSetName = "ListStacks")]
        [ValidateSet("CREATE_IN_PROGRESS","CREATE_FAILED","CREATE_COMPLETE","ROLLBACK_IN_PROGRESS","ROLLBACK_FAILED","ROLLBACK_COMPLETE","DELETE_IN_PROGRESS","DELETE_FAILED","DELETE_COMPLETE","UPDATE_IN_PROGRESS","UPDATE_COMPLETE_CLEANUP_IN_PROGRESS","UPDATE_COMPLETE","UPDATE_ROLLBACK_IN_PROGRESS","UPDATE_ROLLBACK_FAILED","UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS","UPDATE_ROLLBACK_COMPLETE","REVIEW_IN_PROGRESS")]
        [String]
        $StackStatusFilter,
        [parameter(ParameterSetName = "Events")]
        [Switch]
        $Colorize,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = switch ($PSCmdlet.ParameterSetName) {
            Description {
                'DescribeStacks'
                $expand = 'Stacks'
            }
            ListStacks {
                'ListStacks'
                $expand = 'StackSummaries'
            }
            Policy {
                'GetStackPolicy'
                $expand = 'StackPolicyBody'
            }
            Events {
                'DescribeStackEvents'
                $expand = 'StackEvents'
            }
            ResourceList {
                'ListStackResources'
            }
            ExportList {
                'ListExports'
            }
            ImportList {
                'ListImports'
            }
            Resource {
                'DescribeStackResource'
            }
            ResourcesPhysicalId {
                'DescribeStackResources'
            }
            ResourcesStackId {
                'DescribeStackResources'
            }
            EstimatedCostBody {
                'EstimateTemplateCost'
                $expand = 'Url'
            }
            EstimatedCostUrl {
                'EstimateTemplateCost'
                $expand = 'Url'
            }
            EstimatedCostPath {
                'EstimateTemplateCost'
                $expand = 'Url'
            }
            SummaryPath {
                'GetTemplateSummary'
            }
            SummaryBody {
                'GetTemplateSummary'
            }
            SummaryUrl {
                'GetTemplateSummary'
            }
            Template {
                'GetTemplate'
            }
        }
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                StackId {
                    $request.StackName = $StackId
                }
                TemplateStage {
                    $request.TemplateStage = [Amazon.CloudFormation.TemplateStage]::$TemplateStage
                }
                Path {
                    $request.TemplateBody = [System.IO.File]::ReadAllText((Resolve-Path $Path))
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            Write-Error $results
        }
        elseif ($results) {
            if ($Colorize) {
                $strings = @()
                $strings += "`nSTACK ID : $($results.StackId[0])`n"
                $stack = $results | Sort-Object timestamp | Select-Object Timestamp,ResourceStatus,StackName,ResourceType,ResourceStatusReason | Format-Table -AutoSize
                $strings += ($stack | Out-String) -split "`n" | Where-Object {$_}
                Colorize $strings
            }
            else {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Get-VSStack'

function Get-VSStackSet {
    <#
    .SYNOPSIS
    Gets information about a Stack Set

    .DESCRIPTION
    Gets information about a Stack Set

    .PARAMETER Description
    Returns the description of the specified stack set.

    .PARAMETER List
    Returns summary information about stack sets that are associated with the user.

    .PARAMETER StackSetName
    The name or the unique stack ID of the stack set that you want to get information for.

    .PARAMETER MaxResults
    The maximum number of results to be returned with a single call.

    .PARAMETER Status
    The status of the stack sets that you want to get summary information about.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Description")]
    Param
    (
        [parameter(ParameterSetName = "Description")]
        [Switch]
        $Description,
        [parameter(ParameterSetName = "ListStackSets")]
        [Switch]
        $List,
        [parameter(Mandatory = $false)]
        [String]
        $StackSetName,
        [parameter(ParameterSetName = "ListStackSets")]
        [System.Int32]
        $MaxResults,
        [parameter(ParameterSetName = "ListStackSets")]
        [ValidateSet("ACTIVE","DELETED")]
        [String]
        $Status,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = switch ($PSCmdlet.ParameterSetName) {
            Description {
                'DescribeStackSet'
                $expand = 'StackSet'
            }
            ListStackSets {
                'ListStackSets'
                $expand = 'Summaries'
            }
        }
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Status {
                    $request.Status = [Amazon.CloudFormation.StackSetStatus]::$Status
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSStackSet'

function Get-VSStackSetInstance {
    <#
    .SYNOPSIS
    Gets information about an Instance of a Stack Set

    .DESCRIPTION
    Gets information about an Instance of a Stack Set

    .PARAMETER Description
    Returns the stack instance that's associated with the specified stack set, AWS account, and region.

    .PARAMETER List
    Returns summary information about stack instances that are associated with the specified stack set. You can filter for stack instances that are associated with a specific AWS account name or region.

    .PARAMETER StackInstanceAccount
    The name of the AWS account that you want to list stack instances for.

    .PARAMETER StackInstanceRegion
    The name of the region where you want to list stack instances.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to list stack instances for.

    .PARAMETER MaxResults
    The maximum number of results to be returned with a single call.

    .PARAMETER Status
    The status of the stack sets that you want to get summary information about.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Description")]
    Param
    (
        [parameter(ParameterSetName = "Description")]
        [Switch]
        $Description,
        [parameter(ParameterSetName = "ListStackInstances")]
        [Switch]
        $List,
        [parameter(Mandatory = $false)]
        [String]
        $StackInstanceAccount,
        [parameter(Mandatory = $false)]
        [String]
        $StackInstanceRegion,
        [parameter(Mandatory = $false)]
        [String]
        $StackSetName,
        [parameter(ParameterSetName = "ListStackInstances")]
        [System.Int32]
        $MaxResults,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = switch ($PSCmdlet.ParameterSetName) {
            Description {
                'DescribeStackInstance'
                $expand = 'StackInstance'
            }
            ListStackInstances {
                'ListStackInstances'
                $expand = 'Summaries'
            }
        }
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSStackSetInstance'

function Get-VSStackSetOperation {
    <#
    .SYNOPSIS
    Gets information about Stack Set Operations

    .DESCRIPTION
    Gets information about Stack Set Operations

    .PARAMETER Description
    Returns the description of the specified stack set operation.

    .PARAMETER List
    Returns summary information about operations performed on a stack set.

    .PARAMETER ListResults
    Returns summary information about the results of a stack set operation.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to list stack instances for.

    .PARAMETER OperationId
    The unique ID of the stack set operation.

    .PARAMETER MaxResults
    The maximum number of results to be returned with a single call.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Description")]
    Param
    (
        [parameter(ParameterSetName = "Description")]
        [Switch]
        $Description,
        [parameter(ParameterSetName = "ListStackSetOperations")]
        [Switch]
        $List,
        [parameter(ParameterSetName = "ListStackSetOperationResults")]
        [Switch]
        $ListResults,
        [parameter(Mandatory = $true)]
        [String]
        $StackSetName,
        [parameter(ParameterSetName = "Description")]
        [parameter(ParameterSetName = "ListStackSetOperationResults")]
        [String]
        $OperationId,
        [parameter(ParameterSetName = "ListStackSetOperations")]
        [parameter(ParameterSetName = "ListStackSetOperationResults")]
        [System.Int32]
        $MaxResults,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = switch ($PSCmdlet.ParameterSetName) {
            Description {
                'DescribeStackSetOperation'
                $expand = 'StackSetOperation'
            }
            ListStackSetOperations {
                'ListStackSetOperations'
                $expand = 'Summaries'
            }
            ListStackSetOperationResults {
                'ListStackSetOperationResults'
                $expand = 'Summaries'
            }
        }
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Status {
                    $request.Status = [Amazon.CloudFormation.StackSetStatus]::$Status
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSStackSetOperation'

function Invoke-VSChangeSetExecution {
    <#
    .SYNOPSIS
    Executes a Change Set

    .DESCRIPTION
    Executes a Change Set

    .PARAMETER ChangeSetName
    The name or ARN of the change set that you want use to update the specified stack.

    .PARAMETER StackName
    If you specified the name of a change set, specify the stack name or ID (ARN) that is associated with the change set you want to execute.

    .PARAMETER ClientRequestToken
    A unique identifier for this ExecuteChangeSet request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to execute a change set to update a stack with the same name. You might retry ExecuteChangeSet requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER Watch
    If $true, runs Watch-Stack to show the colorized output of the stack events.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true)]
        [String]
        $ChangeSetName,
        [parameter(Mandatory = $true)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [switch]
        $Watch,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = 'ExecuteChangeSet'
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            $results
            if ($Watch) {
                Write-Verbose "Watching Stack!"
                $results | Watch-Stack -ProfileName $ProfileName -Verbose:$false
            }
        }
    }
}

Export-ModuleMember -Function 'Invoke-VSChangeSetExecution'

function New-VSChangeSet {
    <#
    .SYNOPSIS
    Creates a new Change Set.

    .DESCRIPTION
    Creates a new Change Set for the specified StackName.

    .PARAMETER TemplateBody
    The body of the template to be used for this change set.

    .PARAMETER Path
    The path to the template file to be used with this change set.

    .PARAMETER TemplateUrl
    The S3 Url to the template file to be used with this change set.

    .PARAMETER UsePreviousTemplate
    A flag to indicate that this change set should use the previous template.

    .PARAMETER ChangeSetName
    The name of the change set. The name must be unique among all change sets that are associated with the specified stack. A change set name can contain only alphanumeric, case sensitive characters and hyphens. It must start with an alphabetic character and cannot exceed 128 characters.

    .PARAMETER StackName
    The name or the unique ID of the stack for which you are creating a change set. AWS CloudFormation generates the change set by comparing this stack's information with the information that you submit, such as a modified template or different parameter input values.

    .PARAMETER Capabilities
    A list of values that you must specify before AWS CloudFormation can update certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, and AWS::IAM::UserToGroupAddition. If your stack template contains these resources, we recommend that you review all permissions associated with them and edit their permissions if necessary. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error.

    .PARAMETER ChangeSetType
    The type of change set operation. To create a change set for a new stack, specify CREATE. To create a change set for an existing stack, specify UPDATE.

    .PARAMETER ClientToken
    A unique identifier for this CreateChangeSet request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to create another change set with the same name. You might retry CreateChangeSet requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER Description
    A description to help you identify this change set.

    .PARAMETER NotificationARNs
    The Amazon Resource Names (ARNs) of Amazon Simple Notification Service (Amazon SNS) topics that AWS CloudFormation associates with the stack. To remove all associated notification topics, specify an empty list.

    .PARAMETER Parameters
    A list of Parameter structures that specify input parameters for the change set

    .PARAMETER ResourcesToImport
    The resources to import into your stack. Use the helper function `Add-VSChangeSetResourceToImport` to create the `Amazon.CloudFormation.Model.ResourceToImport` objects.

    https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resource-import-existing-stack.html

    .PARAMETER ResourceTypes
    The template resource types that you have permissions to work with if you execute this change set, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance. If the list of resource types doesn't include a resource type that you're updating, the stack update fails. By default, AWS CloudFormation grants permissions to all resource types. AWS Identity and Access Management (IAM) uses this parameter for condition keys in IAM policies for AWS CloudFormation.

    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes when executing the change set. AWS CloudFormation uses the role's credentials to make calls on your behalf. AWS CloudFormation uses this role for all future operations on the stack. As long as users have permission to operate on the stack, AWS CloudFormation uses this role even if the users don't have permission to pass it. Ensure that the role grants least privilege. If you don't specify a value, AWS CloudFormation uses the role that was previously associated with the stack. If no role is available, AWS CloudFormation uses a temporary session that is generated from your user credentials.

    .PARAMETER Tags
    Key-value pairs to associate with this stack. AWS CloudFormation also propagates these tags to resources in the stack. You can specify a maximum of 50 tags.

    .PARAMETER Watch
    If $true, runs Watch-Stack to show the colorized output of the stack events.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .PARAMETER Force
    If $true, executes the change set as soon as it's ready.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "UsePreviousTemplate")]
        [Switch]
        $UsePreviousTemplate,
        [parameter(Mandatory = $true)]
        [Alias('StackId')]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [ValidateScript({
            if ($_.Length -gt 128 -or $_ -notmatch "^[a-zA-Z][-a-zA-Z0-9]+$") {
                throw "A change set name can contain only alphanumeric, case sensitive characters and hyphens. It must start with an alphabetic character and cannot exceed 128 characters."
            }
            else {
                $true
            }
        })]
        [String]
        $ChangeSetName,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.ChangeSetType]
        [ValidateSet('CREATE','IMPORT','UPDATE')]
        $ChangeSetType = 'CREATE',
        [parameter(Mandatory = $false)]
        [Alias('ClientToken')]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String]
        $Description,
        [parameter(Mandatory = $false)]
        [String[]]
        $NotificationARNs,
        [parameter(Mandatory = $false)]
        [ValidateScript( {
                $allowedTypes = "System.Management.Automation.PSCustomObject","Amazon.CloudFormation.Model.Parameter","System.Collections.Hashtable"
                if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") {
                    $true
                }
                else {
                    $PSCmdlet.ThrowTerminatingError((New-VSError -String "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")."))
                }
            })]
        $Parameters,
        [parameter()]
        [Amazon.CloudFormation.Model.ResourceToImport[]]
        $ResourcesToImport,
        [parameter(Mandatory = $false)]
        [String[]]
        $ResourceTypes,
        [parameter(Mandatory = $false)]
        [String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [Hashtable]
        $Tags,
        [parameter(Mandatory = $false)]
        [switch]
        $Watch,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE,
        [parameter(Mandatory = $false)]
        [switch]
        $Force
    )
    Begin {
        Import-AWSSDK
        $tagList = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.Tag]'
        $tagList.Add((VSStackTag -Key BuiltWith -Value VaporShell))
        if (-not $PSBoundParameters.ContainsKey('ChangeSetName')) {
            $ChangeSetName = "$StackName-$(Get-Date -Format "yyyy-MM-dd-HH-mm-ss")"
            $PSBoundParameters['ChangeSetName'] = $ChangeSetName
        }
    }
    Process {
        if ($PSBoundParameters.Keys -notcontains "Description") {
            $PSBoundParameters["Description"] = "Change set created with VaporShell on $((Get-Date).ToString()) by $env:USERNAME"
        }
        $method = "CreateChangeSet"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        $request.ChangeSetType = [Amazon.CloudFormation.ChangeSetType]::FindValue($ChangeSetType)
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                ClientRequestToken {
                    $request.ClientToken = $ClientRequestToken
                }
                Path {
                    $request.TemplateBody = [System.IO.File]::ReadAllText((Resolve-Path $Path))
                }
                ResourcesToImport {
                    $rToI = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.ResourceToImport]'
                    foreach ($res in $ResourcesToImport) {
                        $rToI.Add($res)
                    }
                    $request.ResourcesToImport = $rToI
                }
                Parameters {
                    if ($Parameters[0] -is [Amazon.CloudFormation.Model.Parameter]) {
                        $request.Parameters = $Parameters
                    }
                    elseif ($Parameters -is [System.Collections.Hashtable]) {
                        $parRay = @()
                        foreach ($parKey in $Parameters.Keys) {
                            $parRay += VSStackParameter -ParameterKey $parKey -ParameterValue $Parameters[$parKey]
                        }
                        $request.Parameters = $parRay
                    }
                    elseif ($Parameters -is [System.Management.Automation.PSCustomObject]) {
                        $parRay = @()
                        foreach ($parProp in $Parameters.PSObject.Properties.Name) {
                            $parRay += VSStackParameter -ParameterKey $parProp -ParameterValue $Parameters.$parProp
                        }
                        $request.Parameters = $parRay
                    }
                }
                Tags {
                    foreach ($key in $Tags.Keys) {
                        $tagList.Add((VSStackTag -Key $key -Value $Tags[$key]))
                    }
                }
                ChangeSetType {}
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $request.Tags = $tagList
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            $results
            if ($Force) {
                $execParams = @{
                    ChangeSetName = $ChangeSetName
                    StackName = $StackName
                    ProfileName = $ProfileName
                }
                if ($PSBoundParameters.ContainsKey('ClientRequestToken')) {
                    $execParams['ClientRequestToken'] = $PSBoundParameters['ClientRequestToken']
                }
                Write-Verbose "Waiting until Change Set is ready..."
                $i = 0
                do {
                    Start-Sleep 2
                    $csStatus = Get-VSChangeSet -ChangeSetName $ChangeSetName -StackName $StackName -Verbose:$false
                    $i++
                }
                until ($csStatus.ExecutionStatus -eq 'AVAILABLE' -or $i -ge 150)
                Write-Verbose "Executing Change Set!"
                Invoke-VSChangeSetExecution @execParams
            }
            if ($Watch) {
                Write-Verbose "Watching Stack!"
                $results | Watch-Stack -ProfileName $ProfileName -Verbose:$false
            }
        }
    }
}

Export-ModuleMember -Function 'New-VSChangeSet'

function New-VSStack {
    <#
    .SYNOPSIS
    Creates a new stack

    .PARAMETER TemplateBody
    Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes.

    .PARAMETER Path
    File path to the local template file.

    .PARAMETER TemplateUrl
    Location of file containing the template body. The URL must point to a template (max size: 460,800 bytes) that is located in an Amazon S3 bucket

    .PARAMETER StackName
    The name that is associated with the stack. The name must be unique in the region in which you are creating the stack. A stack name can contain only alphanumeric characters (case sensitive) and hyphens. It must start with an alphabetic character and cannot be longer than 128 characters.

    .PARAMETER Capabilities
    A list of values that you must specify before AWS CloudFormation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, and AWS::IAM::UserToGroupAddition. If your stack template contains these resources, we recommend that you review all permissions associated with them and edit their permissions if necessary. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error.

    .PARAMETER ClientRequestToken
    A unique identifier for this CreateStack request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to create a stack with the same name. You might retry CreateStack requests to ensure that AWS CloudFormation successfully received them. All events triggered by a given stack operation are assigned the same client request token, which you can use to track operations. For example, if you execute a CreateStack operation with the token token1, then all the StackEvents generated by that operation will have ClientRequestToken set as token1. In the console, stack operations display the client request token on the Events tab. Stack operations that are initiated from the console use the token format Console-StackOperation-ID, which helps you easily identify the stack operation . For example, if you create a stack using the console, each stack event would be assigned the same token in the following format: Console-CreateStack-7f59c3cf-00d2-40c7-b2ff-e75db0987002.

    .PARAMETER DisableRollback
    Set to true to disable rollback of the stack if stack creation failed. You can specify either DisableRollback or OnFailure, but not both. Default: false

    .PARAMETER NotificationARNs
    The Simple Notification Service (SNS) topic ARNs to publish stack related events. You can find your SNS topic ARNs using the SNS console or your Command Line Interface (CLI).

    .PARAMETER OnFailure
    Determines what action will be taken if stack creation fails. This must be one of: DO_NOTHING, ROLLBACK, or DELETE. You can specify either OnFailure or DisableRollback, but not both. Default: ROLLBACK

    .PARAMETER Parameters
    A list of Parameter structures that specify input parameters for the stack.

    .PARAMETER ResourceTypes
    The template resource types that you have permissions to work with for this create stack action, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance. Use the following syntax to describe template resource types: AWS::* (for all AWS resource), Custom::* (for all custom resources), Custom::logical_ID (for a specific custom resource), AWS::service_name::* (for all resources of a particular AWS service), and AWS::service_name::resource_logical_ID (for a specific AWS resource). If the list of resource types doesn't include a resource that you're creating, the stack creation fails. By default, AWS CloudFormation grants permissions to all resource types. AWS Identity and Access Management (IAM) uses this parameter for AWS CloudFormation-specific condition keys in IAM policies.

    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to create the stack. AWS CloudFormation uses the role's credentials to make calls on your behalf. AWS CloudFormation always uses this role for all future operations on the stack. As long as users have permission to operate on the stack, AWS CloudFormation uses this role even if the users don't have permission to pass it. Ensure that the role grants least privilege. If you don't specify a value, AWS CloudFormation uses the role that was previously associated with the stack. If no role is available, AWS CloudFormation uses a temporary session that is generated from your user credentials.

    .PARAMETER StackPolicyBody
    Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes.

    .PARAMETER StackPolicyURL
    Location of file containing the template body. The URL must point to a template (max size: 460,800 bytes) that is located in an Amazon S3 bucket.

    .PARAMETER Tags
    A hashtable containing key-value pairs to associate with this stack. AWS CloudFormation also propagates these tags to resources in the stack. You can specify a maximum of 50 tags.

    .PARAMETER TimeoutInMinutes
    The amount of time that can pass before the stack status becomes CREATE_FAILED; if DisableRollback is not set or is set to false, the stack will be rolled back.

    .PARAMETER Watch
    If $true, runs Watch-Stack to show the colorized output of the stack events.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .PARAMETER Force
    If $true, bypasses ShouldProcess and creates the stack. If the stack already exists and a change set is created instead, the change set will also be executed as soon as it's ready.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path",SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [System.Boolean]
        $DisableRollback,
        [parameter(Mandatory = $false)]
        [String[]]
        $NotificationARNs,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.OnFailure]
        $OnFailure,
        [parameter(Mandatory = $false)]
        [ValidateScript( {
                $allowedTypes = "System.Management.Automation.PSCustomObject","Amazon.CloudFormation.Model.Parameter","System.Collections.Hashtable"
                if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") {
                    $true
                }
                else {
                    $PSCmdlet.ThrowTerminatingError((New-VSError -String "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")."))
                }
            })]
        $Parameters,
        [parameter(Mandatory = $false)]
        [String[]]
        $ResourceTypes,
        [parameter(Mandatory = $false)]
        [String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyBody,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyURL,
        [parameter(Mandatory = $false)]
        [Hashtable]
        $Tags,
        [parameter(Mandatory = $false)]
        [Int]
        $TimeoutInMinutes,
        [parameter(Mandatory = $false)]
        [switch]
        $Watch,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE,
        [parameter(Mandatory = $false)]
        [switch]
        $Force
    )
    Begin {
        Import-AWSSDK
        $tagList = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.Tag]'
        $tagList.Add((VSStackTag -Key BuiltWith -Value VaporShell))
    }
    Process {
        Write-Verbose "Checking if Stack '$StackName' exists and creating a Change Set instead if so."
        $stackExists = try {
            $test = Get-VSStack -StackName $StackName -ErrorAction Stop
            if ($test | Select-Object -ExpandProperty StackName) {
                $true
            }
            else {
                $false
            }
        }
        catch {
            $false
        }
        if ($stackExists) {
            $changeSetParams = $PSBoundParameters
            @('DisableRollback','OnFailure','StackPolicyBody','StackPolicyURL','TimeoutInMinutes','Confirm') | ForEach-Object {
                if ($changeSetParams.ContainsKey($_)) {
                    $changeSetParams.Remove($_)
                }
            }
            Write-Verbose "A Stack with name '$StackName' already exists -- creating a Change Set instead."
            New-VSChangeSet @changeSetParams
        }
        else {
            Write-Verbose "Stack '$StackName' not found -- creating the Stack now."
            $method = "CreateStack"
            $requestType = "Amazon.CloudFormation.Model.$($method)Request"
            $request = New-Object $requestType
            foreach ($key in $PSBoundParameters.Keys) {
                switch ($key) {
                    Path {
                        $resolvedPath = (Resolve-Path $Path).Path
                        $request.TemplateBody = [System.IO.File]::ReadAllText($resolvedPath)
                    }
                    Parameters {
                        if ($Parameters[0] -is [Amazon.CloudFormation.Model.Parameter]) {
                            $request.Parameters = $Parameters
                        }
                        elseif ($Parameters -is [System.Collections.Hashtable]) {
                            $parRay = @()
                            foreach ($parKey in $Parameters.Keys) {
                                $parRay += VSStackParameter -ParameterKey $parKey -ParameterValue $Parameters[$parKey]
                            }
                            $request.Parameters = $parRay
                        }
                        elseif ($Parameters -is [System.Management.Automation.PSCustomObject]) {
                            $parRay = @()
                            foreach ($parProp in $Parameters.PSObject.Properties.Name) {
                                $parRay += VSStackParameter -ParameterKey $parProp -ParameterValue $Parameters.$parProp
                            }
                            $request.Parameters = $parRay
                        }
                    }
                    Tags {
                        foreach ($key in $Tags.Keys) {
                            $tagList.Add((VSStackTag -Key $key -Value $Tags[$key]))
                        }
                    }
                    Default {
                        if ($request.PSObject.Properties.Name -contains $key) {
                            $request.$key = $PSBoundParameters[$key]
                        }
                    }
                }
            }
            $request.Tags = $tagList
            if ($Force -or $PSCmdlet.ShouldProcess($request)) {
                $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
                if (!$results) {
                    return
                }
                elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                    $PSCmdlet.ThrowTerminatingError($results)
                }
                elseif ($results) {
                    $results
                    if ($Watch) {
                        Write-Verbose "Watching Stack!"
                        $results | Watch-Stack -ProfileName $ProfileName -Verbose:$false
                    }
                }
            }
        }
    }
}

Export-ModuleMember -Function 'New-VSStack'

function New-VSStackSet {
    <#
    .SYNOPSIS
    Creates a new Stack Set

    .PARAMETER TemplateBody
    Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes.

    .PARAMETER Path
    File path to the local template file.

    .PARAMETER TemplateUrl
    Location of file containing the template body. The URL must point to a template (max size: 460,800 bytes) that is located in an Amazon S3 bucket

    .PARAMETER StackSetName
    The name to associate with the stack set. The name must be unique in the region where you create your stack set. A stack name can contain only alphanumeric characters (case-sensitive) and hyphens. It must start with an alphabetic character and can't be longer than 128 characters.

    .PARAMETER Capabilities
    A list of values that you must specify before AWS CloudFormation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, and AWS::IAM::UserToGroupAddition. If your stack template contains these resources, we recommend that you review all permissions associated with them and edit their permissions if necessary. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error.

    .PARAMETER ClientRequestToken
    A unique identifier for this CreateStackSet request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to create another stack set with the same name. You might retry CreateStackSet requests to ensure that AWS CloudFormation successfully received them.
If you don't specify an operation ID, the SDK generates one automatically.

    .PARAMETER Description
    A description of the stack set. You can use the description to identify the stack set's purpose or other important information.

    .PARAMETER Parameters
    The input parameters for the stack set template.

    .PARAMETER Tags
    Key-value pairs to associate with this stack. AWS CloudFormation also propagates these tags to resources in the stack. You can specify a maximum of 50 tags.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path",SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $true)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String]
        $Description,
        [parameter(Mandatory = $false)]
        [ValidateScript( {
                $allowedTypes = "System.Management.Automation.PSCustomObject","Amazon.CloudFormation.Model.Parameter","System.Collections.Hashtable"
                if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") {
                    $true
                }
                else {
                    $PSCmdlet.ThrowTerminatingError((New-VSError -String "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")."))
                }
            })]
        $Parameters,
        [parameter(Mandatory = $false)]
        [Hashtable]
        $Tags,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
        $tagList = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.Tag]'
        $tagList.Add((VSStackTag -Key BuiltWith -Value VaporShell))
    }
    Process {
        if ($PSBoundParameters.Keys -notcontains "Description") {
            $PSBoundParameters["Description"] = "Stack set created with VaporShell on $((Get-Date).ToString()) by $env:USERNAME"
        }
        $method = "CreateStackSet"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Path {
                    $request.TemplateBody = [System.IO.File]::ReadAllText((Resolve-Path $Path))
                }
                Parameters {
                    if ($Parameters[0] -is [Amazon.CloudFormation.Model.Parameter]) {
                        $request.Parameters = $Parameters
                    }
                    elseif ($Parameters -is [System.Collections.Hashtable]) {
                        $parRay = @()
                        foreach ($parKey in $Parameters.Keys) {
                            $parRay += VSStackParameter -ParameterKey $parKey -ParameterValue $Parameters[$parKey]
                        }
                        $request.Parameters = $parRay
                    }
                    elseif ($Parameters -is [System.Management.Automation.PSCustomObject]) {
                        $parRay = @()
                        foreach ($parProp in $Parameters.PSObject.Properties.Name) {
                            $parRay += VSStackParameter -ParameterKey $parProp -ParameterValue $Parameters.$parProp
                        }
                        $request.Parameters = $parRay
                    }
                }
                Tags {
                    foreach ($key in $Tags.Keys) {
                        $tagList.Add((VSStackTag -Key $key -Value $Tags[$key]))
                    }
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $request.Tags = $tagList
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'New-VSStackSet'

function New-VSStackSetInstances {
    <#
    .SYNOPSIS
    Creates new Stack Set Instances.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to create stack instances from.

    .PARAMETER Accounts
    The names of one or more AWS accounts that you want to create stack instances in the specified region(s) for.

    .PARAMETER OperationId
    The unique identifier for this stack set operation. The operation ID also functions as an idempotency token, to ensure that AWS CloudFormation performs the stack set operation only once, even if you retry the request multiple times. You might retry stack set operation requests to ensure that AWS CloudFormation successfully received them. If you don't specify an operation ID, the SDK generates one automatically. Repeating this stack set operation with a new operation ID retries all stack instances whose status is OUTDATED.

    .PARAMETER OperationPreferences
    Preferences for how AWS CloudFormation performs this stack set operation.

    .PARAMETER Regions
    The names of one or more regions where you want to create stack instances using the specified AWS account(s).

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [String[]]
        $Accounts,
        [parameter(Mandatory = $false)]
        [String]
        $OperationId,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.Model.StackSetOperationPreferences]
        $OperationPreferences,
        [parameter(Mandatory = $false)]
        [String[]]
        $Regions,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "CreateStackInstances"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'New-VSStackSetInstances'

function Remove-VSChangeSet {
    <#
    .SYNOPSIS
    Removes a Change Set.

    .PARAMETER ChangeSetName
    The name of the change set. The name must be unique among all change sets that are associated with the specified stack. A change set name can contain only alphanumeric, case sensitive characters and hyphens. It must start with an alphabetic character and cannot exceed 128 characters.

    .PARAMETER StackName
    The name or the unique ID of the stack for which you are creating a change set. AWS CloudFormation generates the change set by comparing this stack's information with the information that you submit, such as a modified template or different parameter input values.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $ChangeSetName,
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "DeleteChangeSet"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Remove-VSChangeSet'

function Remove-VSStack {
    <#
    .SYNOPSIS
    Removes a Stack.

    .PARAMETER StackName
    The name or the unique stack ID that is associated with the stack.

    .PARAMETER ClientRequestToken
    A unique identifier for this DeleteStack request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to delete a stack with the same name. You might retry DeleteStack requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER RetainResources
    For stacks in the DELETE_FAILED state, a list of resource logical IDs that are associated with the resources you want to retain. During deletion, AWS CloudFormation deletes the stack but does not delete the retained resources. Retaining resources is useful when you cannot delete a resource, such as a non-empty S3 bucket, but you want to delete the stack.

    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to delete the stack. AWS CloudFormation uses the role's credentials to make calls on your behalf. If you don't specify a value, AWS CloudFormation uses the role that was previously associated with the stack. If no role is available, AWS CloudFormation uses a temporary session that is generated from your user credentials.

    .PARAMETER Watch
    If $true, runs Watch-Stack to show the colorized output of the stack events.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)]
        [Alias("StackId")]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String[]]
        $RetainResources,
        [parameter(Mandatory = $false)]
        [String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [switch]
        $Watch,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "DeleteStack"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                if ($Watch) {
                    Write-Verbose "Watching Stack!"
                    Watch-Stack -StackName $StackName -ProfileName $ProfileName -Verbose:$false
                }
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                $results
                if ($Watch) {
                    Write-Verbose "Watching Stack!"
                    Watch-Stack -StackName $StackName -ProfileName $ProfileName -Verbose:$false
                }
            }
        }
    }
}

Export-ModuleMember -Function 'Remove-VSStack'

function Remove-VSStackSet {
    <#
    .SYNOPSIS
    Removes a Stack Set.

    .PARAMETER StackName
    The name or the unique stack ID that is associated with the stack.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "DeleteStackSet"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType -Property @{StackSetName = $StackSetName}
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Remove-VSStackSet'

function Remove-VSStackSetInstances {
    <#
    .SYNOPSIS
    Removes Stack Set Instances.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to delete stack instances for.

    .PARAMETER Accounts
    The names of the AWS accounts that you want to delete stack instances for.

    .PARAMETER OperationId
    The unique identifier for this stack set operation. If you don't specify an operation ID, the SDK generates one automatically. The operation ID also functions as an idempotency token, to ensure that AWS CloudFormation performs the stack set operation only once, even if you retry the request multiple times. You can retry stack set operation requests to ensure that AWS CloudFormation successfully received them. Repeating this stack set operation with a new operation ID retries all stack instances whose status is OUTDATED.

    .PARAMETER OperationPreferences
    Preferences for how AWS CloudFormation performs this stack set operation.

    .PARAMETER Regions
    The regions where you want to delete stack set instances.

    .PARAMETER RetainStacks
    Removes the stack instances from the specified stack set, but doesn't delete the stacks. You can't reassociate a retained stack or add an existing, saved stack to a new stack set.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [String[]]
        $Accounts,
        [parameter(Mandatory = $false)]
        [String]
        $OperationId,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.Model.StackSetOperationPreferences]
        $OperationPreferences,
        [parameter(Mandatory = $false)]
        [String[]]
        $Regions,
        [parameter(Mandatory = $false)]
        [System.Boolean]
        $RetainStacks,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "DeleteStackInstances"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Remove-VSStackSetInstances'

function Resume-VSStackUpdateRollback {
    <#
    .SYNOPSIS
    For a specified stack that is in the UPDATE_ROLLBACK_FAILED state, continues rolling it back to the UPDATE_ROLLBACK_COMPLETE state. Depending on the cause of the failure, you can manually fix the error and continue the rollback. By continuing the rollback, you can return your stack to a working state (the UPDATE_ROLLBACK_COMPLETE state), and then try to update the stack again.

    .PARAMETER StackName
    The name or the unique ID of the stack that you want to continue rolling back. Don't specify the name of a nested stack (a stack that was created by using the AWS::CloudFormation::Stack resource). Instead, use this operation on the parent stack (the stack that contains the AWS::CloudFormation::Stack resource).

    .PARAMETER ClientRequestToken
    A unique identifier for this ContinueUpdateRollback request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to continue the rollback to a stack with the same name. You might retry ContinueUpdateRollback requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER ResourcesToSkip
    A list of the logical IDs of the resources that AWS CloudFormation skips during the continue update rollback operation. You can specify only resources that are in the UPDATE_FAILED state because a rollback failed. You can't specify resources that are in the UPDATE_FAILED state for other reasons, for example, because an update was cancelled. To check why a resource update failed, use the DescribeStackResources action, and view the resource status reason.

    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to roll back the stack. AWS CloudFormation uses the role's credentials to make calls on your behalf. AWS CloudFormation always uses this role for all future operations on the stack. As long as users have permission to operate on the stack, AWS CloudFormation uses this role even if the users don't have permission to pass it. Ensure that the role grants least privilege. If you don't specify a value, AWS CloudFormation uses the role that was previously associated with the stack. If no role is available, AWS CloudFormation uses a temporary session that is generated from your user credentials.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String[]]
        $ResourcesToSkip,
        [parameter(Mandatory = $false)]
        [String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "ContinueUpdateRollback"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Resume-VSStackUpdateRollback'

function Set-VSCredential {
    <#
    .SYNOPSIS
    Sets or updates the provided ProfileName on the Shared Credentials file (~\.aws\credentials).

    .PARAMETER AccessKey
    The access key to be used in the AWSCredentials.

    .PARAMETER SecretKey
    The secret key to use when creating AWSCredentials.

    .PARAMETER Region
    AWS Region to use with this credential set

    .PARAMETER ExternalID
    The external id to use in assume role AWSCredentials.

    .PARAMETER MfaSerial
    The serial number of the MFA to use in assume role AWSCredentials.

    .PARAMETER RoleArn
    The role ARN to use when creating assume role or federated AWSCredentials.

    .PARAMETER SourceProfile
    When this CredentialProfileOptions object references another CredentialProfile, the name of the referenced CredentialProfile.

    .PARAMETER Token
    The session token to be used to create AWSCredentials.

    .PARAMETER ProfileName
     The name that you would like to set for this credential profile. If no ProfileName is provided, it defaults to the AWS_PROFILE environment variable. If that is not set, it uses "default".

    .EXAMPLE
    Set-VSCredential -AccessKey lkjsdfkjio323823kl -SecretKey l38234sdfsdflk+23kjlkfs/skljf_k -Region USWest1

    .FUNCTIONALITY
    VaporShell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $AccessKey,
        [parameter(Mandatory = $false)]
        [String]
        $SecretKey,
        [parameter(Mandatory = $false)]
        [ValidateSet("APNortheast1","APNortheast2","APSouth1","APSoutheast1","APSoutheast2","CACentral1","CNNorth1","EUCentral1","EUWest1","EUWest2","SAEast1","USEast1","USEast2","USGovCloudWest1","USWest1","USWest2")]
        [String]
        $Region,
        [parameter(Mandatory = $false)]
        [String]
        $ExternalID,
        [parameter(Mandatory = $false)]
        [String]
        $MfaSerial,
        [parameter(Mandatory = $false)]
        [String]
        $RoleArn,
        [parameter(Mandatory = $false)]
        [String]
        $SourceProfile,
        [parameter(Mandatory = $false)]
        [String]
        $Token,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        if (!$ProfileName) {$ProfileName = "default"}
        $env:AWS_PROFILE = $ProfileName
        'AWS_PROFILE' | ForEach-Object {
            [System.Environment]::SetEnvironmentVariable($_,$ProfileName,[System.EnvironmentVariableTarget]::User)
        }
        $options = New-Object Amazon.Runtime.CredentialManagement.CredentialProfileOptions
        foreach ($key in $PSBoundParameters.Keys) {
            if ($options.PSObject.Properties.Name -contains $key) {
                Write-Verbose "Setting $key"
                $options.$key = $PSBoundParameters[$key]
            }
        }
        $prof = New-Object Amazon.Runtime.CredentialManagement.CredentialProfile "$ProfileName", $options
        if ($PSBoundParameters.Keys -contains "Region") {
            Write-Verbose "Setting Region"
            $Region = $PSBoundParameters["Region"]
            $prof.Region = [Amazon.RegionEndpoint]::$Region
            $env:AWS_REGION = $env:AWS_DEFAULT_REGION = $prof.Region.SystemName
            'AWS_REGION','AWS_DEFAULT_REGION' | ForEach-Object {
                [System.Environment]::SetEnvironmentVariable($_,$prof.Region.SystemName,[System.EnvironmentVariableTarget]::User)
            }
        }
        $sharedFile = New-Object Amazon.Runtime.CredentialManagement.SharedCredentialsFile
        Write-Verbose "Updating SharedCredentials file"
        $sharedFile.RegisterProfile($prof)
    }
}

Export-ModuleMember -Function 'Set-VSCredential'

function Set-VSStackPolicy {
    <#
    .SYNOPSIS
    Sets the Stack Policy.

    .PARAMETER StackName
    The name or unique stack ID that you want to associate a policy with.

    .PARAMETER StackPolicyBody
    Structure containing the stack policy body.

    .PARAMETER Path
    Path to the local Stack Policy file

    .PARAMETER StackPolicyURL
    Location of a file containing the stack policy. The URL must point to a policy (maximum size: 16 KB) located in an S3 bucket in the same region as the stack.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "StackPolicyBody")]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "StackPolicyBody",ValueFromPipeline = $true)]
        [String]
        $StackPolicyBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "StackPolicyURL")]
        [String]
        $StackPolicyURL,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "SetStackPolicy"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Path {
                    $request.StackPolicyBody = [System.IO.File]::ReadAllText((Resolve-Path $Path))
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Set-VSStackPolicy'

function Stop-VSStackSetOperation {
    <#
    .SYNOPSIS
    Stops an in-progress operation on a stack set and its associated stack instances.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to stop the operation for.

    .PARAMETER OperationId
    The ID of the stack operation.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [String]
        $OperationId,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = 'StopStackSetOperation'
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Stop-VSStackSetOperation'

function Stop-VSStackUpdate {
    <#
    .SYNOPSIS
    Cancels an update on the specified stack. If the call completes successfully, the stack rolls back the update and reverts to the previous stack configuration. You can cancel only stacks that are in the UPDATE_IN_PROGRESS state.

    .PARAMETER StackName
    The name or the unique stack ID that is associated with the stack.

    .PARAMETER ClientRequestToken
    A unique identifier for this CancelUpdateStack request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to cancel an update on a stack with the same name. You might retry CancelUpdateStack requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "CancelUpdateStack"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Stop-VSStackUpdate'

function Update-VSStack {
    <#
    .SYNOPSIS
    Updates a Stack.

    .PARAMETER TemplateBody
    Structure containing the template body with a minimum length of 1 byte and a maximum length of 51,200 bytes. (For more information, go to Template Anatomy in the AWS CloudFormation User Guide.)

    .PARAMETER Path
    The path to the local file containing the stack template.

    .PARAMETER TemplateUrl
    Location of file containing the template body. The URL must point to a template that is located in an Amazon S3 bucket.

    .PARAMETER UsePreviousTemplate
    Reuse the existing template that is associated with the stack that you are updating.

    .PARAMETER Capabilities
    A list of values that you must specify before AWS CloudFormation can update certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, and AWS::IAM::UserToGroupAddition.

    .PARAMETER ClientRequestToken
    A unique identifier for this UpdateStack request. Specify this token if you plan to retry requests so that AWS CloudFormation knows that you're not attempting to update a stack with the same name. You might retry UpdateStack requests to ensure that AWS CloudFormation successfully received them.

    .PARAMETER NotificationARNs
    Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that AWS CloudFormation associates with the stack. Specify an empty list to remove all notification topics.

    .PARAMETER Parameters
    A list of Parameter structures that specify input parameters for the stack. For more information, see the Parameter data type.

    .PARAMETER ResourceTypes
    The template resource types that you have permissions to work with for this update stack action, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance. If the list of resource types doesn't include a resource that you're updating, the stack update fails. By default, AWS CloudFormation grants permissions to all resource types. AWS Identity and Access Management (IAM) uses this parameter for AWS CloudFormation-specific condition keys in IAM policies.

    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes to update the stack. AWS CloudFormation uses the role's credentials to make calls on your behalf. AWS CloudFormation always uses this role for all future operations on the stack. As long as users have permission to operate on the stack, AWS CloudFormation uses this role even if the users don't have permission to pass it. Ensure that the role grants least privilege. If you don't specify a value, AWS CloudFormation uses the role that was previously associated with the stack. If no role is available, AWS CloudFormation uses a temporary session that is generated from your user credentials.

    .PARAMETER StackName
    The name or unique stack ID of the stack to update.

    .PARAMETER StackPolicyBody
    Structure containing a new stack policy body. You can specify either the StackPolicyBody or the StackPolicyURL parameter, but not both. You might update the stack policy, for example, in order to protect a new resource that you created during a stack update. If you do not specify a stack policy, the current policy that is associated with the stack is unchanged.

    .PARAMETER StackPolicyURL
    Location of a file containing the updated stack policy. The URL must point to a policy (max size: 16KB) located in an S3 bucket in the same region as the stack.

    .PARAMETER StackPolicyDuringUpdateBody
    Structure containing the temporary overriding stack policy body.

    .PARAMETER StackPolicyDuringUpdateURL
    Location of a file containing the temporary overriding stack policy. The URL must point to a policy (max size: 16KB) located in an S3 bucket in the same region as the stack.

    .PARAMETER Tags
    A hashtable containing key-value pairs to associate with this stack. AWS CloudFormation also propagates these tags to supported resources in the stack. You can specify a maximum number of 50 tags. If you don't specify this parameter, AWS CloudFormation doesn't modify the stack's tags. If you specify an empty value, AWS CloudFormation removes all associated tags.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path",SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $true,ParameterSetName = "UsePreviousTemplate")]
        [Switch]
        $UsePreviousTemplate,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [String]
        $ClientRequestToken,
        [parameter(Mandatory = $false)]
        [String[]]
        $NotificationARNs,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.Model.Parameter[]]
        $Parameters,
        [parameter(Mandatory = $false)]
        [String[]]
        $ResourceTypes,
        [parameter(Mandatory = $false)]
        [String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyBody,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyURL,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyDuringUpdateBody,
        [parameter(Mandatory = $false)]
        [String]
        $StackPolicyDuringUpdateURL,
        [parameter(Mandatory = $false)]
        [AllowNull()]
        [Hashtable]
        $Tags,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "UpdateStack"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Path {
                    $resolvedPath = (Resolve-Path $Path).Path
                    $request.TemplateBody = [System.IO.File]::ReadAllText($resolvedPath)
                }
                Tags {
                    $tagList = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.Tag]'
                    $tagList.Add((VSStackTag -Key BuiltWith -Value VaporShell))
                    if ($null -ne $Tags) {
                        foreach ($key in $Tags.Keys) {
                            $tagList.Add((VSStackTag -Key $key -Value $Tags[$key]))
                        }
                    }
                    $request.Tags = $tagList
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Update-VSStack'

function Update-VSStackSet {
    <#
    .SYNOPSIS
    Updates the stack set and all associated stack instances. Even if the stack set operation created by updating the stack set fails (completely or partially, below or above a specified failure tolerance), the stack set is updated with your changes. Subsequent CreateStackInstances calls on the specified stack set use the updated stack set.

    .PARAMETER TemplateBody
    The structure that contains the template body, with a minimum length of 1 byte and a maximum length of 51,200 bytes

    .PARAMETER Path
    The path to the local file containing the template.

    .PARAMETER TemplateUrl
    The location of the file that contains the template body. The URL must point to a template (maximum size: 460,800 bytes) that is located in an Amazon S3 bucket.

    .PARAMETER UsePreviousTemplate
    Use the existing template that's associated with the stack set that you're updating.

    .PARAMETER Capabilities
    A list of values that you must specify before AWS CloudFormation can create certain stack sets. Some stack set templates might include resources that can affect permissions in your AWS account—for example, by creating new AWS Identity and Access Management (IAM) users. For those stack sets, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. The following resources require you to specify this parameter: AWS::IAM::AccessKey, AWS::IAM::Group, AWS::IAM::InstanceProfile, AWS::IAM::Policy, AWS::IAM::Role, AWS::IAM::User, AWS::IAM::UserToGroupAddition.

    .PARAMETER Description
    A brief description of updates that you are making.

    .PARAMETER OperationId
    The unique ID for this stack set operation. The operation ID also functions as an idempotency token, to ensure that AWS CloudFormation performs the stack set operation only once, even if you retry the request multiple times. You might retry stack set operation requests to ensure that AWS CloudFormation successfully received them. If you don't specify an operation ID, AWS CloudFormation generates one automatically. Repeating this stack set operation with a new operation ID retries all stack instances whose status is OUTDATED.

    .PARAMETER OperationPreferences
    Preferences for how AWS CloudFormation performs this stack set operation.

    .PARAMETER Parameters
    A list of input parameters for the stack set template.

    .PARAMETER StackSetName
    The name or unique ID of the stack set that you want to update.

    .PARAMETER Tags
    The key-value pairs to associate with this stack set and the stacks created from it. AWS CloudFormation also propagates these tags to supported resources that are created in the stacks. You can specify a maximum number of 50 tags. If you specify tags for this parameter, those tags replace any list of tags that are currently associated with this stack set. This means: If you don't specify this parameter, AWS CloudFormation doesn't modify the stack's tags. If you specify any tags using this parameter, you must specify all the tags that you want associated with this stack set, even tags you've specifed before (for example, when creating the stack set or during a previous update of the stack set.). Any tags that you don't include in the updated list of tags are removed from the stack set, and therefore from the stacks and resources as well. If you specify an empty value, AWS CloudFormation removes all currently associated tags. If you specify new tags as part of an UpdateStackSet action, AWS CloudFormation checks to see if you have the required IAM permission to tag resources. If you omit tags that are currently associated with the stack set from the list of tags you specify, AWS CloudFormation assumes that you want to remove those tags from the stack set, and checks to see if you have permission to untag resources. If you don't have the necessary permission(s), the entire UpdateStackSet action fails with an access denied error, and the stack set is not updated.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "Path",SupportsShouldProcess = $true,ConfirmImpact = "High")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "Path")]
        [ValidateScript( {Test-Path $_})]
        [String]
        $Path,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateUrl")]
        [String]
        $TemplateUrl,
        [parameter(Mandatory = $true,ParameterSetName = "UsePreviousTemplate")]
        [Switch]
        $UsePreviousTemplate,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [String]
        $Description,
        [parameter(Mandatory = $false)]
        [String]
        $OperationId,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.Model.StackSetOperationPreferences]
        $OperationPreferences,
        [parameter(Mandatory = $false)]
        [Amazon.CloudFormation.Model.Parameter[]]
        $Parameters,
        [parameter(Mandatory = $false)]
        [String]
        $StackSetName,
        [parameter(Mandatory = $false)]
        [AllowNull()]
        [Hashtable]
        $Tags,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        if ($PSBoundParameters.Keys -contains "Tags") {
            $PSBoundParameters["Tags"] += (VSStackTag BuiltWith VaporShell)
        }
        else {
            $PSBoundParameters["Tags"] = (VSStackTag BuiltWith VaporShell)
        }
        $method = "UpdateStackSet"
        $requestType = "Amazon.CloudFormation.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                Path {
                    $resolvedPath = (Resolve-Path $Path).Path
                    $request.TemplateBody = [System.IO.File]::ReadAllText($resolvedPath)
                }
                Tags {
                    $tagList = New-Object 'System.Collections.Generic.List[Amazon.CloudFormation.Model.Tag]'
                    $tagList.Add((VSStackTag -Key BuiltWith -Value VaporShell))
                    if ($null -ne $Tags) {
                        foreach ($key in $Tags.Keys) {
                            $tagList.Add((VSStackTag -Key $key -Value $Tags[$key]))
                        }
                    }
                    $request.Tags = $tagList
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        if ($PSCmdlet.ShouldProcess($request)) {
            $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
            if (!$results) {
                return
            }
            elseif ($results -is 'System.Management.Automation.ErrorRecord') {
                $PSCmdlet.ThrowTerminatingError($results)
            }
            elseif ($results) {
                return $results
            }
        }
    }
}

Export-ModuleMember -Function 'Update-VSStackSet'

function Watch-Stack {
    <#
    .SYNOPSIS
    Watches Stack Events as they happen. Colorizes events based on event type.

    .PARAMETER StackId
    The Stack ID or name of the stack that you'd like to watch events for.

    .PARAMETER InNewWindow
    WINDOWS ONLY (For now). Watch events in a new PowerShell window. So you can continue working in your current console.

    .PARAMETER IncludeBlankResourceStatusReasons
    If passed/set to $true, this will also output CREATE_IN_PROGRESS events that do not include a ResourceStatusReason. All other ResourceStatuses

    .PARAMETER RefreshRate
    The rate in seconds that you'd like the event list to poll for new events. Defaults to 2.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [CmdletBinding()]
    Param (
        [parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
        [Alias("StackId")]
        [String[]]
        $StackName,
        [parameter(Mandatory = $false)]
        [Switch]
        $InNewWindow,
        [parameter(Mandatory = $false)]
        [Switch]
        $IncludeBlankResourceStatusReasons,
        [parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [Int]
        $RefreshRate = 2,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK -Verbose:$false
    }
    Process {
        if ($InNewWindow) {
            foreach ($stckName in $StackName) {
                if ($Env:OS -and ($Env:OS -notlike "*Windows*")) {
                    Write-Warning "You must be running Windows to use the '-InNewWindow' parameter -- returning to prompt"
                    return
                }
                else {
                    $conEmu = "$env:programfiles\ConEmu\Conemu64.exe"
                    $command = "Import-Module `"$($Script:VaporshellPath)\Vaporshell.psd1`" -ArgumentList `$false,`$true -Verbose:`$false;Clear-Host;. '$($Script:VaporshellPath)\bin\SetWatchWindow.ps1' -StackId '$stckName';Watch-Stack -StackName '$stckName' -RefreshRate $RefreshRate"
                    if ($ProfileName) {
                        $command += " -ProfileName '$ProfileName'"
                    }
                    $poshArg = "-noprofile -noexit -command & {$command}"
                    if ($env:ConEmuBaseDir) {
                        $poshArg += " -new_console:t:`"$stckName`""
                    }
                    if ((Test-Path $conEmu) -and !$env:ConEmuBaseDir) {
                        Start-Process $conEmu -ArgumentList '/title',"`"$stckName`"",'/cmd','powershell.exe','-noexit','-command',"& {$command}"
                    }
                    else {
                        Start-Process powershell -ArgumentList $poshArg
                    }
                }
            }
        }
        else {
            $i = 0
            $cont = $true
            $prof = @{
                Verbose = $false
            }
            if ($ProfileName) {
                $prof["ProfileName"] = $ProfileName
            }
            $strings = @()
            $head="`nSTACK NAME(S) : $($StackName -join ", ")`n" +
                    "REFRESH : $RefreshRate seconds`n"
            Colorize $head
            $private:tableHeaderAdded = $false
            $private:nameIdMap = @{}
            $stcks = $StackName
            do {
                do {
                    $completed = @()
                    foreach ($stckName in $stcks) {
                        try {
                            if ($private:nameIdMap.ContainsKey($stckName)) {
                                $results = Get-VSStack -Events -StackId $private:nameIdMap[$stckName] @prof -ErrorAction Stop | Sort-Object timestamp
                            }
                            else  {
                                $results = Get-VSStack -Events -StackId "$stckName" @prof -ErrorAction Stop | Sort-Object timestamp
                                $private:nameIdMap[$stckName] = $results[0].StackId
                            }
                            $stName = $results[0].StackId
                            $snLength = $results[0].StackName.Length
                            $resTypeLength = ($results.LogicalResourceId | Where-Object {$_ -ne $results[0].StackName} | Sort-Object -Property Length)[-1].Length
                            if ($resTypeLength -le 26) {
                                $resTypeLength = 26
                            }
                            $stack = $results | Sort-Object TimeStamp | ForEach-Object {
                                if (!$private:tableHeaderAdded) {
                                    "{0,-20} {1,-20} {2,-$($snLength)} {3,-$resTypeLength} {4,-35}" -f 'Timestamp','ResourceStatus','StackName','LogicalId','ResourceStatusReason'
                                    "{0,-20} {1,-20} {2,-$($snLength)} {3,-$resTypeLength} {4,-35}" -f '---------','--------------','---------','------------','--------------------'
                                    $private:tableHeaderAdded = $true
                                }
                                $identifier = if ($_.LogicalResourceId -eq $results[0].StackName) {
                                    'AWS::CloudFormation::Stack'
                                }
                                else {
                                    $_.LogicalResourceId
                                }
                                if ($_.ResourceStatus -ne 'CREATE_IN_PROGRESS' -or $_.ResourceStatusReason -or ($_.ResourceStatus -eq 'CREATE_IN_PROGRESS' -and $PSBoundParameters['IncludeBlankResourceStatusReasons'])) {
                                    $formatted = "{0,-20} {1,-20} {2,-$($snLength)} {3,-$resTypeLength} {4,-35}" -f $_.Timestamp,$_.ResourceStatus,$_.StackName,$identifier,$_.ResourceStatusReason
                                    if ($strings -notcontains $formatted.Replace(' ','')) {
                                        $formatted.Trim()
                                        $strings += $formatted.Replace(' ','')
                                    }
                                }
                            }
                            Colorize $stack
                            if ($i -ge 1 -and ($stack -match '.*_(COMPLETE|FAILED).*AWS::CloudFormation::Stack.*' -or $null -eq $stack)) {
                                $stackStatus = (Get-VSStack -StackId "$stName" @prof).StackStatus.Value
                                if ($stackStatus -match '.*_(COMPLETE|FAILED)$') {
                                    Write-Verbose "[$stckName] Stack status: $stackStatus"
                                    $cont = $false
                                    $completed += $stckName
                                }
                                else {
                                    Start-Sleep $RefreshRate
                                }
                            }
                            else {
                                Start-Sleep $RefreshRate
                            }
                        }
                        catch {
                            $PSCmdlet.ThrowTerminatingError($_)
                            $cont = $false
                        }
                    }
                    $i++
                    $stcks = $stcks | Where-Object {$_ -notin $completed}
                }
                until ($cont -eq $false)
                if ($completed.Count -and $i -ge 1) {
                    $message = "$($stcks.Count) stacks remaining"
                    if ($stcks.Count) {
                        $message += ": [ $($stcks -join ', ') ]"
                    }
                    Write-Verbose $message
                }
            }
            until ($null -eq $stcks)
        }
    }
}

Export-ModuleMember -Function 'Watch-Stack'

function VSStackParameter {
    Param(
        [parameter(Mandatory = $false, Position = 0)]
        [System.String]
        $ParameterKey,
        [parameter(Mandatory = $false, Position = 1)]
        [System.String]
        $ParameterValue,
        [parameter(Mandatory = $false, Position = 2)]
        [System.Boolean]
        $UsePreviousValue
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        New-Object Amazon.CloudFormation.Model.Parameter -Property $PSBoundParameters
    }
}

Export-ModuleMember -Function 'VSStackParameter'

function VSStackSetOperationPreferences {
    [cmdletbinding(DefaultParameterSetName = "FailureToleranceCountMaxConcurrentCount")]
    Param (
        [parameter(Mandatory = $true, Position = 0, ParameterSetName = "FailureToleranceCountMaxConcurrentCount")]
        [parameter(Mandatory = $true, Position = 0, ParameterSetName = "FailureToleranceCountMaxConcurrentPercentage")]
        [System.Int32]
        $FailureToleranceCount,
        [parameter(Mandatory = $true, Position = 0, ParameterSetName = "FailureTolerancePercentageMaxConcurrentCount")]
        [parameter(Mandatory = $true, Position = 0, ParameterSetName = "FailureTolerancePercentageMaxConcurrentPercentage")]
        [System.Int32]
        $FailureTolerancePercentage,
        [parameter(Mandatory = $true, Position = 1, ParameterSetName = "FailureToleranceCountMaxConcurrentCount")]
        [parameter(Mandatory = $true, Position = 1, ParameterSetName = "FailureTolerancePercentageMaxConcurrentCount")]
        [System.Int32]
        $MaxConcurrentCount,
        [parameter(Mandatory = $true, Position = 1, ParameterSetName = "FailureToleranceCountMaxConcurrentPercentage")]
        [parameter(Mandatory = $true, Position = 1, ParameterSetName = "FailureTolerancePercentageMaxConcurrentPercentage")]
        [System.Int32]
        $MaxConcurrentPercentage,
        [parameter(Mandatory = $false, Position = 2)]
        [System.String[]]
        $RegionOrder
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        Write-Verbose "Parameter Set: $($PSCmdlet.ParameterSetName)"
        $obj = New-Object Amazon.CloudFormation.Model.StackSetOperationPreferences
        foreach ($key in $PSBoundParameters.Keys) {
            if ($obj.PSObject.Properties.Name -contains $key) {
                $obj.$key = $PSBoundParameters[$key]
            }
        }
        return $obj
    }
}

Export-ModuleMember -Function 'VSStackSetOperationPreferences'

function VSStackTag {
    Param
    (
        [parameter(Mandatory = $false, Position = 0)]
        [System.String]
        $Key,
        [parameter(Mandatory = $false, Position = 1)]
        [System.String]
        $Value
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        New-Object Amazon.CloudFormation.Model.Tag -Property $PSBoundParameters
    }
}

Export-ModuleMember -Function 'VSStackTag'

function Invoke-VSDeploy {
    <#
    .SYNOPSIS
    Creates a change set then executes it
    
    .DESCRIPTION
    Invokes a change set in CloudFormation. If the stack name already exists, it updates that stack. If it does not, it creates a new stack.
    
    .PARAMETER TemplateBody
    A JSON or YAML string containing the template body.
    
    .PARAMETER TemplateFile
    The path to the local file containing the template.
    
    .PARAMETER StackName
    The name of the AWS CloudFormation stack you're deploying to. If you specify an existing stack, the command updates the stack. If you specify a new stack, the command creates it.
    
    .PARAMETER Parameters
    A hashtable of key-value parameters that specify input parameters for your stack template. If you're updating a stack and you don't specify a parameter, the command uses the stack's existing value. For new stacks, you must specify parameters that don't have a default value.
    
    .PARAMETER Capabilities
    A list of capabilities that you must specify before AWS Cloudformation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM
    
    .PARAMETER DoNotExecute
    Indicates whether to execute the change set. Specify this flag if you want to view your stack changes before executing the change set. The command creates an AWS CloudFormation change set and then exits without executing the change set. After you view the change set, execute it to implement your changes.
    
    .PARAMETER RoleARN
    The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes when executing the change set.
    
    .PARAMETER NotificationARNs
    Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that AWS CloudFormation associates with the stack.
    
    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.
    
    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "TemplateFile")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateFile")]
        [ValidateScript({
                Test-Path $_
            })]
        [String]
        $TemplateFile,
        [parameter(Mandatory = $true,Position = 1)]
        [String]
        $StackName,
        [parameter(Mandatory = $false)]
        [ValidateScript( {
                $allowedTypes = "System.Management.Automation.PSCustomObject","Amazon.CloudFormation.Model.Parameter","System.Collections.Hashtable"
                if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") {
                    $true
                }
                else {
                    $PSCmdlet.ThrowTerminatingError((New-VSError -String "This parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")."))
                }
            })]
        [Alias("ParameterOverrides")]
        $Parameters,
        [parameter(Mandatory = $false)]
        [ValidateSet("CAPABILITY_IAM","CAPABILITY_NAMED_IAM")]
        [String[]]
        $Capabilities,
        [parameter(Mandatory = $false)]
        [Alias("NoExecuteChangeSet")]
        [Switch]
        $DoNotExecute,
        [parameter(Mandatory = $false)]
        [System.String]
        $RoleARN,
        [parameter(Mandatory = $false)]
        [System.String[]]
        $NotificationARNs,
        [parameter(Mandatory = $false)]
        [Switch]
        $Watch,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        $changeSetParams = @{}
        $prof = @{}
        if ($ProfileName -and ($PSBoundParameters.Keys -notcontains "ProfileName")) {
            $PSBoundParameters["ProfileName"] = $ProfileName
        }
        if ($ProfileName) {
            $prof["ProfileName"] = $ProfileName
        }
    }
    Process {
        if ($PSCmdlet.ParameterSetName -eq "TemplateFile") {
            Write-Verbose "Getting TemplateBody from TemplateFile path"
            $templateFilePath = (Resolve-Path $TemplateFile).Path
            $TemplateBody = [System.IO.File]::ReadAllText($templateFilePath)
        }
        $changeSetParams["TemplateBody"] = $TemplateBody
        $changeSetParams["ChangeSetName"] = "$($StackName)-$(Get-Date -Format "yyyyMMdd-HHmmss")"
        foreach ($key in $PSBoundParameters.Keys) {
            if ((Get-Command New-VSChangeSet).Parameters.Keys -contains $key -and $key -ne "Verbose") {
                $changeSetParams[$key] = $PSBoundParameters[$key]
            }
        }
        try {
            Write-Verbose "Creating change set as UPDATE"
            $changeSet = New-VSChangeSet @changeSetParams -Verbose:$false
            Write-Verbose "Change Set type 'UPDATE' created"
        }
        catch {
            try {
                $changeSet = New-VSChangeSet @changeSetParams -ChangeSetType CREATE -Verbose:$false
                Write-Verbose "Change Set type 'CREATE' created"
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        $changeSetDetails = Get-VSChangeSet -Description -ChangeSetName $changeSet.Id -StackName $changeSet.StackId @prof -Verbose:$false
        if ($DoNotExecute) {
            return $changeSetDetails
        }
        else {
            $i=0
            Write-Verbose "Waiting for change set to be available to execute"
            do {
                $i++
                Start-Sleep 1
                $changeSetDetails = Get-VSChangeSet -Description -ChangeSetName $changeSet.Id -StackName $changeSet.StackId -Verbose:$false @prof
            }
            until ($changeSetDetails.ExecutionStatus.Value -eq "AVAILABLE" -or $changeSetDetails.Status.Value -eq "FAILED" -or $i -ge 60)
            if ($changeSetDetails.Status.Value -eq "FAILED") {
                Write-Warning "Change Set FAILED! Reason: $($changeSetDetails.StatusReason)"
            }
            elseif ($i -ge 60) {
                Write-Warning "Change Set is not showing as available to execute after 60 seconds! Returning details"
                return $changeSetDetails
            }
            else {
                Write-Verbose "Executing Change Set after $i seconds"
                try {
                    $execution = Invoke-VSChangeSetExecution -ChangeSetName $changeSetDetails.ChangeSetId -StackName $changeSetDetails.StackId @prof
                    if ($Watch) {
                        Write-Verbose "Watching deployment!"
                        Watch-Stack -StackName $StackName
                    }
                    else {
                        return $changeSetDetails | Select-Object *,@{N="ExecutionResponse";E={$execution}}
                    }
                }
                catch {
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
    }
}

Export-ModuleMember -Function 'Invoke-VSDeploy'

function Invoke-VSPackage {
    <#
    .SYNOPSIS
    Packages the local artifacts (local paths) that your AWS CloudFormation template references.
    
    .DESCRIPTION
    Packages the local artifacts (local paths) that your AWS CloudFormation template references. The command uploads local artifacts, such as source code for an AWS Lambda function or a Swagger file for an AWS API Gateway REST API, to an S3 bucket. The command returns a copy of your template, replacing references to local artifacts with the S3 location where the command uploaded the artifacts.
    
    .PARAMETER TemplateBody
    A JSON or YAML string containing the template body.
    
    .PARAMETER TemplateFile
    The path to the local file containing the template.
    
    .PARAMETER S3Bucket
    The name of the S3 bucket where this command uploads the artifacts that are referenced in your template.
    
    .PARAMETER S3Prefix
    A prefix name that the command adds to the artifacts' name when it uploads them to the S3 bucket. The prefix name is a path name (folder name) for the S3 bucket.
    
    .PARAMETER KMSKeyId
    The ID of an AWS KMS key that the command uses to encrypt artifacts that are at rest in the S3 bucket.
    
    .PARAMETER OutputTemplateFile
    The path to the file where the command writes the output AWS CloudFormation template. If you don't specify a path, the command writes the template to the standard output.
    
    .PARAMETER UseJson
    Indicates whether to use JSON as the format for the output AWS CloudFormation template. YAML is used by default (if cfn-flip is available).
    
    .PARAMETER Force
    Indicates whether to override existing files in the S3 bucket. Specify this flag to upload artifacts even if they match existing artifacts in the S3 bucket.
    
    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.
    
    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding(DefaultParameterSetName = "TemplateFile")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateBody",ValueFromPipeline = $true)]
        [String]
        $TemplateBody,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName = "TemplateFile")]
        [ValidateScript( {
                Test-Path $_
            })]
        [String]
        $TemplateFile,
        [parameter(Mandatory = $true,Position = 1)]
        [String]
        $S3Bucket,
        [parameter(Mandatory = $false)]
        [System.String]
        $S3Prefix,
        [parameter(Mandatory = $false)]
        [System.String]
        $KMSKeyId,
        [parameter(Mandatory = $false)]
        [System.String]
        $OutputTemplateFile,
        [parameter(Mandatory = $false)]
        [Switch]
        $UseJson,
        [parameter(Mandatory = $false)]
        [Alias("ForceUpload")]
        [Switch]
        $Force,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        $prof = @{}
        if ($ProfileName) {
            $prof["ProfileName"] = $ProfileName
        }
        $typeHash = @{
            "AWS::ApiGateway::RestApi"                  = "BodyS3Location"
            "AWS::Lambda::Function"                     = "Code"
            "AWS::Serverless::Function"                 = "CodeUri"
            "AWS::Serverless::Api"                      = "DefinitionUri"
            "AWS::ElasticBeanstalk::ApplicationVersion" = "SourceBundle"
            "AWS::CloudFormation::Stack"                = "TemplateUrl"
        }
        $s3Params = @{}
        if ($S3Prefix) {
            $s3Params["BucketName"] = "$($S3Bucket)/$($S3Prefix)"
            $baseUrl = "$($S3Bucket)/$($S3Prefix)"
        }
        else {
            $s3Params["BucketName"] = $S3Bucket
            $baseUrl = $($S3Bucket)
        }
        if ($KMSKeyId) {
            $s3Params["KMSKeyId"] = $KMSKeyId
        }
        try {
            Write-Verbose "Checking if bucket '$S3Bucket' exists"
            $bucketLoc = Get-VSS3BucketLocation -BucketName "$S3Bucket" @prof -Verbose:$false
            Write-Verbose "Bucket '$S3Bucket' found in $($bucketLoc.Value)"
        }
        catch {
            if ($Force) {
                Write-Verbose "Creating new bucket '$S3Bucket'"
                New-VSS3Box -BucketName "$S3Bucket" @prof -Verbose:$false
            }
            else {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
    }
    Process {
        Add-Type -AssemblyName "System.IO.Compression.Filesystem"
        if ($PSCmdlet.ParameterSetName -eq "TemplateFile") {
            Write-Verbose "Getting TemplateBody from TemplateFile path"
            $templateFilePath = (Resolve-Path $TemplateFile).Path
            $tempParent = (Get-Item $templateFilePath).Directory.FullName
            $TemplateBody = [System.IO.File]::ReadAllText($templateFilePath)
        }
        $tempPSON = Import-Vaporshell -TemplateBody $TemplateBody
        $resourceIds = $tempPSON.Resources | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
        foreach ($res in $resourceIds) {
            $Resource = $tempPSON.Resources.$res
            try {
                $propName = $typeHash["$($Resource.Type)"]
                if ($propName -and $Resource.Properties.$propName) {
                    Write-Verbose "Checking '$($Resource.Type)' resource --- property '$($propName)'"
                    if (($Resource.Properties.$propName -notlike "s3://*") -and ($Resource.Properties.$propName -notlike "http*")) {
                        $found = $true
                        if (Test-Path ("$tempParent\$($Resource.Properties.$propName.TrimStart('.'))")) {
                            $filePath = (Resolve-Path "$tempParent\$($Resource.Properties.$propName.TrimStart('.'))").Path
                            Write-Verbose "File found in template directory at: $filePath"
                        }
                        elseif (Test-Path $Resource.Properties.$propName) {
                            $filePath = (Resolve-Path $Resource.Properties.$propName).Path
                            if ($filePath -like "$($pwd.Path)*") {
                                Write-Verbose "File found in current working directory at: $filePath"
                            }
                            else {
                                Write-Verbose "File found at: $filePath"
                            }
                        }
                        else {
                            $found = $false
                        }
                        if ($found) {
                            $fileInfo = Get-Item $filePath
                            if ($fileInfo.PSIsContainer) {
                                if ($S3Prefix) {
                                    $key = "$($S3Prefix)/$($fileInfo.BaseName).zip"
                                }
                                else {
                                    $key = "$($fileInfo.BaseName).zip"
                                }
                                $outFile = Join-Path $fileInfo.Parent.FullName $key
                                if (Test-Path $outFile) {
                                    Remove-Item $outFile -Force
                                }
                                [System.IO.Compression.Zipfile]::CreateFromDirectory($filePath,$outFile)
                            }
                            else {
                                if ($S3Prefix) {
                                    $key = "$($S3Prefix)/$($fileInfo.Name)"
                                }
                                else {
                                    $key = "$($fileInfo.Name)"
                                }
                                $outFile = $filePath
                            }
                            if ($Force) {
                                Write-Verbose "Uploading object!"
                                $obj = New-VSS3Object -Key $key -FilePath $outFile @s3Params @prof -Verbose:$false
                            }
                            else {
                                Write-Verbose "Checking if object exists in bucket"
                                $existsMeta = Get-VSS3ObjectMetadata -BucketName $baseUrl -Key $key -ErrorAction SilentlyContinue -Verbose:$false
                                if (!$existsMeta) {
                                    Write-Verbose "Object not found -- uploading!"
                                    $obj = New-VSS3Object -Key $key -FilePath $outFile @s3Params @prof -Verbose:$false
                                }
                                elseif ($existsMeta.ContentLength -eq (Get-Item $outFile).Length) {
                                    Write-Warning "Object '$key' already exists in bucket and is the same size. No action apparently necessary -- If this file needs to be reuploaded, re-run this command with the Force parameter included."
                                    return
                                }
                                else {
                                    Write-Warning "Object already exists at this location and Force parameter not used. No action taken to prevent accidental overwrites. -- If this object needs to be overwritten, re-run this command with the Force parameter included."
                                    return
                                }
                            }
                            $Resource.Properties.$propName = "s3://$baseUrl/$key"
                        }
                        else {
                            Write-Warning "$propName value '$($Resource.Properties.$propName)' does not appear to be an S3 URL but is also not locatable in the current working directory or the direct of the template (if provided). Please specify the full path of the local $propName to upload to the S3 bucket '$baseUrl'"
                        }
                    }
                }
                $tempPSON.Resources.$res = $Resource
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        $finalParams = @{}
        if ($OutputTemplateFile) {
            $finalParams["Path"] = $OutputTemplateFile
        }
        if ($UseJson) {
            $finalParams["As"] = "JSON"
        }
        else {
            $finalParams["As"] = "YAML"
        }
    Write-Verbose "Exporting resolved template"
    Export-Vaporshell -VaporshellTemplate $tempPSON @finalParams -Force
    }
}

Export-ModuleMember -Function 'Invoke-VSPackage'

function Get-VSS3BucketList {
    <#
    .SYNOPSIS
    Gets the list of S3 bucket names, owners and creation dates.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "ListBuckets"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSS3BucketList'

function Get-VSS3BucketLocation {
    <#
    .SYNOPSIS
    Gets the location of a specific S3 bucket

    .PARAMETER BucketName
    The name of the bucket you are trying to locate.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true,Position = 0)]
        [String]
        $BucketName,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "GetBucketLocation"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        $request.BucketName = $BucketName
        $expand = "Location"
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSS3BucketLocation'

function Get-VSS3ObjectList {
    <#
    .SYNOPSIS
    Gets the objects from a specific S3 bucket

    .PARAMETER BucketName
    The name of the bucket containing the object.

    .PARAMETER Prefix
    Limits the response to keys that begin with the specified prefix.

    .PARAMETER StartAfter
    StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts listing after this specified key. StartAfter can be any key in the bucket.

    .PARAMETER MaxKeys
    Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true,Position = 0)]
        [String]
        $BucketName,
        [parameter(Mandatory = $false)]
        [String]
        $Prefix,
        [parameter(Mandatory = $false)]
        [String]
        $StartAfter,
        [parameter(Mandatory = $false)]
        [System.Int32]
        $MaxKeys,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "ListObjectsV2"
        $expand = "S3Objects"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $request.FetchOwner = $true
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results | Select-Object *,@{N="OwnerDisplayName";E={$_.Owner.DisplayName}} -ExcludeProperty Owner
        }
    }
}

Export-ModuleMember -Function 'Get-VSS3ObjectList'

function Get-VSS3ObjectMetadata {
    <#
    .SYNOPSIS
    Gets an S3 object's metadata

    .PARAMETER BucketName
    The name of the bucket containing the object.

    .PARAMETER Key
    The name (including prefix, if applicable) of the object.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true,Position = 0)]
        [String]
        $BucketName,
        [parameter(Mandatory = $true,Position = 1)]
        [String]
        $Key,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "GetObjectMetadata"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            if ($request.PSObject.Properties.Name -contains $key) {
                $request.$key = $PSBoundParameters[$key]
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request $expand
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'Get-VSS3ObjectMetadata'

function New-VSS3Box {
    <#
    .SYNOPSIS
    Creates a new bucket in S3. Due to auto-generated resource functions already using the function name 'New-VSS3Bucket', this has been renamed to prevent collisions.

    .PARAMETER BucketName
    The name of the bucket to be created.

    .PARAMETER BucketRegionName
    The bucket region locality expressed using the name of the region. When set, this will determine where your data will reside in S3. Valid values: us-east-1, us-west-1, us-west-2, eu-west-1, ap-southeast-1, ap-southeast-2, ap-northeast-1, sa-east-1

    .PARAMETER CannedACL
    The canned ACL to apply to the bucket. Valid options are: "NoACL","Private","PublicRead","PublicReadWrite","AuthenticatedRead","AWSExecRead","BucketOwnerRead","BucketOwnerFullControl","LogDeliveryWrite"

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true,Position = 0)]
        [String]
        $BucketName,
        [parameter(Mandatory = $false)]
        [ValidateSet("us-east-1","us-west-1","us-west-2","eu-west-1","ap-southeast-1","ap-southeast-2","ap-northeast-1","sa-east-1")]
        [String]
        $BucketRegionName,
        [parameter(Mandatory = $false)]
        [ValidateSet("NoACL","Private","PublicRead","PublicReadWrite","AuthenticatedRead","AWSExecRead","BucketOwnerRead","BucketOwnerFullControl","LogDeliveryWrite")]
        [String]
        $CannedACL,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "PutBucket"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                CannedACL {
                    $request.CannedACL = [Amazon.S3.S3CannedACL]::$CannedACL
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'New-VSS3Box'

function New-VSS3Object {
    <#
    .SYNOPSIS
    Uploads an object to S3

    .PARAMETER BucketName
    The name of the bucket to contain the object.

    .PARAMETER CannedACL
    The canned access control list (CACL) to apply to the object. Valid options are: "NoACL","Private","PublicRead","PublicReadWrite","AuthenticatedRead","AWSExecRead","BucketOwnerRead","BucketOwnerFullControl","LogDeliveryWrite"

    .PARAMETER ContentBody
    Text content to be uploaded. Use this property if you want to upload plaintext to S3. The content type will be set to 'text/plain'.

    .PARAMETER ContentBody
    Text content to be uploaded. Use this property if you want to upload plaintext to S3. The content type will be set to 'text/plain'.

    .PARAMETER FilePath
    The full path and name to a file to be uploaded. If this is set the request will upload the specified file to S3.

    .PARAMETER Key
    The key used to identify the object in S3.

    .PARAMETER KMSKeyId
    The id of the AWS Key Management Service key that Amazon S3 should use to encrypt and decrypt the object. If a key id is not specified, the default key will be used for encryption and decryption.

    .PARAMETER ProfileName
    The name of the configuration profile to deploy the stack with. Defaults to $env:AWS_PROFILE, if set.

    .FUNCTIONALITY
    Vaporshell
    #>

    [cmdletbinding()]
    Param (
        [parameter(Mandatory = $true,Position = 0)]
        [String]
        $BucketName,
        [parameter(Mandatory = $false)]
        [ValidateSet("NoACL","Private","PublicRead","PublicReadWrite","AuthenticatedRead","AWSExecRead","BucketOwnerRead","BucketOwnerFullControl","LogDeliveryWrite")]
        [String]
        $CannedACL,
        [parameter(Mandatory = $false)]
        [System.String]
        $ContentBody,
        [parameter(Mandatory = $false)]
        [System.String]
        $FilePath,
        [parameter(Mandatory = $false)]
        [System.String]
        $Key,
        [parameter(Mandatory = $false)]
        [System.String]
        $KMSKeyId,
        [parameter(Mandatory = $false)]
        [String]
        $ProfileName = $env:AWS_PROFILE
    )
    Begin {
        Import-AWSSDK
    }
    Process {
        $method = "PutObject"
        $requestType = "Amazon.S3.Model.$($method)Request"
        $request = New-Object $requestType
        foreach ($key in $PSBoundParameters.Keys) {
            switch ($key) {
                CannedACL {
                    $request.CannedACL = [Amazon.S3.S3CannedACL]::$CannedACL
                }
                KMSKeyId {
                    $request.ServerSideEncryptionKeyManagementServiceKeyId = $KMSKeyId
                }
                Default {
                    if ($request.PSObject.Properties.Name -contains $key) {
                        $request.$key = $PSBoundParameters[$key]
                    }
                }
            }
        }
        $results = ProcessRequest $PSCmdlet.ParameterSetName $ProfileName $method $request
        if (!$results) {
            return
        }
        elseif ($results -is 'System.Management.Automation.ErrorRecord') {
            $PSCmdlet.ThrowTerminatingError($results)
        }
        elseif ($results) {
            return $results
        }
    }
}

Export-ModuleMember -Function 'New-VSS3Object'

function vsl {
    <#
    .SYNOPSIS
    CLI style superset of VaporShell focused on packaging and deployment of CloudFormation stacks.

    For detailed parameter explanation, please run the following command with the action you'd like help with:

    vsl $action --help #actions: package | deploy | vaporize
    
    .DESCRIPTION
    CLI style superset of VaporShell focused on packaging and deployment of CloudFormation stacks. Allows you to build stacks with detailed transforms like SAM templates containing local resources with one easy command.
    
    .PARAMETER action
    The action that you'd like to do; options for this are: package, deploy, and vaporize (package AND deploy rolled into one).
    
    .PARAMETER help
    Switch to show help for the chosen action.
    
    .PARAMETER parameters
    The arguments to pass to vsl in CLI style:

    --template-file <value> --stack-name <value> --s3-bucket <value> [--s3-prefix <value>] [--parameter-overrides <value> [<value>...]] [--capabilities <value> [<value>...]] [--no-execute-changeset] [--role-arn <value>] [--notification-arns <value> [<value>...]] [--kms-key-id <value>] [--output-template-file <value>] [--use-json] [--force-upload] [--profile-name <value>] [--verbose {info (default)|full}]
    
    .EXAMPLE
    # Packages and deploys a SAM template. This includes zipping up the CodeURI folder, uploading to S3, adjusting the template with the S3 object location for CodeURI, creating the Change Set, then executing once the Change Set becomes available.

    vsl vaporize --tf ".\Helpers\SAM\schedule\template.yaml" --sn testSAMdeployment --s3 scrthq-package --pn scrthq --capabilities iam --v --f

    .EXAMPLE
    # Gets detailed help for the vaporize action

    vsl vaporize --help
    # OR
    vsl vaporize -help
    
    .NOTES
    General notes
    #>

    [cmdletbinding()]
    Param
    (
        [parameter(Mandatory = $true,Position = 0)]
        [ValidateSet("package","deploy","vaporize")]
        $action,
        [parameter(Mandatory = $false)]
        [switch]
        $help,
        [parameter(Mandatory = $false,Position = 1,ValueFromRemainingArguments = $true)]
        $parameters
    )    
    if ($help -or $parameters -match '^--help$') {
        $message = switch ($action) {
            package {@'
vsl package --template-file <value> --s3-bucket <value> [--s3-prefix <value>] [--kms-key-id <value>] [--output-template-file <value>] [--use-json] [--force-upload] [--profile-name <value>] [--verbose]


~ ~ ~ DESCRIPTION ~ ~ ~

Packages the local artifacts (local paths) that your AWS CloudFormation template references. The command uploads local artifacts, such as source code for an AWS Lambda function or a Swagger file for an AWS API Gateway REST API, to an S3 bucket. The command returns a copy of your template, replacing references to local artifacts with the S3 location where the command uploaded the artifacts.

This command can upload local artifacts specified by following properties of a resource:

    BodyS3Location property for the AWS::ApiGateway::RestApi resource
    Code property for the AWS::Lambda::Function resource
    CodeUri property for the AWS::Serverless::Function resource
    DefinitionUri property for the AWS::Serverless::Api resource
    SourceBundle property for the AWS::ElasticBeanstalk::ApplicationVersion resource
    TemplateURL property for the AWS::CloudFormation::Stack resource

To specify a local artifact in your template, specify a path to a local file or folder, as either an absolute or relative path. The relative path is a location that is relative to your template's location.

For example, if your AWS Lambda function source code is in the /home/user/code/lambdafunction/ folder, specify `` CodeUri: /home/user/code/lambdafunction`` for the AWS::Serverless::Function resource. The command returns a template and replaces the local path with the S3 location: CodeUri: s3://mybucket/lambdafunction.zip.


~ ~ ~ OPTIONS ~ ~ ~

REQUIRED
    --tf, --template-file (string) The path where your AWS CloudFormation template is located.
        OR
    --tb, --template-body (string) The stringified template body

    --s3, --s3-bucket (string) The name of the S3 bucket where this command uploads the artifacts that are referenced in your template.


OPTIONAL
    --s3-prefix (string) A prefix name that the command adds to the artifacts' name when it uploads them to the S3 bucket. The prefix name is a path name (folder name) for the S3 bucket.

    --kms-key-id (string) The ID of an AWS KMS key that the command uses to encrypt artifacts that are at rest in the S3 bucket.

    --output-template-file (string) The path to the file where the command writes the output AWS CloudFormation template. If you don't specify a path, the command writes the template to the standard output.

    --use-json (boolean) Indicates whether to use JSON as the format for the output AWS CloudFormation template. YAML is used by default.

    --force-upload (boolean) Indicates whether to override existing files in the S3 bucket. Specify this flag to upload artifacts even if they match existing artifacts in the S3 bucket.
    
    --pn, --profile-name (string) The name of the saved configuration you would like to use for this command. Defaults to $env:AWS_PROFILE if set.

    --v, --verbose (boolean | string) Enables verbose output on the console.
'@

            }
            deploy {@'
vsl deploy --template-file <value> --stack-name <value> [--parameter-overrides <value> [<value>...]] [--capabilities <value> [<value>...]] [--no-execute-changeset] [--role-arn <value>] [--notification-arns <value> [<value>...]] [--profile-name <value>] [--verbose]
            
            
~ ~ ~ DESCRIPTION ~ ~ ~

Deploys the specified AWS CloudFormation template by creating and then executing a change set. The command terminates after AWS CloudFormation executes the change set. If you want to view the change set before AWS CloudFormation executes it, use the --no-execute-changeset flag.

To update a stack, specify the name of an existing stack. To create a new stack, specify a new stack name.


~ ~ ~ OPTIONS ~ ~ ~

REQUIRED
    --tf, --template-file (string) The path where your AWS CloudFormation template is located.
        OR
    --tb, --template-body (string) The stringified template body

    --sn, --stack-name (string) The name of the AWS CloudFormation stack you're deploying to. If you specify an existing stack, the command updates the stack. If you specify a new stack, the command creates it.


OPTIONAL
    --w, --watch (boolean) Immediately starts Watch-Stack after deployment executes. If in Windows, it will open a new window, otherwise it will display in your current terminal window

    --parameter-overrides (list) A list of parameter structures that specify input parameters for your stack template. If you're updating a stack and you don't specify a parameter, the command uses the stack's existing value. For new stacks, you must specify parameters that don't have a default value. Syntax: ParameterKey1=ParameterValue1 ParameterKey2=ParameterValue2 ...

    --capabilities (list) A list of capabilities that you must specify before AWS Cloudformation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error.

    --no-execute-changeset (boolean) Indicates whether to execute the change set. Specify this flag if you want to view your stack changes before executing the change set. The command creates an AWS CloudFormation change set and then exits without executing the change set. After you view the change set, execute it to implement your changes.

    --role-arn (string) The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes when executing the change set.

    --notification-arns (list) Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that AWS CloudFormation associates with the stack.
    
    --pn, --profile-name (string) The name of the saved configuration you would like to use for this command. Defaults to $env:AWS_PROFILE if set.

    --v, --verbose (boolean) Enables verbose output on the console.
'@

            }
            vaporize {@'
vsl vaporize --template-file <value> --stack-name <value> [--s3-bucket <value>] [--s3-prefix <value>] [--parameter-overrides <value> [<value>...]] [--capabilities <value> [<value>...]] [--no-execute-changeset] [--role-arn <value>] [--notification-arns <value> [<value>...]] [--kms-key-id <value>] [--output-template-file <value>] [--use-json] [--force-upload] [--profile-name <value>] [--verbose]


~ ~ ~ DESCRIPTION ~ ~ ~

Combines both package and deploy into one command.


~ ~ ~ OPTIONS ~ ~ ~

REQUIRED
    --tf, --template-file (string) The path where your AWS CloudFormation template is located.
        OR
    --tb, --template-body (string) The stringified template body
    
    --sn, --stack-name (string) The name of the AWS CloudFormation stack you're deploying to. If you specify an existing stack, the command updates the stack. If you specify a new stack, the command creates it.


OPTIONAL
    --w, --watch (boolean) Immediately starts Watch-Stack after deployment executes. If in Windows, it will open a new window, otherwise it will display in your current terminal window

    --s3, --s3-bucket (string) The name of the S3 bucket where this command uploads the artifacts that are referenced in your template.

    --s3-prefix (string) A prefix name that the command adds to the artifacts' name when it uploads them to the S3 bucket. The prefix name is a path name (folder name) for the S3 bucket.
    
    --parameter-overrides (list) A list of parameter structures that specify input parameters for your stack template. If you're updating a stack and you don't specify a parameter, the command uses the stack's existing value. For new stacks, you must specify parameters that don't have a default value. Syntax: ParameterKey1=ParameterValue1 ParameterKey2=ParameterValue2 ...
    
    --caps, --capabilities (list) A list of capabilities that you must specify before AWS Cloudformation can create certain stacks. Some stack templates might include resources that can affect permissions in your AWS account, for example, by creating new AWS Identity and Access Management (IAM) users. For those stacks, you must explicitly acknowledge their capabilities by specifying this parameter. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM. If you have IAM resources, you can specify either capability. If you have IAM resources with custom names, you must specify CAPABILITY_NAMED_IAM. If you don't specify this parameter, this action returns an InsufficientCapabilities error. Short names for

    --no-execute-changeset (boolean) Indicates whether to execute the change set. Specify this flag if you want to view your stack changes before executing the change set. The command creates an AWS CloudFormation change set and then exits without executing the change set. After you view the change set, execute it to implement your changes.

    --role-arn (string) The Amazon Resource Name (ARN) of an AWS Identity and Access Management (IAM) role that AWS CloudFormation assumes when executing the change set.

    --notification-arns (list) Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that AWS CloudFormation associates with the stack.

    --kms-key-id (string) The ID of an AWS KMS key that the command uses to encrypt artifacts that are at rest in the S3 bucket.

    --output-template-file (string) The path to the file where the command writes the output AWS CloudFormation template. If you don't specify a path, the command writes the template to the standard output.

    --use-json (boolean) Indicates whether to use JSON as the format for the output AWS CloudFormation template. YAML is used by default.

    --force-upload (boolean) Indicates whether to override existing files in the S3 bucket. Specify this flag to upload artifacts even if they match existing artifacts in the S3 bucket.
    
    --pn, --prof, --profile-name (string) The name of the saved configuration you would like to use for this command. Defaults to $env:AWS_PROFILE if set.

    --v, --verbose (boolean) Enables verbose output on the console.
'@

            }
        }
        Write-Host $message
    }
    else {
        $paramHash = @{}
        $dplParams = @{}
        $pkgParams = @{}
        if ($parameters -match '^--v$' -or $parameters -match '^--verbose$') {
            $VerboseSaved = $VerbosePreference
            $VerbosePreference = "Continue"
            $paramHash["Verbose"] = $true
        }
        Write-Verbose "Parsing parameters"
        $pkgAvails = @("TemplateBody","TemplateFile","S3Bucket","S3Prefix","KMSKeyId","OutputTemplateFile","UseJson","Force","ProfileName","Verbose")
        $dplAvails = @("TemplateBody","TemplateFile","StackName","Parameters","Capabilities","DoNotExecute","RoleARN","NotificationARNs","Watch","ProfileName","Verbose")
        $aliasHash = @{
            parameteroverrides = "Parameters"
            noexecutechangeset = "DoNotExecute"
            forceupload        = "Force"
            caps = "Capabilities"
            tf = "TemplateFile"
            tb = "TemplateBody"
            sn = "StackName"
            s3 = "S3Bucket"
            pn = "ProfileName"
            prof = "ProfileName"
            v = "Verbose"
            f = "Force"
            w = "Watch"
        }
        $capsHash = @{
            named = "CAPABILITY_NAMED_IAM"
            iam = "CAPABILITY_IAM"
        }
        $parameters | ForEach-Object {
            if ($_ -match '^--') {
                $i = 0
                $lastvar = $_ -replace '-'
                if ($aliasHash[$($lastvar.ToLower())]) {
                    $lastvar = $aliasHash[$($lastvar)]
                }
                $paramHash[$lastvar] = $true
            }
            else {
                $i++
                if ($lastvar -eq "Capabilities") {
                    if ($capsHash[$_]) {
                        $val = $capsHash[$_]
                    }
                    elseif ($_ -ne "CAPABILITY_NAMED_IAM" -and $_ -ne "CAPABILITY_IAM") {
                        $PSCmdlet.ThrowTerminatingError((New-VSError -String "You must specify 'named', 'iam', 'CAPABILITY_NAMED_IAM', or 'CAPABILITY_IAM' for -capabilities!"))
                    }
                    else {
                        $val = $_
                    }
                    if ($i -eq 1) {
                        $paramHash[$lastvar] = $val
                    }
                    else {
                        $paramHash[$lastvar] += $val
                    }
                }
                elseif ($_ -like "*=*" -and $_ -is [System.String]) {
                    $side = $_.Split("=").Trim('"')
                    if ($i -eq 1) {
                        $paramHash[$lastvar] = @{$side[0] = $side[1]}
                    }
                    else {
                        $paramHash[$lastvar][$side[0]] = $side[1]
                    }
                }
                elseif (($_ -eq "true" -or $_ -eq "false") -and $_ -is [System.String]) {
                    if ($_ -eq "true") {
                        $val = $true
                    }
                    else {
                        $val = $false
                    }
                    if ($i -eq 1) {
                        $paramHash[$lastvar] = $val
                    }
                    else {
                        $paramHash[$lastvar] += $val
                    }
                }
                else {
                    if ($i -eq 1) {
                        $paramHash[$lastvar] = $_
                    }
                    else {
                        $paramHash[$lastvar] += $_
                    }
                }
            }
        }
        if ($paramHash.Keys -notcontains "templatefile" -and $paramHash.Keys -notcontains "templatebody") {
            $PSCmdlet.ThrowTerminatingError((New-VSError -String "--template-file and --template-body not found in argument list! You must specify one for all actions"))
        }
        if ($paramHash.Keys -notcontains "stackname" -and ($action -eq "deploy" -or $action -eq "vaporize")) {
            $PSCmdlet.ThrowTerminatingError((New-VSError -String "--stack-name not found in argument list! You must specify a stack-name for either 'vsl deploy' or 'vsl vaporize'"))
        }
        if ($paramHash.Keys -notcontains "s3bucket" -and $paramHash.Keys -notcontains "stackname" -and ($action -eq "package" -or $action -eq "vaporize")) {
            $PSCmdlet.ThrowTerminatingError((New-VSError -String "--s3-bucket and --stackname not found in argument list! You must specify an s3-bucket for either 'vsl package' or 'vsl vaporize' or specify --stackname to upload to a bucket with the same name as the stack"))
        }
        if ($paramHash.Keys -notcontains "S3Bucket" -and $paramHash.Keys -contains "StackName") {
            $paramHash["S3Bucket"] = ($paramHash["StackName"]).ToLower()
        }
        foreach ($key in $paramHash.Keys) {
            if ($pkgAvails -contains $key) {
                $pkgParams[$key] = $paramHash[$key]
            }
            if ($dplAvails -contains $key) {
                $dplParams[$key] = $paramHash[$key]
            }
        }
        Write-Verbose "Parsed parameters:`n$($paramHash | Format-Table -AutoSize | Out-String)"
        Write-Verbose "Invoking action: $action"
        $VerbosePreference = $VerboseSaved
        try {
            switch ($action) {
                package {
                    Invoke-VSPackage @pkgParams
                }
                deploy {
                    Invoke-VSDeploy @dplParams
                }
                vaporize {
                    if ($dplParams.Keys -contains "TemplateFile") {
                        $dplParams.Remove("TemplateFile")
                    }
                    Invoke-VSPackage @pkgParams | Invoke-VSDeploy @dplParams
                }
            }
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
}

Export-ModuleMember -Function 'vsl'

function Add-Include {
    <#
    .SYNOPSIS
        Adds the transform function "AWS::Include" to a Vaporshell template

    .DESCRIPTION
        You can use the AWS::Include transform to work with template snippets that are stored separately from the main AWS CloudFormation template. When you specify Name: 'AWS::Include' and the Location parameter, the Transform key is a placeholder where snippets are injected. AWS CloudFormation inserts those snippets into your main template when Creating a Change Set or Updating Stacks Using Change Sets.

        You might have a Lambda function that you want to reuse in one or more AWS CloudFormation templates. The AWS::Include transform lets you create a reference to a transform snippet in an Amazon S3 bucket. You can add AWS::Include to the Transform function in your AWS CloudFormation template. The AWS::Include function behaves similarly to an include, copy, or import directive in programming languages.

    .LINK
        http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html

    .PARAMETER Location
        The location is an Amazon S3 URI, with a specific file name in an S3 bucket. For example, s3://MyBucketName/MyFile.yaml.

    .EXAMPLE
        Add-Include -Location "s3://MyAmazonS3BucketName/single_wait_condition.yaml"

        When the template is exported, this will convert to: {"Fn::Transform":{"Name":"AWS::Include","Parameters":{"Location":"s3://MyAmazonS3BucketName/single_wait_condition.yaml"}}}
        # If used at the top level, the Logical ID will be 'Transform' instead of 'Fn::Transform'.

    .NOTES
        When using AWS::Include, keep the following in mind:

            * AWS::Include is supported only in regions where AWS Lambda is available. For a list of regions where Lambda is available, see http://docs.aws.amazon.com/general/latest/gr/rande.html#lambda_region.
            * We currently support Amazon S3 URI, but no other Amazon S3 format (such as Amazon S3 ARN). It must be an Amazon S3 bucket, as opposed to something like a GitHub repository.
            * Anyone with access to the Amazon S3 URL can include the snippet in their template.
            * Your template snippet must be valid YAML or valid JSON.
            * A template snippet must pass validation checks for a create stack or update stack operation.
            * AWS CloudFormation resolves transforms first, and then processes the template. The resulting template must be valid JSON or YAML and must not exceed the template size limit.
            * If your snippets change, your stack doesn't automatically pick up those changes. To get those changes, you must update the stack with the updated snippets. If you update your stack, make sure your included snippets haven't changed without your knowledge. To verify before updating the stack, check the change set.
            * When using the update rollback feature, AWS CloudFormation uses a copy of the original template. It will roll back to the original template even if the included snippet was changed.
            * Nested transforms do not work because we do not process transforms iteratively.
            * When creating templates and snippets, you can mix YAML and JSON template languages.
            * We do not currently support using shorthand notations for YAML snippets.
            * You can use multiple transforms within a single template. Nevertheless, you cannot simultaneously have AWS::Include transforms at both the top level of a template and embedded within a section of a template.
            * You can provide a cross-region replication Amazon S3 URI with AWS::Include. Be sure to check S3 bucket names when accessing cross-region replication objects. For more information, see Cross-Region Replication.

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType([Include])]
    [cmdletbinding()]
    Param(
        [parameter(Mandatory,Position = 0)]
        [ValidatePattern("^s3:\/\/.*")]
        [string]
        $Location
    )
    $obj = [Include]::new($Location)
    Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n`n`t$(@{$obj.LogicalId = $obj.Props} | ConvertTo-Json -Depth 5 -Compress)`n"
    $obj
}

Export-ModuleMember -Function 'Add-Include'

function Update-VaporShellModule {
    [CmdletBinding(DefaultParameterSetName = 'Name', SupportsShouldProcess, ConfirmImpact = 'Low')]
    Param(
        [parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,Position = 0,ParameterSetName = 'Name')]
        [VaporShellModule[]]
        $Name,
        [parameter(ParameterSetName = 'All')]
        [switch]
        $All,
        [parameter()]
        [string]
        $MaximumVersion,
        [parameter()]
        [string]
        $RequiredVersion,
        [parameter()]
        [PSCredential]
        $Credential,
        [parameter()]
        [ValidateSet('AllUsers','CurrentUser')]
        [string]
        $Scope,
        [parameter()]
        [string]
        $Proxy,
        [parameter()]
        [PSCredential]
        $ProxyCredential,
        [parameter()]
        [switch]
        $Force,
        [parameter()]
        [switch]
        $AllowPrerelease,
        [parameter()]
        [switch]
        $PassThru
    )
    Begin {
        $installed = Get-Module VaporShell* -ListAvailable
    }
    Process {
        $params = $PSBoundParameters
        if ($PSCmdlet.ParameterSetName -eq 'All') {
            $params.Remove('All') | Out-Null
            $params['Name'] = [enum]::GetValues([VaporShellModule])
        }
        $names = $params['Name'] | ForEach-Object {
            if ($_ -ne 'VaporShell') {
                "VaporShell.$_"
            }
            else {
                $_
            }
        }
        if ($notInstalled = $names | Where-Object {$_ -notin $installed.Name}) {
            Write-Verbose "The following modules are not installed yet and will be installed:`n- $($notInstalled -join "`n- ")"
            $installParams = $params
            $installParams['Name'] = $notInstalled

            if (($installParams.Keys -join '-') -match 'Version|Prerelease') {
                $installParams.Remove('Name') | Out-Null
                foreach ($mod in $alreadyInstalled) {
                    Install-Module -Name $mod @installParams
                }
            }
            else {
                Install-Module @installParams
            }

            $params['Name'] = $names | Where-Object {$_ -in $installed.Name}
        }
        Write-Verbose "Updating modules:`n- $($params['Name'] -join "`n- ")"
        if (($params.Keys -join '-') -match 'Version|Prerelease') {
            $list = $params['Name']
            $params.Remove('Name') | Out-Null
            foreach ($mod in $list) {
                Update-Module -Name $mod @params
            }
        }
        else {
            Update-Module @params
        }
    }
}

Export-ModuleMember -Function 'Update-VaporShellModule'

$aliases = @()
$aliasHash = @{
    'FnBase64' = 'Add-FnBase64'
    '!Base64' = 'Add-FnBase64'
    'FnCidr' = 'Add-FnCidr'
    '!Cidr' = 'Add-FnCidr'
    'FnFindInMap' = 'Add-FnFindInMap'
    '!FindInMap' = 'Add-FnFindInMap'
    'FnGetAtt' = 'Add-FnGetAtt'
    '!GetAtt' = 'Add-FnGetAtt'
    'FnGetAZs' = 'Add-FnGetAZs'
    '!GetAZs' = 'Add-FnGetAZs'
    'FnImportValue' = 'Add-FnImportValue'
    '!ImportValue' = 'Add-FnImportValue'
    'FnJoin' = 'Add-FnJoin'
    '!Join' = 'Add-FnJoin'
    'FnRef' = 'Add-FnRef'
    '!Ref' = 'Add-FnRef'
    'FnSelect' = 'Add-FnSelect'
    '!Select' = 'Add-FnSelect'
    'FnSplit' = 'Add-FnSplit'
    '!Split' = 'Add-FnSplit'
    'FnSub' = 'Add-FnSub'
    '!Sub' = 'Add-FnSub'
    'FnTransform' = 'Add-FnTransform'
    '!Transform' = 'Add-FnTransform'
    'ConAnd' = 'Add-ConAnd'
    '!And' = 'Add-ConAnd'
    'ConEquals' = 'Add-ConEquals'
    '!Equals' = 'Add-ConEquals'
    'ConIf' = 'Add-ConIf'
    '!If' = 'Add-ConIf'
    'ConNot' = 'Add-ConNot'
    '!Not' = 'Add-ConNot'
    'ConOr' = 'Add-ConOr'
    '!Or' = 'Add-ConOr'
    'ConRef' = 'Add-ConRef'
    '!Condition' = 'Add-ConRef'
    '!Include' = 'Add-Include'
}
$aliasHash.GetEnumerator() | ForEach-Object {
    New-Alias -Name $_.Key -Value $_.Value -Force
    $aliases += $_.Key
}

$vars = @()
$varHash = @{
    '_AWSAccountId' = 'AWS::AccountId'
    '_AWSStackId' = 'AWS::StackId'
    '_AWSNoValue' = 'AWS::NoValue'
    '_AWSNotificationARNs' = 'AWS::NotificationARNs'
    '_AWSRegion' = 'AWS::Region'
    '_AWSStackName' = 'AWS::StackName'
    '_AWSInclude' = 'AWS::Include'
    '_AWSPartition' = 'AWS::Partition'
    '_AWSURLSuffix' = 'AWS::URLSuffix'
}
$varHash.GetEnumerator() | ForEach-Object {
    New-Variable -Name $_.Key -Value $_.Value -Force
    $vars += $_.Key
}

Export-ModuleMember -Variable $vars -Alias $aliases

$global:VSError = [System.Collections.Generic.List[VSError]]::new()