📑

AWS CLI の history 機能

2021/03/12に公開
  • AWS CLI の history 機能を使うことで、過去に実行したコマンドや実行結果を記録しておくことができる
  • AWS CLI の実行結果を記録しておきたい場合や、自動的に実行しているコマンドで稀にエラーが発生しているといった場合の調査に役立つかも

AWS CLI の history 機能とは

  • AWS CLI v1.13.0 から追加された機能
  • AWS CLI で実行したコマンドの履歴や実行結果を保存してくれる

history 機能の使い方

AWS CLI Command Reference に history コマンドの使い方について情報がある

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/history/index.html

サクッと試す方法

# history 機能を有効化
# ~/.aws/config に "cli_history = enabled" が設定される
aws configure set cli_history enabled

# 任意の AWS CLI コマンドを実行
aws sts get-caller-identity
aws ec2 describe-regions

# history リスト表示
aws history list

# 直前に実行したコマンドの history を表示
aws history show

# 過去に実行したコマンドを 1つ 指定して history を表示
# xxx には aws history list で表示される UUID (command_id) を指定する
aws history show xxx

# history 機能を無効化
# ~/.aws/config に "cli_history = " が設定される
aws configure set ''
実際の実行例
$ aws configure set cli_history enabled
$ aws sts get-caller-identity
{
    "Account": "123456789012", 
    "UserId": "xxx", 
    "Arn": "arn:aws:iam::123456789012:user/myusername"
}
$ aws ec2 describe-regions
{
    "Regions": [
        {
            "OptInStatus": "opt-in-not-required", 
            "Endpoint": "ec2.eu-north-1.amazonaws.com", 
            "RegionName": "eu-north-1"
        }, 
...
    ]
}
$ aws history list
445378f1-f5dd-457f-ac99-75d2c7d45a75  2021-03-11 03:32:31 PM  ec2 describe-regions                              0
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3  2021-03-11 03:32:24 PM  sts get-caller-identity                           0
$ aws history show
AWS CLI command entered
at time: 2021-03-11 15:32:31.114
with AWS CLI version: aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57
with arguments: [u'ec2', u'describe-regions']

[0] API call made
at time: 2021-03-11 15:32:31.433
to service: ec2
using operation: DescribeRegions
with parameters: {}

[0] HTTP request sent
at time: 2021-03-11 15:32:31.436
to URL: https://ec2.ap-northeast-1.amazonaws.com/
with method: POST
with headers: {
    "Authorization": "AWS4-HMAC-SHA256 Credential=xxx/20210311/ap-northeast-1/ec2/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=b83f...
", 
    "Content-Length": "41", 
    "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", 
    "User-Agent": "aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57", 
    "X-Amz-Date": "20210311T153231Z"
}
with body: Action=DescribeRegions&Version=2016-11-15

[0] HTTP response received
at time: 2021-03-11 15:32:31.501
with status code: 200
with headers: {
    "Cache-Control": "no-cache, no-store", 
    "Content-Length": "3875", 
    "Content-Type": "text/xml;charset=UTF-8", 
    "Date": "Thu, 11 Mar 2021 15:32:31 GMT", 
    "Server": "AmazonEC2", 
    "Strict-Transport-Security": "max-age=31536000; includeSubDomains", 
    "vary": "accept-encoding", 
    "x-amzn-requestid": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb"
}
with body: <?xml version="1.0" ?>
<DescribeRegionsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
    <requestId>763badf3-0ac5-4c0c-8ae5-caa0665f25bb</requestId>
    <regionInfo>
        <item>
            <regionName>eu-north-1</regionName>
            <regionEndpoint>ec2.eu-north-1.amazonaws.com</regionEndpoint>
            <optInStatus>opt-in-not-required</optInStatus>
        </item>

...

[0] HTTP response parsed
at time: 2021-03-11 15:32:31.503
parsed to: {
    "Regions": [
        {
            "Endpoint": "ec2.eu-north-1.amazonaws.com", 
            "OptInStatus": "opt-in-not-required", 
            "RegionName": "eu-north-1"
        }, 
...
    ], 
    "ResponseMetadata": {
        "HTTPHeaders": {
            "cache-control": "no-cache, no-store", 
            "content-length": "3875", 
            "content-type": "text/xml;charset=UTF-8", 
            "date": "Thu, 11 Mar 2021 15:32:31 GMT", 
            "server": "AmazonEC2", 
            "strict-transport-security": "max-age=31536000; includeSubDomains", 
            "vary": "accept-encoding", 
            "x-amzn-requestid": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb"
        }, 
        "HTTPStatusCode": 200, 
        "RequestId": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb"
    }
}

...

AWS CLI command exited
at time: 2021-03-11 15:32:31.507
with return code: 0

$ aws history show 5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3
AWS CLI command entered
at time: 2021-03-11 15:32:24.458
with AWS CLI version: aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57
with arguments: [u'sts', u'get-caller-identity']

[0] API call made
at time: 2021-03-11 15:32:24.560
to service: sts
using operation: GetCallerIdentity
with parameters: {}

[0] HTTP request sent
at time: 2021-03-11 15:32:24.563
to URL: https://sts.amazonaws.com/
with method: POST
with headers: {
    "Authorization": "AWS4-HMAC-SHA256 Credential=xxx/20210311/us-east-1/sts/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=50c0...", 
    "Content-Length": "43", 
    "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", 
    "User-Agent": "aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57", 
    "X-Amz-Date": "20210311T153224Z"
}
with body: Action=GetCallerIdentity&Version=2011-06-15

[0] HTTP response received
at time: 2021-03-11 15:32:25.252
with status code: 200
with headers: {
    "Content-Length": "407", 
    "Content-Type": "text/xml", 
    "Date": "Thu, 11 Mar 2021 15:32:24 GMT", 
    "x-amzn-RequestId": "e732121e-3849-4cfe-afb2-baa0349b2936"
}
with body: <?xml version="1.0" ?>
<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
    <GetCallerIdentityResult>
        <Arn>arn:aws:iam::123456789012:user/myusername</Arn>
        <UserId>xxx</UserId>
        <Account>123456789012</Account>
    </GetCallerIdentityResult>
    <ResponseMetadata>
        <RequestId>e732121e-3849-4cfe-afb2-baa0349b2936</RequestId>
    </ResponseMetadata>
</GetCallerIdentityResponse>

[0] HTTP response parsed
at time: 2021-03-11 15:32:25.254
parsed to: {
    "Account": "123456789012", 
    "Arn": "arn:aws:iam::123456789012:user/myusername", 
    "ResponseMetadata": {
        "HTTPHeaders": {
            "content-length": "407", 
            "content-type": "text/xml", 
            "date": "Thu, 11 Mar 2021 15:32:24 GMT", 
            "x-amzn-requestid": "e732121e-3849-4cfe-afb2-baa0349b2936"
        }, 
        "HTTPStatusCode": 200, 
        "RequestId": "e732121e-3849-4cfe-afb2-baa0349b2936"
    }, 
    "UserId": "xxx"
}

AWS CLI command exited
at time: 2021-03-11 15:32:25.256
with return code: 0

history の保存挙動について

  • ~/.aws/cli/history/history.db に history データが保存される
  • history.db の実体は SQLite 3 なので sqlite3 コマンド等で操作することが可能
保存先発掘の軌跡

tree コマンドでディレクトリを見た感じ ~/.aws/cli/history/history.db がそれっぽい

$ tree ~/.aws
/home/ec2-user/.aws
├── cli
│   └── history
│       └── history.db
├── config
└── credentials

file コマンドで見てみると SQLite であることが分かる

$ file ~/.aws/cli/history/history.db
/home/ec2-user/.aws/cli/history/history.db: SQLite 3.x database

sqlite3 コマンドで中身を見てみる

$ sqlite3 ~/.aws/cli/history/history.db
SQLite version 3.34.0 2020-12-01 16:14:00
Enter ".help" for usage hints.
sqlite> .tables
records

history.db の中身は records という Table のみ。
SELECT で中身を見てみる。

sqlite> SELECT * FROM records;
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3||CLI|CLI_VERSION|1615476744458|"aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57"
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3||CLI|CLI_ARGUMENTS|1615476744461|["sts", "get-caller-identity"]
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3|a399c21a-22ff-47a9-ad57-baa8f826b855|BOTOCORE|API_CALL|1615476744560|{"operation": "GetCallerIdentity", "params": {}, "service": "sts"}
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3|a399c21a-22ff-47a9-ad57-baa8f826b855|BOTOCORE|HTTP_REQUEST|1615476744563|{"url": "https://sts.amazonaws.com/", "headers": {"Content-Length": "43", "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "X-Amz-Date": "20210311T153224Z", "Authorization": "AWS4-HMAC-SHA256 Credential=xxx/20210311/us-east-1/sts/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=50c00412063c2d749e992394465f965b19a0bc313ae135b852af5cd3807cfe21", "User-Agent": "aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57"}, "body": "Action=GetCallerIdentity&Version=2011-06-15", "method": "POST", "streaming": false}
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3|a399c21a-22ff-47a9-ad57-baa8f826b855|BOTOCORE|HTTP_RESPONSE|1615476745252|{"body": "<GetCallerIdentityResponse xmlns=\"https://sts.amazonaws.com/doc/2011-06-15/\">\n  <GetCallerIdentityResult>\n    <Arn>arn:aws:iam::123456789012:user/myusername</Arn>\n    <UserId>xxx</UserId>\n    <Account>123456789012</Account>\n  </GetCallerIdentityResult>\n  <ResponseMetadata>\n    <RequestId>e732121e-3849-4cfe-afb2-baa0349b2936</RequestId>\n  </ResponseMetadata>\n</GetCallerIdentityResponse>\n", "headers": {"x-amzn-RequestId": "e732121e-3849-4cfe-afb2-baa0349b2936", "Date": "Thu, 11 Mar 2021 15:32:24 GMT", "Content-Length": "407", "Content-Type": "text/xml"}, "streaming": false, "context": {"operation_name": "GetCallerIdentity"}, "status_code": 200}
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3|a399c21a-22ff-47a9-ad57-baa8f826b855|BOTOCORE|PARSED_RESPONSE|1615476745254|{"Account": "123456789012", "UserId": "xxx", "ResponseMetadata": {"HTTPStatusCode": 200, "RequestId": "e732121e-3849-4cfe-afb2-baa0349b2936", "HTTPHeaders": {"x-amzn-requestid": "e732121e-3849-4cfe-afb2-baa0349b2936", "date": "Thu, 11 Mar 2021 15:32:24 GMT", "content-length": "407", "content-type": "text/xml"}}, "Arn": "arn:aws:iam::123456789012:user/myusername"}
5c62d327-fd3f-4eef-a2ff-4ef9bbc773d3||CLI|CLI_RC|1615476745256|0
445378f1-f5dd-457f-ac99-75d2c7d45a75||CLI|CLI_VERSION|1615476751114|"aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57"
445378f1-f5dd-457f-ac99-75d2c7d45a75||CLI|CLI_ARGUMENTS|1615476751117|["ec2", "describe-regions"]
445378f1-f5dd-457f-ac99-75d2c7d45a75|03c96ec0-0139-4eef-9537-d5fb472f3c9d|BOTOCORE|API_CALL|1615476751433|{"operation": "DescribeRegions", "params": {}, "service": "ec2"}
445378f1-f5dd-457f-ac99-75d2c7d45a75|03c96ec0-0139-4eef-9537-d5fb472f3c9d|BOTOCORE|HTTP_REQUEST|1615476751436|{"url": "https://ec2.ap-northeast-1.amazonaws.com/", "headers": {"Content-Length": "41", "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "X-Amz-Date": "20210311T153231Z", "Authorization": "AWS4-HMAC-SHA256 Credential=xxx/20210311/ap-northeast-1/ec2/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=b83f12fbe5d2cc6a88444443076671a5009da7e59289ab604317b0fed3d80138", "User-Agent": "aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57"}, "body": "Action=DescribeRegions&Version=2016-11-15", "method": "POST", "streaming": false}
445378f1-f5dd-457f-ac99-75d2c7d45a75|03c96ec0-0139-4eef-9537-d5fb472f3c9d|BOTOCORE|HTTP_RESPONSE|1615476751501|{"body": "...", "headers": {"x-amzn-requestid": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb", "Content-Length": "3875", "Strict-Transport-Security": "max-age=31536000; includeSubDomains", "vary": "accept-encoding", "Server": "AmazonEC2", "Cache-Control": "no-cache, no-store", "Date": "Thu, 11 Mar 2021 15:32:31 GMT", "Content-Type": "text/xml;charset=UTF-8"}, "streaming": false, "context": {"operation_name": "DescribeRegions"}, "status_code": 200}
445378f1-f5dd-457f-ac99-75d2c7d45a75|03c96ec0-0139-4eef-9537-d5fb472f3c9d|BOTOCORE|PARSED_RESPONSE|1615476751503|{"Regions": ..., "ResponseMetadata": {"HTTPStatusCode": 200, "RequestId": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb", "HTTPHeaders": {"x-amzn-requestid": "763badf3-0ac5-4c0c-8ae5-caa0665f25bb", "content-length": "3875", "strict-transport-security": "max-age=31536000; includeSubDomains", "vary": "accept-encoding", "server": "AmazonEC2", "cache-control": "no-cache, no-store", "date": "Thu, 11 Mar 2021 15:32:31 GMT", "content-type": "text/xml;charset=UTF-8"}}}
445378f1-f5dd-457f-ac99-75d2c7d45a75||CLI|CLI_RC|1615476751507|0

history コマンドで表示されるデータが保存されていることが発掘できた

AWS CLI の実装箇所としては https://github.com/aws/aws-cli/blob/develop/awscli/customizations/history/db.py に SQLite への書き込みを行う DatabaseRecordWriter クラスが定義されている

history 機能の使い所を考察

CloudTrail に未対応な AWS API 呼び出し状況の記録

  • AWS CLI のコマンド実行は、基本的に AWS API 呼び出しを行うので、実行履歴を確認するなら CloudTrail が使用できる
    • しかし、すべての AWS API が CloudTrail に対応している訳ではない
  • history コマンドは CloudTrail 対応状況に関係なく、 AWS CLI から実行された AWS API 呼び出しを記録できる点が便利

トラブルシューティング

  • AWS CLI を使う場合のトラブルシューティングとしては --debug オプションを指定して実行するのが一般的(?)
  • しかし、 AWS CLI のコマンド実行を自動化しているような場合に --debug オプションを指定するには、自動化内容を修正する必要が生じたり、出力されるログ内容が膨大になるといった問題がある
  • history 機能の場合、とりあえずで設定を仕込んでおけるので手軽に導入できる

この記事の内容を試した環境

$ cat /etc/os-release 
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
$ aws --version
aws-cli/1.18.217 Python/2.7.18 Linux/4.14.219-164.354.amzn2.x86_64 botocore/1.19.57

Discussion