AWS CLIでMFAを使うときの作業を自動化する

## 概要

IAMユーザの権限にAWS CLIからの利用にもMFAが強制されている場合、

1. MFAデバイスで一時解除コードを取得
1. `get-session-token`で一時的なトークンを取得。このとき一時解除コードを入力
1. 取得したトークンを環境変数または設定ファイルに投入
1. AWS CLI実行

という手順が必要になります。

https://repost.aws/ja/knowledge-center/authenticate-mfa-cli

AWS CLIが実行できるようにするまでの準備が面倒なので自動化しようというのが今回の趣旨です。


## 手動だと面倒なところ

`get-session-token`すると下記のような出力が得られます。

```json:get-session-token実行例
$ aws --profile <プロファイル> sts get-session-token --serial-number <MFAデバイス識別子> --token-code <一時解除コード> --output json
{
    "Credentials": {
        "AccessKeyId": "ASXXXXXXXXXXXXXXXXXX",
        "SecretAccessKey": "jexxxxbCxxxx11xxxxE2xxxxB3xxxxCbxxxx4zw8",
        "SessionToken": "IQoJb3JpZ2luX2VjEG0aDmFwLW5vcnRoZWFzdC0xIkgwRgIhAP2QuV9k(以下略)",
        "Expiration": "2024-05-28T16:49:28+00:00"
    }
}
```

AWS CLIを使うには、上記からAccessKeyId、SecretAccessKey、SessionTokenを抜き出して、次のいずれかの作業を行う必要があります。

選択肢1：環境変数AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENにそれぞれを設定する
```bash:選択肢1：環境変数に設定
export AWS_ACCESS_KEY_ID=ASXXXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=jexxxxbCxxxx11xxxxE2xxxxB3xxxxCbxxxx4zw8
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEG0aDmFwLW5vcnRoZWFzdC0xIkgwRgIhAP2QuV9k(以下略)
```

選択肢2：`~/.aws/config`と`~/.aws/credentials`にMFA用プロファイルを追記する

```ini:選択肢2：~/.aws/config
[default]
mfa_serial = arn:aws:iam::account_id:mfa/devicename
account = account_id
region = ap-northeast-1
output = json

[profile mfa]
account = account_id
region = ap-northeast-1
output = json
```

```ini:選択肢2：~/.aws/credentials
[default]
aws_access_key_id = (略)
aws_secret_access_key = (略)

[mfa]
aws_access_key_id = ASXXXXXXXXXXXXXXXXXX
aws_secret_access_key = jexxxxbCxxxx11xxxxE2xxxxB3xxxxCbxxxx4zw8
aws_session_token = IQoJb3JpZ2luX2VjEG0aDmFwLW5vcnRoZWFzdC0xIkgwRgIhAP2QuV9k(以下略)
```

`get-session-token`のコマンドは長いし、コピペしないといけないパラメータが3つもあるし、`/`を含むのでクリックでの自動範囲選択が使えないし、トークンが数行に渡る長さだしと、これを手作業でやろうとすると非常に面倒です。


## MFA設定を自動化するスクリプト

という訳で自動化しました。

```bash:~/.aws/aws-mfa-set-session-token.sh
#!/bin/bash

CONFIG_FILE=~/.aws/config
CREDENTIALS_FILE=~/.aws/credentials


TOKEN_JSON=$(aws --profile $AWS_MFA_PROFILE sts get-session-token --serial-number $AWS_MFA_SERIAL --token-code $1 --output json)

if [ $? -eq 0 ]; then
  export AWS_ACCESS_KEY_ID=$(echo ${TOKEN_JSON} | sed -E 's/^.*"AccessKeyId": *"([^"]+)".*$/\1/')
  export AWS_SECRET_ACCESS_KEY=$(echo ${TOKEN_JSON} | sed -E 's/^.*"SecretAccessKey": *"([^"]+)".*$/\1/')
  export AWS_SESSION_TOKEN=$(echo ${TOKEN_JSON} | sed -E 's/^.*"SessionToken": *"([^"]+)".*$/\1/')

  env | grep AWS_ | sort

  # ~/.aws/config の更新
  if ! grep -q "\[profile ${AWS_MFA_PROFILE}-mfa\]" ${CONFIG_FILE}; then
    {
      echo ""
      echo "[profile ${AWS_MFA_PROFILE}-mfa]"
      echo "region = ap-northeast-1"
      echo "output = json"
    } >> ${CONFIG_FILE}
  fi

  # ~/.aws/credentials の更新
  if grep -q "\[${AWS_MFA_PROFILE}-mfa\]" ${CREDENTIALS_FILE}; then
    sed -i "/\[${AWS_MFA_PROFILE//\//\\/}-mfa\]/,/^\[/ {
      s|aws_access_key_id.*|aws_access_key_id     = ${AWS_ACCESS_KEY_ID}|
      s|aws_secret_access_key.*|aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY}|
      s|aws_session_token.*|aws_session_token     = ${AWS_SESSION_TOKEN}|
    }" ${CREDENTIALS_FILE}
  else
    {
      echo ""
      echo "[${AWS_MFA_PROFILE}-mfa]"
      echo "aws_access_key_id     = ${AWS_ACCESS_KEY_ID}"
      echo "aws_secret_access_key = ${AWS_SECRET_ACCESS_KEY}"
      echo "aws_session_token     = ${AWS_SESSION_TOKEN}"
    } >> ${CREDENTIALS_FILE}
  fi
fi
```

置き場所はどこでも大丈夫ですが、`~/.aws/aws-mfa-set-session-token.sh`に設置した前提で以降進めます。

なおTOKEN_JSONから値を抜き出すsedはこちらの記事を参考にさせていただきました。

https://qiita.com/hoto17296/items/38f8575265a514660745


## 何が起きるか

このスクリプトを実行すると、まずアクセスキー、シークレットキー、トークンが環境変数にセットされます。
と同時に、`<プロファイル名>-mfa`というプロファイルがaws cliの設定ファイルに追加されます。
すでに同名のプロファイルがある場合、クレデンシャルを新しいもので上書きします。
`<プロファイル名>`はこの後設定する`AWS_MFA_PROFILE`です。


## 使い方

`~/.bashrc`等にこう書いてください。
`~/.bashrc`等を書き換えたときはログインし直してください。

```bash:~/.bashrc等への記述例
aws-mfa() {
  export AWS_MFA_PROFILE=default  ##get-session-tokenできるAWS CLIのプロファイル
  export AWS_MFA_SERIAL=arn:aws:iam::account_id:mfa/devicename  ##MFAデバイスの識別子
  source ~/.aws/aws-mfa-set-session-token.sh $1
}
```

次にMFAデバイスで一時解除コードを取得してください。

最後に作ったスクリプトを下記のように実行してください。

```bash:スクリプト実行
aws-mfa <一時解除コード>
```


### AWS CLI実行方法

スクリプト実行後は普通にAWS CLIが利用可能になります。

スクリプトを実行したセッションにはトークンが環境変数に設定されているので、プロファイルを省略可能です。
プロファイルを指定しない場合、最後にスクリプトを実行したときに取得したトークンを使用します。

```bash:AWS CLI実行例1
aws ec2 describe-instances
```

スクリプトを実行したのと別のセッションだとトークンが環境変数に設定されてないため、プロファイルを指定する必要があります。
スクリプト実行時のAWS_MFA_PROFILE環境変数に`-mfa`を加えたものがプロファイル名になります。

```bash:AWS CLI実行例2
aws ec2 describe-instances --profile <AWS_MFA_PROFILE>-mfa
```