はじめに
AnsibleでAWS KMS(以下KMS)を構築&利用しようとして、Ansibleの使用言語であるPythonにハマった話です。
インフラ自動構築ツールとしてのAnsibleの立ち位置や、使い方などは省略させてもらいます。
(Ansible歴が15営業日程度なので、アレレな使い方をしている可能性があります。)
前提条件
Ansibleでしたかったこと
- KMSカスタマーマスターキーの構築(以下CMK)
- CMKにエイリアスを付与する
- CMKをエイリアスから取得する
- CMKでEBSを暗号化
ハマった経緯
AnsibleのAWSモジュール一覧を見たところ、KMS構築モジュールがないやんけ!
(え、あるよと思った方、実はこのaws_kmsモジュールはKMSを利用するIAMロール・ユーザーを定める代物です。)
となり、AnsibleからAWS CloudFormation(以下CloudFormation)を呼び(使用モジュール)、Ansibleでしたかったこと1 & 2を実施するという荒技をしました。
aws_kms_factsモジュール(以下aws_kms_facts)で暗号化に使いたいCMKをエイリアスのフィルターで取得し、ec2_volモジュールで暗号化しようとしたときに、ことは起こりました。
実行Ansibleソース
エイリアスからEBS暗号化に使用するCMKを取得する
- name: KMS for EBS
aws_kms_facts:
region: "{{ vars.region }}"
filters:
alias: "{{ vars.kms_by_cloudformation.kms_ebs_tag }}"
register: _aws_kms_facts_for_ebs
- debug: var=_aws_kms_facts_for_ebs
- debug: var=_aws_kms_facts_for_ebs.keys
エイリアスからEBS暗号化に使用するCMKを取得した結果の標準出力
_aws_kms_facts_for_ebs.keys[0].key_id
を取得したかったのです。
なのに、_aws_kms_facts_for_ebs.keys
がないだと。。
参照:https://qiita.com/odashun1015/items/f7bd23336264f022e3a1
ok: [localhost] => {
"_aws_kms_facts_for_ebs": {
"changed": false,
"failed": false,
"keys": [
{
"aliases": [
"test-kms-ebs"
],
"aws_account_id": "123456789",
"creation_date": "2018-12-21T03:05:33.885000+00:00",
"description": "customer master key for ebs",
"enabled": true,
"grants": [],
"key_arn": "arn:aws:kms:eu-west-1:123456789:key/123456789abc-123456789-abc",
"key_id": "123456789abc-123456789-abc",
"key_manager": "CUSTOMER",
"key_state": "Enabled",
"key_usage": "ENCRYPT_DECRYPT",
"origin": "AWS_KMS",
"policies": [
"{\n \"Version\" : \"2012-10-17\",\n \"Id\" : \"kms-ebs\",\n \"Statement\" : [ {\n \"Sid\" : \"Enable IAM User Permissions\",\n \"Effect\" : \"Allow\",\n \"Principal\" : {\n \"AWS\" : \"arn:aws:iam::123456789:root\"\n },\n \"Action\" : \"kms:*\",\n \"Resource\" : \"*\"\n }, {\n \"Sid\" : \"Allow access for Key Administrators\",\n \"Effect\" : \"Allow\",\n \"Principal\" : {\n \"AWS\" : \"arn:aws:iam::123456789:user/TestUser\"\n },\n \"Action\" : [ \"kms:Create*\", \"kms:Describe*\", \"kms:Enable*\", \"kms:List*\", \"kms:Put*\", \"kms:Update*\", \"kms:Revoke*\", \"kms:Disable*\", \"kms:Get*\", \"kms:Delete*\", \"kms:TagResource\", \"kms:UntagResource\", \"kms:ScheduleKeyDeletion\", \"kms:CancelKeyDeletion\" ],\n \"Resource\" : \"*\"\n } ]\n}"
],
"tags": {
"Name": "test-kms-ebs"
}
}
]
}
}
ok: [localhost] => {
"_aws_kms_facts_for_ebs.keys": "<built-in method keys of dict object at 0x2715490>"
}
EBS暗号化⇨エラー発生
- name: Encrypted EBS for App Server 1
ec2_vol:
region: "{{ vars.region }}"
device_name: "{{ vars.ec2_app.volumes.volume_02.device_name }}"
volume_type: "{{ vars.ec2_app.volumes.volume_02.volume_type }}"
volume_size: "{{ vars.ec2_app.volumes.volume_02.volume_size }}"
delete_on_termination: "{{ vars.ec2_app.volumes.volume_02.delete_on_termination }}"
encrypted: yes
kms_key_id: "{{ _aws_kms_facts_for_ebs.keys[0].key_id }}"
tags:
Name: "{{ vars.ec2_app_01.instance_tags.Name }}"
register: _ec2_vol_app_01_enc
- debug: var=_ec2_vol_app_01_enc
出現したエラー
TASK [ec2 : Encrypted EBS for App Server 1] ******************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: dict object has no element 0\n\nThe error appears to have been in '/home/test/ansible/roles/ec2/tasks/main.yml': line 67, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Encrypted EBS for App Server 1\n ^ here\n"}
to retry, use: --limit @/home/test/ansible/site.retry
[localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable.
え、aws_kms_facts実行結果で取得できるdictオブジェクトに確かに「keys」というキーはあるよ!
なぜバリューが取れない??。上記参照ページに出会うまで、キー名を変えたりして、方向性の違うことをしてました。
解決策
EBS暗号化を、aws_kms_facts実行結果で取得できるdictオブジェクトのキーをひとつひとつ舐め、「keys」というキーがあれば、そのバリューを取得する処理に変更しました。
EBS暗号化ソースは以下です。
- name: Encrypted EBS for App Server 1
ec2_vol:
region: "{{ vars.region }}"
device_name: "{{ vars.ec2_app.volumes.volume_02.device_name }}"
volume_type: "{{ vars.ec2_app.volumes.volume_02.volume_type }}"
volume_size: "{{ vars.ec2_app.volumes.volume_02.volume_size }}"
delete_on_termination: "{{ vars.ec2_app.volumes.volume_02.delete_on_termination }}"
encrypted: yes
kms_key_id: >-
{% if item.key == 'keys' %}{{ item.value[0].key_id }}{% endif %}
instance: "{{ _ec2_app_01.tagged_instances[0].id }}"
tags:
Name: "{{ vars.ec2_app_01.instance_tags.Name }}"
with_dict: "{{ _aws_kms_facts_for_ebs }}"
register: _ec2_vol_app_01_enc
- debug: var=_ec2_vol_app_01_enc
まとめ
Pythonのkeysは特別な意味を持つので、オブジェクト内の「keys」キーは、keys()と間違われないようにしましょう