vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

Ansible で AWS Systems Manager パラメータストア (ついでにシークレットマネージャー) から値を取得

Parameter Store

パラメータストア参照は aws_ssm プラグインで行えます。 version 2.5 から実装されているようです。

Requirements 記載の通り、 botocore,boto3 が必要になります。インストールされていない状態だと下記エラーとなります。

FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'aws_ssm'. Error was a <class 'ansible.errors.AnsibleError'>, original message: botocore and boto3 are required for aws_ssm lookup."}

DescribeParameters

# ap-northeast-1
+---------------+--------------------+----------------+
|     KeyId     |       Name         |     Type       |
+---------------+--------------------+----------------+
|  alias/aws/ssm|  an1-securestring  |  SecureString  |
|  None         |  an1-string        |  String        |
|  None         |  an1-stringlist    |  StringList    |
+---------------+--------------------+----------------+

# us-west-2
+---------------+--------------------+----------------+
|     KeyId     |       Name         |     Type       |
+---------------+--------------------+----------------+
|  alias/aws/ssm|  uw2-securestring  |  SecureString  |
|  None         |  uw2-string        |  String        |
|  None         |  uw2-stringlist    |  StringList    |
+---------------+--------------------+----------------+

tasks例

存在しないパラメータはエラーにならず空文字で返却されるため、値チェックを入れた方が良さそうです。

  pre_tasks:
    - name: 'set ssm parameter store values'
      set_fact:
        an1:
          string: "{{ lookup('aws_ssm', 'an1-string') }}"
          securestring: "{{ lookup('aws_ssm', 'an1-securestring') }}"
          stringlist: "{{ lookup('aws_ssm', 'an1-stringlist') }}"
          not_exist: "{{ lookup('aws_ssm', 'dummy') }}"
        uw2:
          string: "{{ lookup('aws_ssm', 'uw2-string', region='us-west-2') }}"
          securestring: "{{ lookup('aws_ssm', 'uw2-securestring', region='us-west-2') }}"
          stringlist: "{{ lookup('aws_ssm', 'uw2-stringlist', region='us-west-2') }}"
          not_exist: "{{ lookup('aws_ssm', 'dummy') }}"

    - name: 'check ssm parameter store values'
      debug: msg="check variable {{ item }}"
      failed_when: '{{ item }} == ""'
      ignore_errors: yes
      with_items:
        - "an1.string"
        - "an1.securestring"
        - "an1.stringlist"
        - "an1.not_exist"
        - "uw2.string"
        - "uw2.securestring"
        - "uw2.stringlist"
        - "uw2.not_exist"

  tasks:
    - name: 'output ssm parameter store values'
      debug: var="{{ item }}"
      with_items:
        - "an1.string"
        - "an1.securestring"
        - "an1.stringlist"
        - "an1.not_exist"
        - "uw2.string"
        - "uw2.securestring"
        - "uw2.stringlist"
        - "uw2.not_exist"

実行結果

TASK [set ssm parameter store values] ***************************************************
ok: [test-instance]

TASK [check ssm parameter store values] *************************************************
 [WARNING]: when statements should not include jinja2 templating delimiters such as {{
}} or {% %}. Found: {{ item }} == ""

ok: [test-instance] => (item=an1.string) => {
    "msg": "check variable an1.string"
}
ok: [test-instance] => (item=an1.securestring) => {
    "msg": "check variable an1.securestring"
}
ok: [test-instance] => (item=an1.stringlist) => {
    "msg": "check variable an1.stringlist"
}
failed: [test-instance] (item=an1.not_exist) => {
    "msg": "check variable an1.not_exist"
}
ok: [test-instance] => (item=uw2.string) => {
    "msg": "check variable uw2.string"
}
ok: [test-instance] => (item=uw2.securestring) => {
    "msg": "check variable uw2.securestring"
}
ok: [test-instance] => (item=uw2.stringlist) => {
    "msg": "check variable uw2.stringlist"
}
failed: [test-instance] (item=uw2.not_exist) => {
    "msg": "check variable uw2.not_exist"
}
fatal: [test-instance]: FAILED! => {"msg": "All items completed"}
...ignoring

TASK [output ssm parameter store values] ************************************************
ok: [test-instance] => (item=an1.string) => {
    "an1.string": "Tokyo文字列",
    "item": "an1.string"
}
ok: [test-instance] => (item=an1.securestring) => {
    "an1.securestring": "Tokyo secure文字列",
    "item": "an1.securestring"
}
ok: [test-instance] => (item=an1.stringlist) => {
    "an1.stringlist": "Tokyo リスト1,Tokyo リスト2,Tokyo リスト3",
    "item": "an1.stringlist"
}
ok: [test-instance] => (item=an1.not_exist) => {
    "an1.not_exist": "",
    "item": "an1.not_exist"
}
ok: [test-instance] => (item=uw2.string) => {
    "item": "uw2.string",
    "uw2.string": "Oregon 文字列"
}
ok: [test-instance] => (item=uw2.securestring) => {
    "item": "uw2.securestring",
    "uw2.securestring": "Oregon secure文字列"
}
ok: [test-instance] => (item=uw2.stringlist) => {
    "item": "uw2.stringlist",
    "uw2.stringlist": "Oregon リスト1,Oregon リスト2,Oregon リスト3"
}
ok: [test-instance] => (item=uw2.not_exist) => {
    "item": "uw2.not_exist",
    "uw2.not_exist": ""
}

Secrets Manager

aws_secret プラグインを使う方法と、aws_ssm 経由で取得する方法の2パターンが利用できそうです。

aws_secret

シークレットマネージャー参照は aws_secret プラグインで行えます。 version 2.8 (2019-05-16) から追加されました。

Requirements 記載の通り、 botocore,boto3 が必要になります。インストールされていない状態だと下記エラーとなります。

FAILED! => {"msg": "The lookup aws_secret requires boto3 and botocore."}

近い内に修正されると思いますが、region指定部分で不具合があります。

FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'aws_secret'. Error was a <type 'exceptions.KeyError'>, original message: 'Requested entry (plugin_type: lookup plugin: aws_secret setting: region ) was not defined in configuration.'"}

PRされている修正を反映すると、取得できました。

  • ansible/plugins/lookup/aws_secret.py
@@ -14,6 +14,7 @@
   - botocore>=1.10.0
 extends_documentation_fragment:
   - aws_credentials
+  - aws_region
 short_description: Look up secrets stored in AWS Secrets Manager.
 description:
   - Look up secrets stored in AWS Secrets Manager provided the caller

GetSecretValue

+------------------+----------------------------------+
|    Name          |        SecretString              |
+------------------+----------------------------------+
|  dev/test_secret |  {"dev/test_secret":"testtest"}  |
+------------------+----------------------------------+

tasks例

存在しないシークレットの指定はエラーとなります。

FAILED! => {"msg": "An unhandled exception occurred while running the lookup plugin 'aws_secret'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Failed to retrieve secret: ResourceNotFoundException(u'An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets Manager can\\u2019t find the specified secret.',)"}
  pre_tasks:
    - name: 'set secrets'
      set_fact:
        an1:
          secret1: "{{ lookup('aws_secret', 'dev/test_secret') }}"
  tasks:
    - name: 'output secrets'
      debug: var="{{ item }}"
      with_items:
        - "an1.secret1"
        - "an1.not_exist"

実行結果

TASK [set secrets] **********************************************************************
ok: [test-instance]

TASK [output secrets] *******************************************************************
ok: [test-instance] => (item=an1.secret1) => {
    "an1.secret1": {
        "dev/test_secret": "testtest"
    },
    "ansible_loop_var": "item",
    "item": "an1.secret1"
}

aws_ssm

制限事項がいくつかありますが、SSM 経由でシークレットマネージャの参照が可能ですので、同様に取得できます。

tasks例

予約パス /aws/reference/secretsmanager/ を含めて指定します。

  pre_tasks:
    - name: 'set secrets using ssm parameter store'
      set_fact:
        an1:
          secret: "{{ lookup('aws_ssm', '/aws/reference/secretsmanager/dev/test_secret') }}"
          not_exist: "{{ lookup('aws_ssm', '/aws/reference/secretsmanager/dummy') }}"
  tasks:
    - name: 'output secrets'
      debug: var="{{ item }}"
      with_items:
        - "an1.secret"
        - "an1.not_exist"

実行結果

TASK [set secrets using ssm parameter store] ********************************************
ok: [test-instance]

TASK [output secrets] *******************************************************************
ok: [test-instance] => (item=an1.secret) => {
    "an1.secret": {
        "dev/test_secret": "testtest"
    },
    "ansible_loop_var": "item",
    "item": "an1.secret"
}
ok: [test-instance] => (item=an1.not_exist) => {
    "an1.not_exist": "",
    "ansible_loop_var": "item",
    "item": "an1.not_exist"
}

__NSPlaceholderDate initialize エラー

Mac OS Mojave (10.14.x) で playbook を実行した際に以下のようなエラーが発生しました。

+[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
+[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

以下の環境変数を設定することで回避可能です。

export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

High Sierra (10.13.x) からの仕様変更により発生するようです。