Skip to content

Instantly share code, notes, and snippets.

@robvanderleek
Last active March 11, 2024 19:56
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robvanderleek/7dd54a53ce72e835ac1f319ef2631c5e to your computer and use it in GitHub Desktop.
Save robvanderleek/7dd54a53ce72e835ac1f319ef2631c5e to your computer and use it in GitHub Desktop.
AWS SSO command-line login script (that also updates .aws/credentials)

AWS SSO command-line login script (that also updates .aws/credentials)

The AWS Command Line Interface tool from Amazon (for macOS available here on Homebrew) makes it possible to login to AWS through a SSO (Single Sign-On) identity provider such as Okta. However, if you login via "aws sso login" the AWS credentials file (located at ~/.aws/credentials) is not updated, this is a problem for tools/libraries that rely on that file.

This script is a wrapper around aws sso login that also updates the .aws/credentials file. It only requires AWS CLI and Python 3 to run.

Configuration

Make sure your .aws/config file has a section (or multiple sections) that have SSO configuation options. For example:

[production]
sso_start_url = 
sso_region = 
sso_account_id = 
sso_role_name = 
region =

[development]
sso_start_url = 
sso_region = 
sso_account_id = 
sso_role_name = 
region = 

Usage

Save the script file and give it execute permissions (chmod 755 aws-sso-login). Then run it without arguments to get a list of configured SSO profiles:

$ ./aws-sso-login
$ Available profiles: ['production', 'development']

To login to a profile, run it with the name of that profile as the single argument:

$ ./aws-sso-login production

If there's no active session you will be redirected to a browser to complete the login. After a succesful login the .aws/credentials is updated.

#!/usr/bin/env python3
from configparser import ConfigParser
import json
import os
import subprocess
import sys
CREDENTIALS_PATH = os.path.join(os.path.expanduser("~"), '.aws', 'credentials')
CONFIG_PATH = os.path.join(os.path.expanduser("~"), '.aws', 'config')
CACHE_DIR = os.path.join(os.path.expanduser("~"), '.aws', 'cli', 'cache')
def read_credentials_file():
with open(CREDENTIALS_PATH, 'r') as f:
return f.read()
def update_content(content, sso_creds):
magic_header = '### GENERATED, DO NOT EDIT BELOW\n'
defaultSection = (
f'{magic_header}\n[default]\n'
f'aws_access_key_id={sso_creds["AccessKeyId"]}\n'
f'aws_secret_access_key={sso_creds["SecretAccessKey"]}\n'
f'aws_session_token={sso_creds["SessionToken"]}\n'
)
content = read_credentials_file()
if magic_header in content:
content = content[0:content.index(magic_header)]
else:
content += '\n'
return content + defaultSection
def write_credentials_file(content):
with open(CREDENTIALS_PATH, 'w') as f:
f.write(content)
def update_aws_credentials_file(sso_creds):
content = read_credentials_file()
updated = update_content(content, sso_creds)
write_credentials_file(updated)
def list_credentials_cache_files():
if not os.path.exists(CACHE_DIR):
return []
all_files = [os.path.join(CACHE_DIR, f) for f in os.listdir(CACHE_DIR)]
cache_files = [f for f in all_files if f.endswith('.json')]
return cache_files
def clear_credentials_cache_files():
for f in list_credentials_cache_files():
os.remove(f)
def read_credentials_from_file(filename):
with open(filename, 'r') as f:
obj = json.load(f)
return obj['Credentials']
def already_logged_in(profile):
cmd = f'aws sts get-caller-identity --profile {profile} 2>&1 >/dev/null'
return subprocess.call(cmd, shell=True) == 0
def list_sso_profiles():
config = ConfigParser()
config.read(CONFIG_PATH)
result = []
for section in config.sections():
if config.has_option(section, 'sso_start_url'):
section = section.replace('profile ', '')
result.append(section)
return result
def login(profile):
if not already_logged_in(profile):
subprocess.call(f'aws sso login --profile {profile}', shell=True)
if not already_logged_in(profile):
print('Something went wrong')
sys.exit(1)
if __name__ == '__main__':
sso_profiles = list_sso_profiles()
if len(sys.argv) != 2 or sys.argv[1] not in sso_profiles:
print(f'Available profiles: {sso_profiles}')
sys.exit(1)
profile = sys.argv[1]
clear_credentials_cache_files()
login(profile)
cache_files = list_credentials_cache_files()
creds = read_credentials_from_file(cache_files[0])
update_aws_credentials_file(creds)
print(f'Logged in to profile: {profile}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment