ヒアドキュメントを使ってAWS CLIの –cli-input パラメーターを便利に使う

2021.01.10

しばたです。

AWS CLIやAWS Tools for PowerShellはその特性上一つのコマンドで多くのパラメーターを扱うことが多くなります。
この様な場合、PowerShellではスプラッティングを使うとコマンドのパラメーターを宣言的 *1に記述でき非常に便利です。

私もAWS Tools for PowerShellではスプラッティングを多用し、たとえば以下の様な感じの記述を常用します。

Import-Module AWS.Tools.EC2
Import-Module AWS.Tools.SimpleSystemsManagement

# 例) New-EC2Instance でEC2インスタンスを作成する
#     多数のパラメーターを扱うときはスプラッティングを使い $params 変数にパラメーターの内容を記述
$params = @{
    KeyName = 'my-ec2-keypair'
    ImageId = $(Get-SSMParameter -Name /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base -Region ap-northeast-1 -Select Parameter.Value)
    InstanceType = 't3.medium'
    MinCount = 1
    MaxCount = 1
    SubnetId = (Get-EC2Subnet -Filter @{Name = 'tag:Name'; Values = 'my-subnet-name'}).SubnetId
    SecurityGroupId = (Get-EC2SecurityGroup -Filter @{Name = 'group-name'; Values = 'my-security-group-name'}).GroupId
    EbsOptimized = $false
    BlockDeviceMapping = [Amazon.EC2.Model.BlockDeviceMapping]@{
        DeviceName = '/dev/sda1'
        Ebs = @{
            VolumeType = 'gp2'
            VolumeSize = 30
            DeleteOnTermination = $true
        }
    }
    TagSpecification = @{
        ResourceType = 'Instance';
        Tags         = @( @{Key = 'Name'; value = 'my-windows-server-2019'});
    }
}
New-EC2Instance @params

AWS CLIにはCLIスケルトンがある

AWS CLIでスプラッティングは使えませんがAWS CLIにはCLI スケルトンと呼ばれる機能があり、多数のパラメーターをJSONまたはYAMLの形で指定することが可能です。

細かい話は上記記事をご覧いただければと思います。
今回は紹介のために簡単な例を出すと、たとえばaws ec2 describe-imagesコマンドでAMIイメージを検索する場合以下の様なJSONをparameters.jsonという名前で保存し、

parameters.json

{
    "Filters": [
        { "Name": "architecture", "Values": [ "x86_64" ] },
        { "Name": "name", "Values": [ "amzn2-ami-hvm*" ] }
    ],
    "Owners": [ "amazon" ]
}

--cli-input-jsonパラメーターでこのJSONファイルを指定して実行することができます。

# parameters.json がカレントディレクトリにある場合の指定例
# ※--queryパラメーターは表示の都合で記述している
aws ec2 describe-images --cli-input-json file://./parameters.json \
    --query 'reverse(sort_by(Images, &CreationDate))[0].{Architecture:Architecture, CreationDate:CreationDate, ImageId:ImageId, Name:Name}'

結果この様な感じで現在最新のAmazon Linux 2 AMIイメージを検索できます。

--cli-input パラメーターはヒアドキュメントを受け入れる

ドキュメントやサンプルではこの--cli-input-json(および--cli-input-yaml)パラメーターはfile://スキーマ形式のファイル名を指定していますが、このパラメーターはJSONやYAMLの内容を直接指定しても構いません。
変数で指定してやれば改行込みの文字列も使えます。

以降、BashおよびPowerShellでヒアドキュメントを使った例を紹介します。

検証環境

現時点のAWS CloudShellを検証環境とします。

  • AWS CLI 2.0.58
  • Bash 4.2.46
  • PowerShell 7.0.3

1. Bashでヒアドキュメントを使って--cli-input-jsonを指定

Bashでヒアドキュメントを扱う方法はいくつかあると思いますが今回はcatコマンドを使ったものを採用します。
先述の例は以下の様な感じで--cli-input-json$params変数を渡すことができます。

# params変数にJSONの内容を設定
params=$(cat <<EOM
{
    "Filters": [
        { "Name": "architecture", "Values": [ "x86_64" ] },
        { "Name": "name", "Values": [ "amzn2-ami-hvm*" ] }
    ],
    "Owners": [ "amazon" ]
}
EOM
)
# --cli-input-json パラメーターはJSONを直接指定可能
aws ec2 describe-images --cli-input-json "$params" \
    --query 'reverse(sort_by(Images, &CreationDate))[0].{Architecture:Architecture, CreationDate:CreationDate, ImageId:ImageId, Name:Name}'

注意点としては$params変数は"$params"とダブルクォートでくくる必要がある点くらいです。

2. Bashでヒアドキュメントを使って--cli-input-yamlを指定

CLIスケルトンではJSONだけでなくYAMLも使うことができ、前項と同様に扱えます。

# params変数にYAMLの内容を設定
params=$(cat <<EOM
Filters:
- Name: 'architecture'
  Values:
  - 'x86_64'
- Name: 'name'
  Values:
  - 'amzn2-ami-hvm*' # serach Amazon Linux 2
Owners:
- 'amazon'
EOM
)
# --cli-input-yaml パラメーターはYAMLを直接指定可能
aws ec2 describe-images --cli-input-yaml "$params" \
    --query 'reverse(sort_by(Images, &CreationDate))[0].{Architecture:Architecture, CreationDate:CreationDate, ImageId:ImageId, Name:Name}'

3. PowerShellでヒアドキュメントを使って--cli-input-jsonを指定

PowerShellでヒアドキュメントは@''@(または@""@)で設定でき以下の様な感じで$params変数を定義できます。

$params = @'
{
    "Filters": [
        { "Name": "architecture", "Values": [ "x86_64" ] },
        { "Name": "name", "Values": [ "amzn2-ami-hvm*" ] }
    ],
    "Owners": [ "amazon" ]
}
'@

この変数をそのまま--cli-input-jsonに渡すことができれば良かったのですが、残念ながらPowerShellから外部にあるawsコマンドにダブルクォートを引き渡す際はエスケープを要求されていしまいます。
エスケープ無しでawsコマンドを実行すると下図の様にJSON解析エラーになってしまいます。

Error parsing parameter 'cli-input-json': Invalid JSON received.

このためちょっとひと手間(-replace '"','\"'の部分)かけてやる必要があります。

# PowerShell → awsコマンドに渡す際はダブルクォートのエスケープが必要
# -replace演算子を使って一括エスケープ
$params = @'
{
    "Filters": [
        { "Name": "architecture", "Values": [ "x86_64" ] },
        { "Name": "name", "Values": [ "amzn2-ami-hvm*" ] }
    ],
    "Owners": [ "amazon" ]
}
'@ -replace '"','\"'
# --cli-input-json パラメーターはJSONを直接指定可能
aws ec2 describe-images --cli-input-json $params `
    --query 'reverse(sort_by(Images, &CreationDate))[0].{Architecture:Architecture, CreationDate:CreationDate, ImageId:ImageId, Name:Name}'

ちなみに-replace演算子を使ってエスケープした後のJSONはこんな感じになっています。

{
    \"Filters\": [
        { \"Name\": \"architecture\", \"Values\": [ \"x86_64\" ] },
        { \"Name\": \"name\", \"Values\": [ \"amzn2-ami-hvm*\" ] }
    ],
    \"Owners\": [ \"amazon\" ]
}

4. PowerShellでヒアドキュメントを使って--cli-input-yamlを指定

最後にPowerShellでYAMLを使った場合の例です。

YAMLではダブルクォートは使わなくても良いのでこちらはエスケープ不要です。
このためPowerShell上ではJSONよりYAMLを使う方が良い気がします。

# YAMLではダブルクォートは必須ではないのでエスケープ不要
# (※もちろんダブルクォートを使った場合は要エスケープ)
$params = @'
Filters:
- Name: 'architecture'
  Values:
  - 'x86_64'
- Name: 'name'
  Values:
  - 'amzn2-ami-hvm*' # serach Amazon Linux 2
Owners:
- 'amazon'
'@
# --cli-input-yaml パラメーターはYAMLを直接指定可能
aws ec2 describe-images --cli-input-yaml $params `
    --query 'reverse(sort_by(Images, &CreationDate))[0].{Architecture:Architecture, CreationDate:CreationDate, ImageId:ImageId, Name:Name}'

5. (おまけ) PowerShellでスプラッティングを使った場合

これまでの例と同等の処理をAWS Tools for PowerShell + スプラッティングを使った場合は以下の様になります。

$params = @{
    Filter = (
        @{ Name = 'architecture'; Values = 'x86_64'},
        @{ Name = 'name'; Values = 'amzn2-ami-hvm*'}
    );
    Owner = 'amazon'
}
Get-EC2Image @params |
    Sort-Object CreationDate -Descending | Select-Object Architecture, CreationDate, ImageId, Name -First 1

AWS Tools for PowerShellに--queryパラメーターは無いため代わりにSort-ObjectSelect-Objectを使っているものの、これまでの例と似た記述感になってると思うのですがいかがでしょうか?

最後に

以上となります。
ちょっとしたテクニックですがCLIの書き方の幅が広がると思いますのでぜひ使ってみてください。

脚注

  1. あくまでもハッシュ定義の宣言での話ですが...