DEV Community

oscarrobert-star
oscarrobert-star

Posted on • Edited on

How to automate using MFA in AWS CLI

Problem statement

I am a frequent AWS CLI user, and this has never been a challenge until my organisation decided to enforce AWS MFA for all users - a recommended practice. This affected how I use AWS CLI. Check out how to enable and use MFA here.

If you are a cli user, here's how to use MFA in the cli.

Look at the hustle you have to go through when when using the cli. To add to that, imagine if you had 4 AWS environment. Will you be copying these credentials into the .aws/credentials file all the time, or update the environment variables each time?

Solution

In the DevOps spirit, I had to find a way to automate this. My solution has 2 parts:

  1. a python script that does the authentication for us and updates .aws/credentials with the new session access keys and token.

  2. creating aliases to run the python script each time we are authenticating.

Part 1 - The python script

The first part of this script is to define the command-line arguments needed when using the script. These include your mfa device ARN, the mfa token from the virtual device and the AWS cli profile you wish to authenticate mfa in. If you only have one profile, set the profile as default.

parser = argparse.ArgumentParser(description = description)
parser.add_argument('--mfa_serial_number', type = str, dest= 'mfa_serial_number', help = 'Enter your mfa device arn')
parser.add_argument('--profile_name', dest = 'profile_name', type = str, help = 'use default if you dont have any unique profiles')
parser.add_argument('--mfa_token_code', dest = 'mfa_token_code', type = str, help = 'MFA code from your virtual device')

# Parse the command-line arguments
args = parser.parse_args()

# Check that both inputs are present
if not all([mfa_serial_number, mfa_token_code, profile_name]):
    parser.error('Required: --mfa_token_code, --mfa_serial_number and --profile_name are required.')
Enter fullscreen mode Exit fullscreen mode

The second part of the script does the authentication for you and stores the credentials in variables. You have the liberty to set how long you want your session to last. In this script, it's set to one hour. You can set that time between 15 minutes and 36 hours (set in seconds).

##################### Authenticate MFA ##########################
session = boto3.Session(profile_name=profile_name) # enter profile 
sts_client = session.client('sts')
response = sts_client.get_session_token(
    DurationSeconds=3600, # Value changes between 900 (15 mins) - 129600 (36 hours)
    SerialNumber=mfa_serial_number,
    TokenCode=mfa_token_code
)
access_key = response['Credentials']['AccessKeyId']
secret_key = response['Credentials']['SecretAccessKey']
session_token = response['Credentials']['SessionToken']
Enter fullscreen mode Exit fullscreen mode

The final part of this script updates .aws/credentials for you. For it to work, you need to edit the credentials file and add a new profile block. You can add the following gibberish as the script will update it each time it's run with the correct details.

[mfa]
wertyio
asdfgh
zxcvbn
Enter fullscreen mode Exit fullscreen mode

This script will edit the file inplace by looking for the pattern [mfa] and edit 3 subsequent lines by adding access key, secret key and session token.

#################### Update .aws/credential file with the temporary mfa credentials #####################

# Define the pattern to search for
pattern = re.compile(r'\[mfa\]')

# Define the replacement content for the three lines
replacement_lines = [f'aws_access_key_id = {access_key}\n', f'aws_secret_access_key = {secret_key}\n', f'aws_session_token = {session_token}\n']
# print(replacement_lines)

home = os.path.expanduser("~")
# Open the file in inplace mode using fileinput
with fileinput.input(f'{home}/.aws/credentials', inplace=True) as file:
    # Iterate over the lines in the file
    for line in file:
        # If the line matches the pattern, print it to the console and then modify the next three lines
        if pattern.match(line):
            print(line, end='')
            # Print the new replacement lines
            for replacement_line in replacement_lines:
                print(replacement_line, end='')
            # Skip the next three lines in the original file
            next(file)
            next(file)
            next(file)
        # Otherwise, just print the line to the console
        else:
            print(line, end='')

Enter fullscreen mode Exit fullscreen mode

Part 2 - creating aliases

Aliases are shortcuts that you can create in your command line interface to execute a longer command with a shorter keyword. In this case, I have created aliases for the Python script that automates MFA. Here's how to create aliases in your command line interface.

This is just a personal preference. Feel free to modify these as you please.
At the time of writing I'm on a macbook hence i'll be using .zshrc to create my aliases.

The first alias I need is sourcing my python environment varible

alias source_env='source /Users/devops/Desktop/python_lab/env/bin/activate'
Enter fullscreen mode Exit fullscreen mode

The number of aliases you need depends on the number of environments you have. For example, if you have three environments say test, staging and prod the this is how to do it.

alias mfaTest='source_env  && python /Users/devops/Desktop/python_lab/enableMFA/newCred.py --mfa_serial_number $TEST_DEVICE --profile_name test --mfa_token_code '
alias mfaStaging='source_env  && python /Users/devops/Desktop/python_lab/enableMFA/newCred.py --mfa_serial_number $STAGING_DEVICE --profile_name staging --mfa_token_code '
alias mfaProd='source_env  && python /Users/devops/Desktop/python_lab/enableMFA/newCred.py --mfa_serial_number $PROD_DEVICE --profile_name prod --mfa_token_code '
Enter fullscreen mode Exit fullscreen mode

Notice my script takes in 3 arguments we defined earlier in the python script:

  1. mfa_serial_number - This is your AWS MFA device arn. (The assumption here is that you have set up MFA in the console). Also note that it's being passed from an environment variable which you can set up in the same file (.zshrc) using this line export PROD_DEVICE='arn:aws:iam::123456789012:mfa/my-device'

  2. profile_name - working with different aws account may require you to setup different aws cli profiles. Pass the correct profile name here.

  3. mfa_token_code - this is the MFA token code from your authenticator app. We'll pass this each time we are calling the aliases.

Finally, to authenticate call the alias with the authentication code following it as follows:

mfaTest 123456
Enter fullscreen mode Exit fullscreen mode

Finally, depending on the permissions you have you can describe any service to make sure you are in the right AWS account.

Alternatively:

If you are an admin and have IAM permissions in all the AWS accounts, you can create an IAM role with cross account permissions. This way you can assume an IAM role for whatever account you need.

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Image of Stellar post

How a Hackathon Win Led to My Startup Getting Funded

In this episode, you'll see:

  • The hackathon wins that sparked the journey.
  • The moment José and Joseph decided to go all-in.
  • Building a working prototype on Stellar.
  • Using the PassKeys feature of Soroban.
  • Getting funded via the Stellar Community Fund.

Watch the video 🎥

👋 Kindness is contagious

Please show some love ❤️ or share a kind word in the comments if you found this useful!

Got it!