Public/IAM/Set-ATIAMCliExternalCredentials.ps1

function Set-ATIAMCliExternalCredentials
{
<#
    .SYNOPSIS
        Configue aws-toolbox as an AWS CLI Credential Process

    .DESCRIPTION
        This cmdlet maps a PowerShell stored profile into the AWS CLI credential file
        as a provider of external credentials. This is useful to get AWS CLI to use a
        saved SAML profile when e.g. you use Active Directory integration to authenticate
        with AWS

    .PARAMETER ProfileName
        Name of PowerShell stored profile to use.

    .PARAMETER CliProfileName
        Name of profile to create in CLI credentials file. If omitted, then the name
        passed to ProfileName will be used.

    .EXAMPLE
        Set-ATIAMCliExternalCredentials -ProfileName MySamlProfile
        Creates an AWS CLI external credential profile named 'MySamlProfile' that maps onto the PowerShell profile named 'MySamlProfile'

    .EXAMPLE
        Set-ATIAMCliExternalCredentials -ProfileName MySamlProfile -CliProfileName MyCliSamlProfile
        Creates an AWS CLI external credential profile named 'MyCliSamlProfile' that maps onto the PowerShell profile named 'MySamlProfile'
#>

    [CmdletBinding()]
    param
    (
        [string]$CliProfileName
    )

    DynamicParam
    {
        $validateSet = Get-AWSCredential -ListProfileDetail | Select-Object -ExpandProperty ProfileName | Sort-Object -Unique
        New-DynamicParam -Name ProfileName -Mandatory -ValidateSet $validateSet -HelpMessage 'Name of PowerShell stored profile to use'
    }

    begin
    {
        [scriptblock]$cacheScriptBlock = {
            #!/usr/bin/env pwsh

            # Silence warnings, or aws will consume them and fail
            $warnPef = $WarningPreference

            try
            {
                $WarningPreference = 'SilentlyContinue'
                $credentialCache = '{0}'
                $profileName = '{1}'

                if (Test-Path -Path $credentialCache)
                {
                    $profiles = Get-Content -Raw -Path $credentialCache | ConvertFrom-Json
                }
                else
                {
                    $profiles = @()
                }

                $profile = $profiles | Where-Object {
                    $_.Name -eq $profileName
                }

                $cred = @{
                    Expiration = [DateTime]'1900-01-01'
                }

                if ($profile)
                {
                    # Decode secure string back to JSON
                    $ss = ConvertTo-SecureString $profile.Credential
                    $json = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ss)))

                    # Check credential time
                    $cred = $json | ConvertFrom-Json
                }

                if ($cred.Expiration -le [datetime]::UtcNow.AddMinutes(-5))
                {
                    # Regenerate
                    if ($PSVersionTable.PSVersion.Major -lt 6)
                    {
                        Import-Module aws-toolbox
                    }
                    else
                    {
                        Import-Module aws-toolbox.netcore
                    }

                    Set-AwsCredential -ProfileName $profileName
                    $json = Get-ATIAMSessionCredentials -AwsCli

                    $encryptedCredential = $json | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString

                    if ($profile)
                    {
                        $profile.Credential = $encryptedCredential
                    }
                    else
                    {
                        $profiles += New-Object PSObject -Property @{
                            Name = $profileName
                            Credential = $encryptedCredential
                        }
                    }

                    # Write out credential cache
                    $profiles | ConvertTo-Json | Set-Content -Path $credentialCache -Force
                }
            }
            finally
            {
                $WarningPreference = $warnPef
            }

            # Emit credential
            $json
        }

        foreach ($p in $PSBoundParameters.Keys)
        {
            if (-not (Get-Variable -Name $p -Scope Local -ErrorAction SilentlyContinue))
            {
                Set-Variable -Name $p -Value $PSBoundParameters[$p] -Scope Local
            }
        }
        if ($null -eq $ProfileName)
        {
            throw "Profile Name not set"
        }
    }

    process
    {}

    end
    {
        $windows = $PSVersionTable.PSVersion.Major -lt 6 -or $IsWindows
        $credentialStore = Get-CliConfiguration -ConfigurationFileName credentials

        if ([string]::IsNullOrEmpty($CliProfileName))
        {
            $CliProfileName = $ProfileName
        }

        $creds = Read-CliConfigurationFile -Credentials

        if ($creds.ContainsKey($CliProfileName))
        {
            $creds.Remove($CliProfileName)
        }

        # Write cache script for this profile
        $cacheScriptDir = Join-Path $credentialStore.Directory 'aws-toolbox-cache'
        if (-not (Test-Path -Path $cacheScriptDir -PathType Container))
        {
            New-Item -Path $cacheScriptDir -ItemType Directory | Out-Null
        }

        $cacheScriptPath = Join-Path $cacheScriptDir "$($ProfileName).ps1"
        $credentialCachePath = Join-Path $cacheScriptDir "credential-cache"

        $cacheScript = ($cacheScriptBlock.ToString().Replace('{0}', $credentialCachePath).Replace('{1}', $ProfileName)) -split ([System.Environment]::NewLine)

        $line = 0

        # Remove leading blank lines
        while([string]::IsNullOrEmpty($cacheScript[$line]))
        {
            $line++
        }

        $cacheScript[$line] -match '^(\s*)' | Out-Null
        $totalLines = $cacheScript.Length
        $blanks = ($Matches.1).Length

        # Remove leading space
        $cacheScript = $(
            for($i = $line; $i -lt $totalLines; ++$i)
            {
                if ($cacheScript[$i].Length -ge $blanks)
                {
                    $cacheScript[$i].Substring($blanks)
                }
                else
                {
                    $cacheScript[$i]
                }
            }
        ) -join ([System.Environment]::NewLine)

        # Write Cache script
        $cacheScript | Set-Content -Path $cacheScriptPath -Force -Encoding ascii

        if (-not $windows)
        {
            # Make cache script executable
            & chmod +x $cacheScript
        }

        $creds[$CliProfileName] = @{
            credential_process = (Get-CredentialProcess -CacheScriptPath $cacheScriptPath).CredentialProcess -f $ProfileName
        }

        $creds | Write-CliConfigurationFile -Credentials
    }
}