Compute EngineからCLIでAWSのリソースへアクセスする

以前、IAMのスイッチロール周りをまとめた[記事](https://qiita.com/odayushin/items/4f85f381a5906a2320ab)を書いたのですが、訳あってCompute Engineで構築したLinuxからCLIでAWSリソースへアクセスする必要が出てきたので、AssumeRoleWithWebIdentityを使ったやり方をまとめます（AssumeRole自体の説明は省略します）

## GCP サービスアカウントの準備

1. IAMと管理 > サービスアカウント > サービスアカウントを作成からサービスアカウントを作成します
![スクリーンショット 2023-11-17 175735.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/c5d428c6-f940-b179-33c7-0b5dc3bd0e9c.png)

1. 任意の名前を付けて作成します（サービスアカウント名だけ入れればOKです）
今回はGCPの権限は何も付与しませんでしたが、必要に応じて付与してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/b6cf7fb2-4bc0-b29f-b2e9-e9e2e93c1ab0.png)

1. 一意のIDをコピーしておきます
![スクリーンショット 2023-11-17 180502.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/3d502493-8cba-2b4e-53a7-48c441139df5.png)

## AWS IAMロールの準備

まずは普通にIAMロールを作成します。GCPからのアクセスに必要な権限だけをつけてください
作成したIAMロールの信頼ポリシーを↓のように修正します。accounts.google.com:audは先ほど作成したサービスアカウントの一意のIDを記載します。信頼ポリシーの内容は最小限にしてるので、IPアドレス制限などする場合は過去記事ご参照ください


```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "accounts.google.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "accounts.google.com:aud": "xxxxxxxxxxxx" <= 先ほどコピーしたID
                },
                "StringLike": {
                    "sts:RoleSessionName": "任意の値"
                }
            }
        }
    ]
}
```

## Compute Engineインスタンスを起動する

1. 管理コンソールからポチポチCompute Engineインスタンスを起動していきましょう
サービスアカウントを設定するところだけ、先ほど作成したものにするのを忘れないようにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/95359fd1-0aef-7a04-69b6-7f62323f7227.png)

## AWS CLIを設定する
インスタンスが起動したら管理コンソールからSSHで接続して設定していきます

1. AWS CLIをインストールします
[公式ドキュメント](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)に記載の通り

1. boto3をインストールしておきます

    ```
    # pipも入ってなければインストール
    $ sudo apt install -y python3-pip
    $ pip install boto3
    ```

1. AWS CLIの設定

    ```
    $ aws configure
    AWS Access Key ID [None]:
    AWS Secret Access Key [None]:
    Default region name [ap-northeast-1]: ap-northeast-1
    Default output format [json]: json
    ```

    作成されたconfigファイルを開いて、`credential_process`の行を追加しておきます
    後ほど、pythonファイルをインスタンスにコピーするので配置場所を決めて`credential_process`に書いておきます

    ```
    [default]
    region = ap-northeast-1
    output = json
    credential_process = python3 /path/to/credential_process.py arn:aws:iam::012345678901:role/{role_name}
    ```

1. credential_process.pyをconfigに記載したパスに配置します
credential_process.pyはこちら

    ```python:/path/to/credential_process.py
    #!/usr/bin/python3
    import requests
    import boto3
    import json
    import sys
     
     
    def get_metadata(path: str, parameter: str):
        metadata_url = f'http://metadata.google.internal/computeMetadata/v1/{path}/{parameter}'
        headers = {'Metadata-Flavor': 'Google'}
    
        try:
            meta_request = requests.get(metadata_url, headers=headers)
        except requests.exceptions.RequestException as e:
            raise SystemExit(e)
     
        if meta_request.ok:
            return meta_request.text
        else:
            raise SystemExit('Compute Engine meta data error')
     
     
    if __name__ == '__main__':
        try:
            aws_role_arn = sys.argv[1]
        except IndexError:
            exit(0)
     
        token = get_metadata('instance', 'service-accounts/default/identity?format=standard&audience=gcp')
     
        sts = boto3.client('sts', aws_access_key_id='', aws_secret_access_key='')
     
        res = sts.assume_role_with_web_identity(
            RoleArn=aws_role_arn,
            WebIdentityToken=token,
            RoleSessionName='任意の値') <= 信頼ポリシーに設定した値を記載
     
        aws_temporary_credentials = {
            'Version': 1,
            'AccessKeyId': res['Credentials']['AccessKeyId'],
            'SecretAccessKey': res['Credentials']['SecretAccessKey'],
            'SessionToken': res['Credentials']['SessionToken'],
            'Expiration': res['Credentials']['Expiration'].isoformat()
        }
     
        print(json.dumps(aws_temporary_credentials))
    ```

## 動作確認する

これで動くようになったはずです

```
$ aws sts get-caller-identity
{
    "UserId": "xxxxxxxxxxxxxxxxx:{role_session_name}",
    "Account": "012345678901",
    "Arn": "arn:aws:sts::012345678901:assumed-role/{your_role_name}/{role_session_name}"
}
```

以上です