こんにちは。AWS CLIが好きな福島です。
今回は、セキュリティ強化のため、EC2でIMDSv2を強制化するのに伴い、 AWS CLIを使い、全てのEC2でIMDSv1の使用履歴を確認したので、その方法をご紹介いたします。

IMDSv2については、以下のブログで紹介しているため、ご興味がある方は、ぜひご覧ください。
実行環境
今回、コマンドを実行した環境は、以下の通りとなります。
(本記事でご紹介しているコマンドの中には、Linuxのコマンドを利用している箇所があります。)
# uname -a Linux LAPTOP-CNM26HN6 4.4.0-18362-Microsoft #1049-Microsoft Thu Aug 14 12:01:00 PST 2020 x86_64 x86_64 x86_64 GNU/Linux #
結論
以下のコマンドを実行しました。
◆ポイント
- CloudWatchで取得できるメトリクス「MetadataNoToken」の値を利用
- メトリクス「MetadataNoToken」の1時間ごとの最大値を2週間分、取得
- MetadataNoToken$(date +"%Y%m%d_%H%M%S").csvファイルに結果を出力
- B列を空けているため、1時間ごとの最大値をSUM関数により計算することでIMDSv1の使用履歴を確認
- 値がNoneになる場合があります。(推測ですが、インスタンスを停止しているなどで値を取得できない場合にNoneになるかと存じます。)
CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line
do
cat << EOF > json
[
{
"Id":"q1",
"MetricStat":
{
"Metric":
{
"Namespace":"AWS/EC2",
"MetricName":"MetadataNoToken",
"Dimensions":[
{
"Name":"InstanceId",
"Value":"$line"
}
]
},
"Period":3600,
"Stat":"Maximum"
}
}
]
EOF
echo $line,, > /tmp/awscli.tmp ;\
aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\
Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\
Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\
Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\
Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\
Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\
Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\
Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\
Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\
Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\
Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\
Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\
Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\
Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\
Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\
Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\
Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\
Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\
Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\
Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\
Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\
Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\
Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\
Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\
Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\
StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp
cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
echo "" >> ${CSV_FILE}
done
- 実行結果

ExcelのSUM関数を利用して計算

B列が0のインスタンスは、IMDSv1を使用していない(2週間)ことが分かる。
処理の説明
CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\
⇒変数名の定義
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId]" --output text | while read line
⇒全EC2のインスタンスIDをwhileにより1つずつline変数に代入
cat << EOF > json
[
{
"Id":"q1",
"MetricStat":
{
"Metric":
{
"Namespace":"AWS/EC2",
"MetricName":"MetadataNoToken",
"Dimensions":[
{
"Name":"InstanceId",
"Value":"$line" ★
}
]
},
"Period":3600,
"Stat":"Maximum"
}
}
]
EOF
⇒CloudWatchコマンドで使うJSONファイルを生成。
Value(★)にインスタンスIDを記載。
aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\ Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\ Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\ Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\ Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\ Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\ Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\ Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\ Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\ Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\ Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\ Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\ Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\ Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\ Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\ Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\ Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\ Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\ Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\ Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\ Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\ Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\ Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\ Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\ Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\ StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp
⇒CloudWatchコマンドによりメトリクス「MetadataNoToken」の1時間ごとの最大値を2週間分、取得。
cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
echo "" >> ${CSV_FILE}
⇒CSVファイルへ出力
おまけ①
上記スクリプトでは、IMDSv1が使用履歴を確認することはできますが、
詳細な時間まで確認することはできません。
詳細な時間を確認したい場合は、cloudwatchコマンドのqueryで「Timestamps[0]」、「Timestamps[1]」と指定します。
- 実行例
aws cloudwatch get-metric-data --metric-data-queries file://json \ --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") \ --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") \ --query "MetricDataResults[].[Timestamps[0],Values[0],Timestamps[1],Values[1],StatusCode]" --output text | tr "\t" ","
- 実行結果
2021-02-09T09:00:00+00:00,3.0,2021-02-09T08:00:00+00:00,3.0,Complete
おまけ②(IMDSv2設定確認)
IMDSv2を強制化できているか確認するコマンドは、以下の通りです。 ※メタデータのHttpTokensおよびHttpEndpointの設定値を確認します。
- 実行例
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId,MetadataOptions.HttpTokens,MetadataOptions.HttpEndpoint]" --output text
- 実行結果
i-0f6126b7aeedfabd6 required enabled i-050536efdd9dc1126 optional enabled i-0b66442bd16bf7f21 optional enabled i-0808672558492fde8 optional enabled
⇒ 2列目が「required」、3列目が「enabled」になっていれば、IMDSv2のみ使う設定になっています。
おまけ③(IMDSv2設定変更)
- 実行例
aws ec2 modify-instance-metadata-options --instance-id i-0f61b7aedddabd6 --http-tokens required --http-endpoint enabled
おまけ④(Nameタグ付き)
CSV_FILE=MetadataNoToken_$(date +"%Y_%m%d_%H%M%S").csv;\
aws ec2 describe-instances --query "Reservations[].Instances[].[InstanceId,Tags[?Key=='Name'] | [0].Value]" --output text | tr "\t" "," | while read line
do
instance_id=$(echo $line | awk -F, '{print $1}')
name_tag=$(echo $line | awk -F, '{print $2}')
cat << EOF > json
[
{
"Id":"q1",
"MetricStat":
{
"Metric":
{
"Namespace":"AWS/EC2",
"MetricName":"MetadataNoToken",
"Dimensions":[
{
"Name":"InstanceId",
"Value":"$instance_id"
}
]
},
"Period":3600,
"Stat":"Maximum"
}
}
]
EOF
echo $instance_id,$name_tag,, > /tmp/awscli.tmp ;\
aws cloudwatch get-metric-data --metric-data-queries file://json --start-time $(date -u +"%Y-%m-%dT%H:%M:%SZ" -d "30 days ago") --end-time $(date -u +"%Y-%m-%dT%H:%M:%SZ") --query "MetricDataResults[].[\
Values[1],Values[2],Values[3],Values[4],Values[5],Values[6],Values[7],\
Values[8],Values[9],Values[10],Values[11],Values[12],Values[13],Values[14],\
Values[15],Values[16],Values[17],Values[18],Values[19],Values[20],Values[21],\
Values[22],Values[23],Values[24],Values[25],Values[26],Values[27],Values[28],\
Values[29],Values[30],Values[31],Values[32],Values[33],Values[34],Values[35],\
Values[36],Values[37],Values[38],Values[39],Values[40],Values[41],Values[42],\
Values[43],Values[44],Values[45],Values[46],Values[47],Values[48],Values[49],\
Values[50],Values[51],Values[52],Values[53],Values[54],Values[55],Values[56],\
Values[57],Values[58],Values[59],Values[60],Values[61],Values[62],Values[63],\
Values[64],Values[65],Values[66],Values[67],Values[68],Values[69],Values[70],\
Values[71],Values[72],Values[73],Values[74],Values[75],Values[76],Values[77],\
Values[78],Values[79],Values[80],Values[81],Values[82],Values[83],Values[84],\
Values[85],Values[86],Values[87],Values[88],Values[89],Values[90],Values[91],\
Values[92],Values[93],Values[94],Values[95],Values[96],Values[97],Values[98],\
Values[99],Values[100],Values[101],Values[102],Values[103],Values[104],Values[105],\
Values[106],Values[107],Values[108],Values[109],Values[110],Values[111],Values[112],\
Values[113],Values[114],Values[115],Values[116],Values[117],Values[118],Values[119],\
Values[120],Values[121],Values[122],Values[123],Values[124],Values[125],Values[126],\
Values[127],Values[128],Values[129],Values[130],Values[131],Values[132],Values[133],\
Values[134],Values[135],Values[136],Values[137],Values[138],Values[139],Values[140],\
Values[141],Values[142],Values[143],Values[144],Values[145],Values[146],Values[147],\
Values[148],Values[149],Values[150],Values[151],Values[152],Values[153],Values[154],\
Values[155],Values[156],Values[157],Values[158],Values[159],Values[160],Values[161],\
Values[162],Values[163],Values[164],Values[165],Values[166],Values[167],Values[168],\
StatusCode]" --output text | tr "\t" "," >> /tmp/awscli.tmp
cat /tmp/awscli.tmp | tr "\n" " " | sed 's/ //g' >> ${CSV_FILE}
echo "" >> ${CSV_FILE}
done
おわりに
今回は、IMDSv1の使用履歴を確認するスクリプトをご紹介しました。
どなたかのお役に立てれば幸いです。