mito’s blog

IT技術メインの雑記。思い立ったが吉日。

[Ansible][AWS] ParameterStoreモジュールでユーザとパスワードを登録する

はじめに

community.awsにあるssm_parameterモジュールで、ユーザとパスワードを登録します。

community.aws.ssm_parameter module – Manage key-value pairs in AWS Systems Manager Parameter Store — Ansible Documentation


AWS Systems Manager(SSM)パラメータストアは名前にシークレットと付かないし、同系統のサービスでシークレットマネージャーがあるためセキュリティは強くないイメージを持ってしまいますが、そんなことはありません。
オプションには string_type があり、平文のString StringList、暗号化のSecureStringと選択できます。


環境

  • ansible core: 2.13.7
  • community.aws: 5.0.0

5.0.0以降ののモジュール名はcommunity.aws.ssm_parameterで、それより前はcommunity.aws.aws_ssm_parameter_storeです。
使い方は特に変わりありません。


パラメータストアにユーザとパスワードを登録する

パラメータストアは、1組のkey:value(またはlist)が登録できます。
複数のkey:valueが登録できるシークレートマネージャとは異なります。

Playbook

2組のkey:valueを登録するので、それぞれモジュールを実行します。
また、パラメータストアの値はlookupプラグインで取得できます。

---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - name: Create key/value pair in AWS SSM parameter store
      community.aws.ssm_parameter:
        name: "web_user"
        string_type: "String"          # ユーザの種別はStringを指定
        value: "admin"

    - name: Create key/value pair in AWS SSM parameter store
      community.aws.ssm_parameter:
        name: "web_password"
        string_type: "SecureString"    # パスワードの種別はSecureStringを指定
        value: "oioioioi"

    - debug:
        msg: "{{ lookup('amazon.aws.aws_ssm', item) }}"  # lookupプラグインを使って呼び出す
      loop:
        - "web_user"
        - "web_password"


実行ログ

想定通りに登録されました。

$ ansible-playbook add_paramater.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] **************************************************************************

TASK [Create key/value pair in AWS SSM parameter store] ***********************************
changed: [localhost]

TASK [Create key/value pair in AWS SSM parameter store] ***********************************
changed: [localhost]

TASK [debug] ******************************************************************************
ok: [localhost] => (item=web_user) => {
    "msg": "admin"
}
ok: [localhost] => (item=web_password) => {
    "msg": "oioioioi"
}

PLAY RECAP ********************************************************************************
localhost : ok=3  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 





「/」で階層構造を表示する

nameの先頭に「/」を付ける必要があります。
複数のkey:valueをまとめられるシークレットマネージャーと比べると見づらい部分もありますが、
先頭にシステム名を入れスラッシュで区切ると、登録数が増えても幾分か見やすくなります。

Playbook
---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - name: Create key/value pair in AWS SSM parameter store
      community.aws.ssm_parameter:
        name: "/web/user"             # 先頭に必ず「/」を入れる
        string_type: "String"
        value: "admin"

    - name: Create key/value pair in AWS SSM parameter store
      community.aws.ssm_parameter:
        name: "/web/password"         # 先頭に必ず「/」を入れ
        string_type: "SecureString"
        value: "oioioioi"

    - debug:
        msg: "{{ lookup('amazon.aws.aws_ssm', item) }}"
      loop:
        - "/web/user"
        - "/web/password"


実行ログ

無事に取得できました。

$ ansible-playbook add_paramater.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] **************************************************************************

TASK [Create key/value pair in AWS SSM parameter store] ***********************************
changed: [localhost]

TASK [Create key/value pair in AWS SSM parameter store] ***********************************
changed: [localhost]

TASK [debug] ******************************************************************************
ok: [localhost] => (item=/web/user) => {
    "msg": "admin"
}
ok: [localhost] => (item=/web/password) => {
    "msg": "oioioioi"
}

PLAY RECAP ********************************************************************************
localhost : ok=3  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

$ 


備考

値を登録するだけであれば、パラメータストアは値の履歴が残り、課金されるまでの上限も余裕があるため、シークレットマネージャーよりも使いやすいかもしれません。
また、CloudFormationではSecureStringは作成できないようです。

[Ansible][AWS] SecretsManagerモジュールでユーザとパスワードを登録する(更新2023.02.13)

はじめに

community.awsにあるsecretsmanager_secretモジュールで、ユーザとパスワードを登録します。

community.aws.secretsmanager_secret module – Manage secrets stored in AWS Secrets Manager — Ansible Documentation



環境

  • ansible core: 2.13.7
  • community.aws: 5.0.0

community.awsの最新版は、Ansible Core 2.11.0以前のバージョンをサポートしていません。

Ansible version compatibility Tested with the Ansible Core 2.12, and 2.13 releases, and the current development version of Ansible. Ansible Core versions before 2.11.0 are not supported. In particular, Ansible Core 2.10 and Ansible 2.9 are not supported.


AWS Secrets Managerにユーザとパスワードを登録する

Playbook

ほぼ公式ドキュメント記載のサンプル通りです。
2つのkey:valueを登録するので、json_secretオプションを指定します。
また、ローテーションは指定していないので無効のままです。

注意点として、SecretsManagerのシークレット名は完全に削除されるまで最低7日間の待機期間が必要です。
7日以内に同じシークレット名を再作成することはできないので、短期間で再作成するならユニークな文字列を付与することになります。

---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - name: Add Secrets Manager
      community.aws.secretsmanager_secret:
        name: "web"
        state: present
        secret_type: 'string'
        # json_secretは、登録するJSON形式のデータを指定します。
        # secretと排他関係にあります。secretは、文字列またはバイナリ データを登録するときに指定します。
        json_secret: '{"user":"ec2-user", "password":"12345678"}'


実行ログ

想定通りに登録されました。

 $ ansible-playbook add_secretmanager.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ************************************************************

TASK [Add Secrets Manager] **************************************************
changed: [localhost]

PLAY RECAP ******************************************************************
localhost : ok=1  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 




AWS Secrets Managerからユーザ、パスワードを取得する

Playbook

登録したなら取り出します。
lookupプラグインを使い、シークレット名を指定します。

---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - debug:
        msg: "{{ lookup('amazon.aws.aws_secret', 'web') }}"


実行ログ

さらっと取得できました。

 $ ansible-playbook get_secretmanager.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***********************************************************

TASK [debug] ***************************************************************
ok: [localhost] => {
    "msg": {
        "password": "12345678",
        "user": "ec2-user"
    }
}

PLAY RECAP *****************************************************************
localhost : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 


※2023.02.13 追加

AWS Secrets Managerのパスワードを更新する

登録済みのパスワードを更新します。
シークレットに複数のkey:valueが登録されている場合、一部のみの更新・追加はできません。
変更が無いkey:valueは削除され、更新したkey:value、追加したkey:valueのみになります。
そのため、パスワードの更新とともにユーザを再登録します。

Playbook

登録したパスワードを更新します。
ユーザは変更ありませんが、シークレットから取得します。

---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - debug:
        msg: "{{ lookup('amazon.aws.aws_secret', 'web') }}"
      register: result

    - name: change password for Secrets Manager
      community.aws.secretsmanager_secret:
        name: "web"
        state: present
        secret_type: 'string'
        json_secret: "{{ new }}"
      vars:
        new:
          user: "{{ result.msg.user }}"
          password: "oioioioi"    # 更新するパスワード

    - debug:
        msg: "{{ lookup('amazon.aws.aws_secret', 'web') }}"


実行ログ

無事、パスワードが更新できました。

$ ansible-playbook update_secretmanager.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***********************************************************

TASK [debug] ***************************************************************
ok: [localhost] => {
    "msg": {
        "password": "12345678",
        "user": "ec2-user"
    }
}

TASK [change password for Secrets Manager] *********************************
changed: [localhost]

TASK [debug] ***************************************************************
ok: [localhost] => {
    "msg": {
        "password": "oioioioi",
        "user": "ec2-user"
    }
}

PLAY RECAP *****************************************************************
localhost : ok=3  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 


AWS Secrets Managerのパスワード更新に失敗するパターン

シークレットに複数のkey:valueが登録されている状態で、パスワードのみ登録(更新)してみます。

Playbook

json_secretに、パスワードのみを指定します。

---
- hosts: localhost
  gather_facts: no
  
  tasks:
    - debug:
        msg: "{{ lookup('amazon.aws.aws_secret', 'web') }}"
      register: result

    - name: change password for Secrets Manager
      community.aws.secretsmanager_secret:
        name: "web"
        state: present
        secret_type: 'string'
        json_secret: "{{ new }}"
      vars:
        new:
          password: "oioioioi"    # 更新するパスワード

    - debug:
        msg: "{{ lookup('amazon.aws.aws_secret', 'web') }}" 


実行ログ

ユーザは削除され、更新したパスワードのみが残ります。

$ ansible-playbook update_secretsmanager.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ***********************************************************

TASK [debug] ***************************************************************
ok: [localhost] => {
    "msg": {
        "password": "12345678",
        "user": "ec2-user"
    }
}

TASK [change password for Secrets Manager] *********************************
changed: [localhost]

TASK [debug] ***************************************************************
ok: [localhost] => {
    "msg": {
        "password": "oioioioi"
    }
}

PLAY RECAP *****************************************************************
localhost : ok=3  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 


備考

AnsibleでのSecretsManagerは、AWS CLIコマンドから見るに値の更新はできなさそうで、すべての値を再登録しかなさそう。

自社のアドベントカレンダーを企画し、無事に完遂した話

はじめに

終わってみれば、ご協力もあり無事に完遂しましたが、社内(特に他部署と)の交流不足が大きかったと思います。
他部署でちらほらブログを書いていた方が数名いたので何とかなるでしょと思っていましたが、そもそも会話したことがないという状況でした。完全に勢いのみで開催しました。


準備

アドベントカレンダーについての詳細をGoogleドキュメントにまとめ、社内に公開しました。

  • アドベントカレンダーとは
  • アドベントカレンダーを開催する理由
    • 「社外にアウトプットする人が増えたら良いな、外から見た弊社が格好よくうつるんじゃないか」と、アウトプットする人を増やすいい機会だと思ったからです。
    • アウトプットは良いぞ
  • 投稿までの流れ
  • 投稿内容のご相談
    • ○○番煎じとか需要とか気にしないでいいです、毎年同じ内容の雑誌も出てますなどと話しました
  • 社内外へのPRとして私がすること
    • 投稿だけじゃなくツイートもします


周知

slackでどーんと告知した後、上司や他部署の役職者にも相談しました。
順序逆のほうがやりやすかったかもしれないけど、謎の強制力が働くので(気持ち的に)難しいところがあります。


記事一覧

並べてみると、投稿があった分野は多岐にわたっていました。

日付 タイトル
12/1 [AWS Backup] バックアップとリストアを試してみた - mito’s blog
12/2 [Ansible x Terraform] TerraformでKeyPairの作成やAmazon Secrets ManagerにKeyPairを登録し、Ansibleで利用する - mito’s blog
12/3 今の時代だからこそ、アニメーションで伝えよう
12/4 VSCode + Docusaurus で最高のドキュメント作成環境を構築する
12/5 いつの間にかRaspberry Pi Imagerが更に便利になっていました
12/6 Docusaurus で作成するドキュメントはココがいいね!
12/7 Nginx + Docusaurus でドキュメントをコンテナ化してみる
12/8 LaravelとReactでWebSocketを使ったブロードキャストアプリを作ってみる - Qiita
12/9 OpenAPIGeneratorの自動生成におけるコントローラーの生成単位はURLで決まる
12/10 Nginx + OAuth2 Proxy で静的 Web サイトに認証機能を追加してみる
12/11 静的 Web サイトに認証をつけて AWS App Runner でデプロイしてみる
12/12 SwiftUI でサイズクラスに対応する - Qiita
12/13 IndexedDBの概念がわかりづらい件について - Qiita
12/14 Cloud9でseleniumを動かしてみた - Qiita
12/15 seleniumをコンテナ化して動かしてみた - Qiita
12/16 Google Cloud Next '22に参加してクラウドエンジニアの育成について考えた - Qiita
12/17 ML Kit Text Recognition v2で日本語認識Androidアプリ - Qiita
12/18 [Ansible] Assume Roleモジュールを使う - mito’s blog
12/19 [Terraform] 関数を使って、年月日時分秒をユニークな文字列として扱う - mito’s blog
12/20 フロントエンドを知らない一般人がトレンディなフレームワーク SvelteKit を眺める - ローカル環境構築編 -
12/21 GAEの起動/停止をスケジューリングする
12/22 フロントエンドを知らない一般人がトレンディなフレームワーク SvelteKit を眺める - ルーティング編 -
12/23 非技術者がJavaScriptフレームワーク・ライブラリの違いを調べてみた - Qiita
12/24 【対策メモ】AWS認定DevOpsエンジニア-プロフェッショナル試験 - Qiita
12/25 【合格体験記】AWS認定DevOpsエンジニア-プロフェッショナル(DOP-C01) - Qiita


最後に

来年も開催したいなと思います。 順調に埋まるように(それだけじゃないけど)、じわじわと他部署にもアウトプットの芽を埋めてみます。

[Terraform] 関数を使って、年月日時分秒をユニークな文字列として扱う

この記事は、カサレアル Advent Calendar 2022 の19日目のエントリです。

はじめに

ユニークな文字列が欲しかったので、年月日時分秒を取得し文字列にしました。
以下の関数を組み合わせます。


TFファイル

現在の時間2022年12月18日11時26分29秒20221218112629に変換します。

  • tfファイル
locals {
  # timestampでUTCを取得する
  now_utc = timestamp()
  # timeaddで9hを足してJSTに変換する
  now_jst = timeadd(local.now_utc, "9h") 
  # formatdateで取得したい形式に変換する
  datetime = formatdate("YYYYMMDDhhmmss", local.now_jst)
}

output "datetime"{
  value = local.datetime
}


実行結果

$ terraform apply

Changes to Outputs:
  + datetime = (known after apply)

You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

datetime = "20221218112629"
$ 


備考

24時間表記にするため、formatdate関数ではhhを指定します。


[Ansible] Assume Roleモジュールを使う

この記事は、カサレアル Advent Calendar 2022 の18日目のエントリです。

はじめに

最近、AnsibleのAWSコレクションにamazon.awsとcommunity.awsの2つがあることを知りました。
community.awsのほうにAssume Roleモジュールがあったので、それを試してみます。

なお、AssumeRoleってなに?という方は、記憶に残しやすい記事がありますのでご参照ください。


環境

  • ansible core: 2.13.7
  • community.aws: 5.0.0

community.awsの最新版は、Ansible Core 2.11.0以前のバージョンをサポートしていません。

Ansible version compatibility Tested with the Ansible Core 2.12, and 2.13 releases, and the current development version of Ansible. Ansible Core versions before 2.11.0 are not supported. In particular, Ansible Core 2.10 and Ansible 2.9 are not supported.


AssumeRole する Playbook

sts_assume_roleモジュールの実行前後で、ARNやクレデンシャル情報等を出力します。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - shell:
        cmd: aws sts get-caller-identity
      register: result

    - name: before
      debug:
        msg: "{{ result.stdout }}"

    - name: set assume_role
      sts_assume_role:
        role_arn: "arn:aws:iam::*******:role/********"  #引き継ぎたいRoleを指定
        role_session_name: "Test_Role"
      register:  assumed_role

    - name: after
      debug:
        msg: "{{ assumed_role }}"


Playbook の実行結果

sts_assume_roleモジュールにより、ARNや一時的なクレデンシャル情報等が設定されました(ほぼ伏字ですが)。

$ ansible-playbook assume_role.yml 
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] ********************************************************************

TASK [shell] ************************************************************************
changed: [localhost]

TASK [before] ***********************************************************************
ok: [localhost] => {
    "msg": {
        "Account": "***********",
        "Arn": "arn:aws:iam::***********:user/***********",
        "UserId": "***********"
    }
}

TASK [set assume_role] **************************************************************
changed: [localhost]

TASK [after] ************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": true,
        "failed": false,
        "sts_creds": {
            "access_key": "***********",
            "expiration": "2022-12-17T17:31:46+00:00",
            "secret_key": "**********************",
            "session_token": "*********************************"
        },
        "sts_user": {
            "arn": "arn:aws:sts::***********:assumed-role/***********/Test_Role",
            "assumed_role_id": "***********:Test_Role"
        }
    }
}

PLAY RECAP **************************************************************************
localhost : ok=4  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   

$ 


以降のタスクでaws_access_key: "{{ assumed_role.sts_creds.access_key }}"のように指定し、AssumeRole後のクレデンシャル情報等を使用します。