diff --git a/LICENSE b/LICENSE index 1bb4f21..8d34180 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2025 Amazon.com, Inc. or its affiliates. and Frank Bernhardt. All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 0899588..e9b3817 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,53 @@ command before you can use any AWS SSO profile. However, once you have logged in once, you will be able to use any of the created profiles until your authorization token expires. +### Quick Start (One-Liner) + +Run the tool directly without downloading: + +**Bash (macOS/Linux):** +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.sh) +``` + +Example: +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.sh) \ + us-east-1 "https://mycompany.awsapps.com/start" +``` + +With options (non-interactive + default profile + account mappings): +```bash +bash <(curl -fsSL https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.sh) \ + -y \ + --map "Production:Prod" \ + --map "Development:Dev" \ + --default "DevAdminAccess" \ + us-east-1 "https://mycompany.awsapps.com/start" +``` + +**PowerShell (Windows):** +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.ps1))) -Region -StartUrl +``` + +Example: +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.ps1))) ` + -Region us-east-1 ` + -StartUrl "https://mycompany.awsapps.com/start" +``` + +With options (non-interactive + default profile + account mappings): +```powershell +& ([scriptblock]::Create((irm https://raw.githubusercontent.com/frank-bee/aws-sso-profile-tool/main/awsssoprofiletool.ps1))) ` + -Region us-east-1 ` + -StartUrl "https://mycompany.awsapps.com/start" ` + -NoPrompt ` + -Map "Production:Prod","Development:Dev" ` + -Default "DevAdminAccess" +``` + ### Installation To install the tool, follow these steps: @@ -39,17 +86,39 @@ the following methods: To run the script, do one of the following: * If the script is executable, run it with `./awsssoprofiletool.sh - []` +[-y] [--map "FROM:TO" ...] [--default ] []` * If the script is not executable, run it with `bash awsssoprofiletool.sh - []` +[-y] [--map "FROM:TO" ...] [--default ] []` The arguments are as follows: -* <region> - the region where AWS SSO is running -* <start_url> - the start URL from the AWS SSO page -* <profile_file> - where the profiles will be created; defaults to +* `-y` - (optional) non-interactive mode; overwrites config and creates all profiles without prompts +* `--map "FROM:TO"` - (optional) map account name FROM to TO in profile names; can be specified multiple times +* `--default ` - (optional) create a `[default]` profile that mirrors the specified profile name +* `` - the region where AWS SSO is running +* `` - the start URL from the AWS SSO page +* `` - where the profiles will be created; defaults to ~/.aws/config +**Example with account name mappings:** +```bash +./awsssoprofiletool.sh -y \ + --map "Production:Prod" \ + --map "Development:Dev" \ + us-east-1 https://example.awsapps.com/start +``` + +This would create profiles like `ProdAdminAccess` instead of `ProductionAdminAccess`. + +**Example with default profile:** +```bash +./awsssoprofiletool.sh -y \ + --default "DevAdminAccess" \ + us-east-1 https://example.awsapps.com/start +``` + +This creates all profiles plus a `[default]` section that mirrors `DevAdminAccess`, so AWS CLI commands work without specifying `--profile`. + ## Security See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. diff --git a/awsssoprofiletool.ps1 b/awsssoprofiletool.ps1 new file mode 100644 index 0000000..c363ba3 --- /dev/null +++ b/awsssoprofiletool.ps1 @@ -0,0 +1,412 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + AWS Profile Generator + +.DESCRIPTION + Generates AWS SSO profiles for all accounts and roles available to the user. + +.PARAMETER Region + The region where AWS SSO is configured (e.g., us-east-1) + +.PARAMETER StartUrl + The AWS SSO start URL + +.PARAMETER ProfileFile + The file where the profiles will be written (default is ~/.aws/config) + +.PARAMETER NoPrompt + Run in non-interactive mode. Overwrites the config file and creates all profiles without prompts. + +.PARAMETER Map + Map account names to shorter/different names in profile names. Format: "FROM:TO". + Can be specified multiple times. Example: -Map "Infrastructure:Infra" -Map "Development:Dev" + +.PARAMETER Default + Create a [default] profile that mirrors the specified profile name. + Example: -Default "DevAdministratorAccess" + +.EXAMPLE + .\awsssoprofiletool.ps1 -Region us-east-1 -StartUrl "https://example.awsapps.com/start" + +.EXAMPLE + .\awsssoprofiletool.ps1 -Region us-east-1 -StartUrl "https://example.awsapps.com/start" -NoPrompt + +.EXAMPLE + .\awsssoprofiletool.ps1 -Region us-east-1 -StartUrl "https://example.awsapps.com/start" -Map "Infrastructure:Infra" -Map "Development:Dev" + +.EXAMPLE + .\awsssoprofiletool.ps1 -Region us-east-1 -StartUrl "https://example.awsapps.com/start" -Default "DevAdministratorAccess" + +.NOTES + Copyright 2025 Amazon.com, Inc. or its affiliates. and Frank Bernhardt. All Rights Reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$Region, + + [Parameter(Mandatory = $true, Position = 1)] + [string]$StartUrl, + + [Parameter(Mandatory = $false, Position = 2)] + [string]$ProfileFile = (Join-Path $env:USERPROFILE ".aws\config"), + + [Parameter(Mandatory = $false)] + [switch]$NoPrompt, + + [Parameter(Mandatory = $false)] + [string[]]$Map, + + [Parameter(Mandatory = $false)] + [string]$Default +) + +Write-Host "AWS Profile Generator" + +$ACCOUNTPAGESIZE = 10 +$ROLEPAGESIZE = 10 + +# Variables to store default profile settings when found +$defaultAccountId = "" +$defaultRoleName = "" +$defaultRegionValue = "" +$defaultOutputValue = "" + +# Build account name mappings hashtable from -Map parameter +$accountMappings = @{} +if ($Map) { + foreach ($mapping in $Map) { + $parts = $mapping -split ':', 2 + if ($parts.Count -ne 2 -or [string]::IsNullOrEmpty($parts[0]) -or [string]::IsNullOrEmpty($parts[1])) { + Write-Error "Error: -Map value must be in FROM:TO format (e.g., 'Infrastructure:Infra')" + return + } + $accountMappings[$parts[0]] = $parts[1] + } +} + +# Check AWS CLI version +try { + $awsVersion = aws --version 2>&1 + if ($awsVersion -match "aws-cli/1") { + Write-Error "ERROR: This script requires AWS CLI v2 or higher" + return + } +} +catch { + Write-Error "ERROR: AWS CLI not found. Please install AWS CLI v2." + return +} + +# Overwrite option +if ($NoPrompt) { + $overwrite = $true +} +else { + Write-Host "" + $overwriteResp = Read-Host "Would you like to overwrite the output file ($ProfileFile)? (Y/n)" + if ([string]::IsNullOrEmpty($overwriteResp) -or $overwriteResp -eq 'Y' -or $overwriteResp -eq 'y') { + $overwrite = $true + } + else { + $overwrite = $false + } +} + +# Ensure .aws directory exists +$awsDir = Split-Path $ProfileFile -Parent +if (-not (Test-Path $awsDir)) { + New-Item -ItemType Directory -Path $awsDir -Force | Out-Null +} + +if ($overwrite) { + "" | Set-Content -Path $ProfileFile -NoNewline +} + +# Register client +Write-Host "" +Write-Host -NoNewline "Registering client... " + +$registerJson = aws sso-oidc register-client --client-name 'profiletool' --client-type 'public' --region $Region --output json 2>&1 + +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed" + Write-Error "$registerJson" + return +} + +$registerOutput = $registerJson | ConvertFrom-Json +Write-Host "Succeeded" + +$secret = $registerOutput.clientSecret +$clientId = $registerOutput.clientId + +# Start device authorization +Write-Host -NoNewline "Starting device authorization... " + +$authJson = aws sso-oidc start-device-authorization --client-id $clientId --client-secret $secret --start-url $StartUrl --region $Region --output json 2>&1 + +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed" + Write-Error "$authJson" + return +} + +$authOutput = $authJson | ConvertFrom-Json +Write-Host "Succeeded" + +$regUrl = $authOutput.verificationUriComplete +$deviceCode = $authOutput.deviceCode + +Write-Host "" +Write-Host "Open the following URL in your browser and sign in, then click the Allow button:" +Write-Host "" +Write-Host $regUrl +Write-Host "" +Read-Host "Press after you have signed in to continue..." + +# Get access token +Write-Host -NoNewline "Getting access token... " + +$tokenJson = aws sso-oidc create-token --client-id $clientId --client-secret $secret --grant-type 'urn:ietf:params:oauth:grant-type:device_code' --device-code $deviceCode --region $Region --output json 2>&1 + +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed" + Write-Error "$tokenJson" + return +} + +$tokenOutput = $tokenJson | ConvertFrom-Json +Write-Host "Succeeded" + +$token = $tokenOutput.accessToken + +# Set defaults for profiles +$defRegion = $Region +$defOutput = "json" + +# Batch or interactive +if ($NoPrompt) { + $interactive = $false + $awsRegion = $defRegion + $output = $defOutput +} +else { + Write-Host "" + Write-Host "This script can create all profiles with default values" + Write-Host "or it can prompt you regarding each profile before it gets created." + Write-Host "" + $resp = Read-Host "Would you like to be prompted for each profile? (Y/n)" + + # Default to not prompted (N) + $interactive = $false + $awsRegion = $defRegion + $output = $defOutput + + if ($resp -eq 'Y' -or $resp -eq 'y') { + $interactive = $true + } +} + +# Retrieve accounts +Write-Host "" +Write-Host -NoNewline "Retrieving accounts... " + +$accountsJson = aws sso list-accounts --access-token $token --page-size $ACCOUNTPAGESIZE --region $Region --output json 2>&1 + +if ($LASTEXITCODE -ne 0) { + Write-Host "Failed" + Write-Error "$accountsJson" + return +} + +$accountsOutput = $accountsJson | ConvertFrom-Json +$accounts = $accountsOutput.accountList | Sort-Object -Property accountName +Write-Host "Succeeded" + +$createdProfiles = @() + +# Write sso-session block +Add-Content -Path $ProfileFile -Value "" +Add-Content -Path $ProfileFile -Value "#BEGIN_AWS_SSO_PROFILES" +Add-Content -Path $ProfileFile -Value "" +Add-Content -Path $ProfileFile -Value "[sso-session my-sso]" +Add-Content -Path $ProfileFile -Value "sso_start_url = $StartUrl" +Add-Content -Path $ProfileFile -Value "sso_region = $Region" +Add-Content -Path $ProfileFile -Value "sso_registration_scopes = sso:account:access" + + +# Process each account +foreach ($account in $accounts) { + $acctNum = $account.accountId + $acctName = $account.accountName + + # Apply account name mappings (if -Map was specified) + if ($accountMappings.ContainsKey($acctName)) { + $acctName = $accountMappings[$acctName] + } + + Write-Host "" + Write-Host "Adding roles for account $acctNum ($acctName)..." + + # Add comment to profile file + Add-Content -Path $ProfileFile -Value "" + Add-Content -Path $ProfileFile -Value "# $acctName ($acctNum)" + + $rolesJson = aws sso list-account-roles --account-id $acctNum --access-token $token --page-size $ROLEPAGESIZE --region $Region --output json 2>&1 + + if ($LASTEXITCODE -ne 0) { + Write-Host "Failed to retrieve roles." + Write-Error "$rolesJson" + return + } + + $rolesOutput = $rolesJson | ConvertFrom-Json + $roles = $rolesOutput.roleList + + foreach ($role in $roles) { + $roleName = $role.roleName + + Write-Host "" + + if ($interactive) { + $create = Read-Host "Create a profile for $roleName role? (Y/n)" + if ($create -eq 'n' -or $create -eq 'N') { + continue + } + + Write-Host "" + $inputRegion = Read-Host "CLI default client Region [$defRegion]" + if (-not [string]::IsNullOrEmpty($inputRegion)) { + $awsRegion = $inputRegion + $defRegion = $awsRegion + } + else { + $awsRegion = $defRegion + } + + $inputOutput = Read-Host "CLI default output format [$defOutput]" + if (-not [string]::IsNullOrEmpty($inputOutput)) { + $output = $inputOutput + $defOutput = $output + } + else { + $output = $defOutput + } + } + + # Sanitize account name (keep only alphanumeric and hyphen) + $safeAcctName = $acctName -replace '[^a-zA-Z0-9-]', '' + $defaultProfileName = "${safeAcctName}${roleName}" + + while ($true) { + if ($interactive) { + $inputProfileName = Read-Host "CLI profile name [$defaultProfileName]" + if (-not [string]::IsNullOrEmpty($inputProfileName)) { + $profileName = $inputProfileName + } + else { + $profileName = $defaultProfileName + } + } + else { + $profileName = $defaultProfileName + } + + # Check if profile already exists + if (Test-Path $ProfileFile) { + $existingContent = Get-Content $ProfileFile -Raw + if ($existingContent -match "\[\s*profile\s+$([regex]::Escape($profileName))\s*\]") { + Write-Host "Profile name already exists!" + if ($interactive) { + continue + } + else { + Write-Host "Skipping..." + break + } + } + } + + Write-Host -NoNewline "Creating $profileName... " + Add-Content -Path $ProfileFile -Value "" + Add-Content -Path $ProfileFile -Value "[profile $profileName]" + Add-Content -Path $ProfileFile -Value "sso_session = my-sso" + Add-Content -Path $ProfileFile -Value "sso_account_id = $acctNum" + Add-Content -Path $ProfileFile -Value "sso_role_name = $roleName" + Add-Content -Path $ProfileFile -Value "region = $awsRegion" + Add-Content -Path $ProfileFile -Value "output = $output" + Write-Host "Succeeded" + $createdProfiles += $profileName + + # Check if this profile should be the default + if (-not [string]::IsNullOrEmpty($Default) -and $profileName -eq $Default) { + $defaultAccountId = $acctNum + $defaultRoleName = $roleName + $defaultRegionValue = $awsRegion + $defaultOutputValue = $output + } + break + } + } + + Write-Host "" + Write-Host "Done adding roles for AWS account $acctNum ($acctName)" +} + +# Write default profile if specified and found +if (-not [string]::IsNullOrEmpty($Default)) { + if (-not [string]::IsNullOrEmpty($defaultAccountId)) { + Add-Content -Path $ProfileFile -Value "" + Add-Content -Path $ProfileFile -Value "# Default profile (mirrors $Default)" + Add-Content -Path $ProfileFile -Value "[default]" + Add-Content -Path $ProfileFile -Value "sso_session = my-sso" + Add-Content -Path $ProfileFile -Value "sso_account_id = $defaultAccountId" + Add-Content -Path $ProfileFile -Value "sso_role_name = $defaultRoleName" + Add-Content -Path $ProfileFile -Value "region = $defaultRegionValue" + Add-Content -Path $ProfileFile -Value "output = $defaultOutputValue" + Write-Host "" + Write-Host "Created [default] profile mirroring $Default" + } + else { + Write-Host "" + Write-Host "WARNING: -Default profile '$Default' was not found among created profiles" + } +} + +# Add old profile +Add-Content -Path $ProfileFile -Value "" +Add-Content -Path $ProfileFile -Value "" +Add-Content -Path $ProfileFile -Value "[profile old]" +Add-Content -Path $ProfileFile -Value "region = us-east-1" +Add-Content -Path $ProfileFile -Value "" +Add-Content -Path $ProfileFile -Value "#END_AWS_SSO_PROFILES" + +Write-Host "" +Write-Host "Processing complete." +Write-Host "" +Write-Host "Added the following profiles to ${ProfileFile}:" +Write-Host "" + +foreach ($profile in $createdProfiles) { + Write-Host $profile +} + +Write-Host "" diff --git a/awsssoprofiletool.sh b/awsssoprofiletool.sh index 55c7b27..0685a9a 100755 --- a/awsssoprofiletool.sh +++ b/awsssoprofiletool.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2025 Amazon.com, Inc. or its affiliates. and Frank Bernhardt. All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,30 +20,128 @@ # # Syntax: # -# ssoprofiletool [] +# awsssoprofiletool.sh [-y] [--map "FROM:TO" ...] [--default ] [] # # is the region where AWS SSO is configured (e.g., us-east-1) # is the AWS SSO start URL # is the file where the profiles will be written (default is # ~/.aws/config) +# +# Options: +# -y : Run in non-interactive mode. Overwrites the config file and creates all +# profiles without prompts. +# --map "FROM:TO" : Map account name FROM to TO in profile names. Can be +# specified multiple times. Example: --map "Infrastructure:Infra" +# --default : Create a [default] profile that mirrors the specified +# profile. Example: --default "DevAdministratorAccess" ACCOUNTPAGESIZE=10 ROLEPAGESIZE=10 PROFILEFILE="$HOME/.aws/config" -if [ $# -lt 2 ]; -then - echo "Syntax: $0 []" - exit 1 +# Store account name mappings as newline-separated "FROM:TO" entries (bash 3 compatible) +account_mappings="" + +noprompt=false +default_profile="" + +# Variables to store default profile settings when found +default_account_id="" +default_role_name="" +default_region="" +default_output="" + +# Parse options +while [ $# -gt 0 ]; do + case "$1" in + -y) + noprompt=true + shift + ;; + --map) + if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then + echo "Error: --map requires a value in FROM:TO format" + exit 1 + fi + # Validate format contains exactly one colon with non-empty parts + from_name="${2%%:*}" + to_name="${2#*:}" + if [ -z "$from_name" ] || [ -z "$to_name" ] || [ "$from_name" = "$2" ]; then + echo "Error: --map value must be in FROM:TO format (e.g., 'Infrastructure:Infra')" + exit 1 + fi + account_mappings="${account_mappings}${2} +" + shift 2 + ;; + --default) + if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then + echo "Error: --default requires a profile name" + exit 1 + fi + default_profile="$2" + shift 2 + ;; + -*) + echo "Unknown option: $1" + echo "Syntax: $0 [-y] [--map \"FROM:TO\" ...] [--default ] []" + exit 1 + ;; + *) + break + ;; + esac +done + +# Function to apply account name mapping (bash 3 compatible) +apply_mapping() { + local name="$1" + local mapped + # Search for mapping in the stored mappings + mapped=$(echo "$account_mappings" | grep "^${name}:" | head -1) + if [ -n "$mapped" ]; then + echo "${mapped#*:}" + else + echo "$name" + fi +} + +if [ $# -lt 2 ]; then + echo "Syntax: $0 [-y] [--map \"FROM:TO\" ...] [--default ] []" + exit 1 fi -if [ $# -eq 3 ]; -then +region=$1 +starturl=$2 + +if [ $# -eq 3 ]; then profilefile=$3 else profilefile=$PROFILEFILE fi +# Overwrite option +if [ "$noprompt" = true ]; then + overwrite=true +else + echo + printf "%s" "Would you like to overwrite the output file ($profilefile)? (Y/n): " + read overwrite_resp < /dev/tty + if [ -z "$overwrite_resp" ]; + then + overwrite=true + elif [ "$overwrite_resp" == 'n' ] || [ "$overwrite_resp" == 'N' ]; + then + overwrite=false + else + overwrite=true + fi +fi + +if [ "$overwrite" = true ]; then + > "$profilefile" +fi + if [[ $(aws --version) == aws-cli/1* ]] then echo "ERROR: $0 requires AWS CLI v2 or higher" @@ -53,9 +151,9 @@ fi # Get secret and client ID to begin authentication session echo -echo -n "Registering client... " +printf "%s" "Registering client... " -out=$(aws sso-oidc register-client --client-name 'profiletool' --client-type 'public' --region "$1" --output text) +out=$(aws sso-oidc register-client --client-name 'profiletool' --client-type 'public' --region "$region" --output text) if [ $? -ne 0 ]; then @@ -65,14 +163,14 @@ else echo "Succeeded" fi -secret=$(awk -F ' ' '{print $3}' <<< "$out") -clientid=$(awk -F ' ' '{print $1}' <<< "$out") +secret=$(awk '{print $3}' <<< "$out") +clientid=$(awk '{print $1}' <<< "$out") # Start the authentication process -echo -n "Starting device authorization... " +printf "%s" "Starting device authorization... " -out=$(aws sso-oidc start-device-authorization --client-id "$clientid" --client-secret "$secret" --start-url "$2" --region "$1" --output text) +out=$(aws sso-oidc start-device-authorization --client-id "$clientid" --client-secret "$secret" --start-url "$starturl" --region "$region" --output text) if [ $? -ne 0 ]; then @@ -82,8 +180,8 @@ else echo "Succeeded" fi -regurl=$(awk -F ' ' '{print $6}' <<< "$out") -devicecode=$(awk -F ' ' '{print $1}' <<< "$out") +regurl=$(awk '{print $6}' <<< "$out") +devicecode=$(awk '{print $1}' <<< "$out") echo echo "Open the following URL in your browser and sign in, then click the Allow button:" @@ -92,13 +190,13 @@ echo "$regurl" echo echo "Press after you have signed in to continue..." -read continue +read continue < /dev/tty # Get the access token for use in the remaining API calls -echo -n "Getting access token... " +printf "%s" "Getting access token... " -out=$(aws sso-oidc create-token --client-id "$clientid" --client-secret "$secret" --grant-type 'urn:ietf:params:oauth:grant-type:device_code' --device-code "$devicecode" --region "$1" --output text) +out=$(aws sso-oidc create-token --client-id "$clientid" --client-secret "$secret" --grant-type 'urn:ietf:params:oauth:grant-type:device_code' --device-code "$devicecode" --region "$region" --output text) if [ $? -ne 0 ]; then @@ -108,44 +206,49 @@ else echo "Succeeded" fi -token=$(awk -F ' ' '{print $1}' <<< "$out") +token=$(awk '{print $1}' <<< "$out") # Set defaults for profiles -defregion="$1" +defregion="$region" defoutput="json" # Batch or interactive -echo -echo "$0 can create all profiles with default values" -echo "or it can prompt you regarding each profile before it gets created." -echo -echo -n "Would you like to be prompted for each profile? (Y/n): " -read resp < /dev/tty -if [ -z "$resp" ]; -then - interactive=true -elif [ "$resp" == 'n' ] || [ "$resp" == 'N' ]; -then +if [ "$noprompt" = true ]; then interactive=false awsregion=$defregion output=$defoutput else - interactive=true + echo + echo "$0 can create all profiles with default values" + echo "or it can prompt you regarding each profile before it gets created." + echo + printf "%s" "Would you like to be prompted for each profile? (Y/n): " + read resp < /dev/tty + # Default to not prompted (N) + interactive=false + awsregion=$defregion + output=$defoutput + if [ "$resp" == 'Y' ] || [ "$resp" == 'y' ]; + then + interactive=true + fi fi # Retrieve accounts first echo -echo -n "Retrieving accounts... " +printf "%s" "Retrieving accounts... " -acctsfile="$(mktemp ./sso.accts.XXXXXX)" +acctsfile="$(mktemp /tmp/sso.accts.XXXXXX)" # Set up trap to clean up temp file trap '{ rm -f "$acctsfile"; echo; exit 255; }' SIGINT SIGTERM -aws sso list-accounts --access-token "$token" --page-size $ACCOUNTPAGESIZE --region "$1" --output text > "$acctsfile" +aws sso list-accounts --access-token "$token" --page-size $ACCOUNTPAGESIZE --region "$region" --output text > "$acctsfile" +# Sort by account name (3rd column) +sort -t $'\t' -k 3 -o "$acctsfile" "$acctsfile" if [ $? -ne 0 ]; then @@ -158,22 +261,35 @@ fi declare -a created_profiles echo "" >> "$profilefile" -echo "###" >> "$profilefile" -echo "### The section below added by awsssoprofiletool.sh" >> "$profilefile" -echo "###" >> "$profilefile" +echo "#BEGIN_AWS_SSO_PROFILES" >> "$profilefile" + +echo "" >> "$profilefile" +echo "[sso-session my-sso]" >> "$profilefile" +echo "sso_start_url = $starturl" >> "$profilefile" +echo "sso_region = $region" >> "$profilefile" +echo "sso_registration_scopes = sso:account:access" >> "$profilefile" + # Read in accounts while IFS=$'\t' read skip acctnum acctname acctowner; do + # Apply account name mappings (if --map was specified) + acctname=$(apply_mapping "$acctname") + echo echo "Adding roles for account $acctnum ($acctname)..." - rolesfile="$(mktemp ./sso.roles.XXXXXX)" + + # Add comment to profile file + echo "" >> "$profilefile" + echo "# $acctname ($acctnum)" >> "$profilefile" + + rolesfile="$(mktemp /tmp/sso.roles.XXXXXX)" # Set up trap to clean up both temp files trap '{ rm -f "$rolesfile" "$acctsfile"; echo; exit 255; }' SIGINT SIGTERM - aws sso list-account-roles --account-id "$acctnum" --access-token "$token" --page-size $ROLEPAGESIZE --region "$1" --output text > "$rolesfile" + aws sso list-account-roles --account-id "$acctnum" --access-token "$token" --page-size $ROLEPAGESIZE --region "$region" --output text > "$rolesfile" if [ $? -ne 0 ]; then @@ -186,7 +302,7 @@ do echo if $interactive ; then - echo -n "Create a profile for $rolename role? (Y/n): " + printf "%s" "Create a profile for $rolename role? (Y/n): " read create < /dev/tty if [ -z "$create" ]; then @@ -197,21 +313,22 @@ do fi echo - echo -n "CLI default client Region [$defregion]: " + printf "%s" "CLI default client Region [$defregion]: " read awsregion < /dev/tty if [ -z "$awsregion" ]; then awsregion=$defregion ; fi defregion=$awsregion - echo -n "CLI default output format [$defoutput]: " + printf "%s" "CLI default output format [$defoutput]: " read output < /dev/tty if [ -z "$output" ]; then output=$defoutput ; fi defoutput=$output fi - p="$rolename-$acctnum" + safe_acctname=$(echo "$acctname" | tr -cd '[:alnum:]-') + p="${safe_acctname}${rolename}" while true ; do if $interactive ; then - echo -n "CLI profile name [$p]: " + printf "%s" "CLI profile name [$p]: " read profilename < /dev/tty if [ -z "$profilename" ]; then profilename=$p ; fi if [ -f "$profilefile" ]; @@ -238,17 +355,24 @@ do fi fi done - echo -n "Creating $profilename... " + printf "%s" "Creating $profilename... " echo "" >> "$profilefile" echo "[profile $profilename]" >> "$profilefile" - echo "sso_start_url = $2" >> "$profilefile" - echo "sso_region = $1" >> "$profilefile" + echo "sso_session = my-sso" >> "$profilefile" echo "sso_account_id = $acctnum" >> "$profilefile" echo "sso_role_name = $rolename" >> "$profilefile" echo "region = $awsregion" >> "$profilefile" echo "output = $output" >> "$profilefile" echo "Succeeded" created_profiles+=("$profilename") + + # Check if this profile should be the default + if [ -n "$default_profile" ] && [ "$profilename" = "$default_profile" ]; then + default_account_id="$acctnum" + default_role_name="$rolename" + default_region="$awsregion" + default_output="$output" + fi done < "$rolesfile" rm "$rolesfile" @@ -258,10 +382,31 @@ do done < "$acctsfile" rm "$acctsfile" +# Write default profile if specified and found +if [ -n "$default_profile" ]; then + if [ -n "$default_account_id" ]; then + echo "" >> "$profilefile" + echo "# Default profile (mirrors $default_profile)" >> "$profilefile" + echo "[default]" >> "$profilefile" + echo "sso_session = my-sso" >> "$profilefile" + echo "sso_account_id = $default_account_id" >> "$profilefile" + echo "sso_role_name = $default_role_name" >> "$profilefile" + echo "region = $default_region" >> "$profilefile" + echo "output = $default_output" >> "$profilefile" + echo + echo "Created [default] profile mirroring $default_profile" + else + echo + echo "WARNING: --default profile '$default_profile' was not found among created profiles" + fi +fi + echo >> "$profilefile" -echo "###" >> "$profilefile" -echo "### The section above added by awsssoprofiletool.sh" >> "$profilefile" -echo "###" >> "$profilefile" +echo "" >> "$profilefile" +echo "[profile old]" >> "$profilefile" +echo "region = us-east-1" >> "$profilefile" + +echo "#END_AWS_SSO_PROFILES" >> "$profilefile" echo echo "Processing complete."