mito’s blog

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

[Ansible x Terraform] TerraformでKeyPairの作成やAmazon Secrets ManagerにKeyPairを登録し、Ansibleで利用する

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

はじめに

TerraformでEC2インスタンスやKerPairなどのリソースを作成し、それをAnsibleで活用します。
以下の流れで試します。

  1. [Terraform] EC2インスタンスSSHキー(KeyPair)などのリソースを作成します
  2. [Terraform] Amazon Secrets ManagerにSSHキーを登録します
  3. [Ansible] Amazon Secrets ManagerからSSHキーをダウンロードします
  4. [Ansible] SSHキーを使って、対象ノードにパッケージをインストールします



Terraformのリソース「Secrets Manager」について

リソースaws_secretsmanager_secretでシークレットの名前を、リソースaws_secretsmanager_secret_versionでユーザ名/パスワードやキー/バリューなどのシークレット情報が登録できます。

Terraform Registry

locals {
  KEY = {
    ssh_key = file("./xxxx.pem")
  }
}

resource "aws_secretsmanager_secret" "asm_secret" {
  name = "シークレット名"
}

resource "aws_secretsmanager_secret_version" "asm_secret_version" {
  secret_id     = aws_secretsmanager_secret.asm_secret.id
  secret_string = jsonencode(local.KEY)
}


Ansibleのlookupプラグインamazon.aws.aws_secret」について

lookupプラグインを使って、Amazon Secrets ManagerからKerPair情報を取得します。

amazon.aws.aws_secret lookup – Look up secrets stored in AWS Secrets Manager — Ansible Documentation

"{{ lookup('amazon.aws.aws_secret', 'シークレット名', region='リージョン', aws_access_key='XXXXXX', aws_secret_key='XXXXXX') }}"


環境

  • ホストOS: Amazon Linux 2
  • 対象ノードOS: Amazon Linux 2(terraformで作成)
  • terraform: v1.3.4
  • ansible core: v2.13.6
  • amazon.aws: v5.1.0
  • Python: v3.10.6
  • python3-boto: v2.49.0-4
  • python3-botocore: v1.23.34


用意するファイル

  • main.tf
    • 実装内容
      • EC2インスタンスを作成する
      • KeyPairを作成する
      • Secrets ManagerへKeyPairを登録する
      • セキュリティグループを作成する
        • ホストからのSSHアクセスを許可する
  • main.yml
    • 実装内容
      • Secrets ManagerからKeyPairを取得する
      • KeyPairを使ってgitをインストールする
  • ansible.cfg
    • 実装内容
      • host_key_checkingの無効


main.tf
provider "aws" {
    region = "ap-northeast-1"
    access_key = "XXXXXX"
    secret_key = "XXXXXX"
}

# EC2インスタンスの作成
resource "aws_instance" "mito_ec2" {
    ami           = "ami-072bfb8ae2c884cc4"    # Amazon Linux 2
    instance_type = "t2.micro"
    associate_public_ip_address = "true"
    key_name      = aws_key_pair.deployer.key_name
    vpc_security_group_ids = [aws_security_group.sg_mito.id]
    tags = {
        Name = "mito_ec2"
    }
}

# キーペアの作成
resource "tls_private_key" "rsa_4096" {
  algorithm = "RSA"
  rsa_bits  = 4096
}

resource "aws_key_pair" "deployer" {
  key_name   = "mito_key"
  public_key = tls_private_key.rsa_4096.public_key_openssh
  lifecycle {
    ignore_changes  = [key_name]
  }
}

# セキュリティグループの作成
resource "aws_security_group" "sg_mito" {
  name        = "sg_mito"
  description = "sg_mito"

  ingress {
    description = "SSH"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["${chomp(data.http.myip.response_body)}/32"]  # ホストのパブリックIPを許可する
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name      = "sg_mito"
  }
}

# 返り値でホストのパブリックIPを取得する
data "http" "myip" {
  url = "http://ipv4.icanhazip.com"
}

# Secrets ManagerSSHキーを登録する
resource "aws_secretsmanager_secret" "asm_secret" {
  name = "test_asm_secret"
}

resource "aws_secretsmanager_secret_version" "asm_secret_version" {
  secret_id     = aws_secretsmanager_secret.asm_secret.id
  secret_string = jsonencode( {"ssh_key" = tls_private_key.rsa_4096.private_key_pem})
}


main.yml

1Play目でSSHキーをダウンロードし、2Play目でパッケージをインストールします。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - name: create ssh_key
      ansible.builtin.copy:
        content: "{{ key.ssh_key}}"
        dest: ./mito_key.pem
        mode: 0600
      vars:
        key: "{{ lookup('amazon.aws.aws_secret', 'test_asm_secret', region='ap-northeast-1', aws_access_key='XXXXXX', aws_secret_key='XXXXXX') }}"

- hosts: all
  gather_facts: no
  become: yes

  tasks:
    - name: install git
      yum:
        name: git
        state: present


ansible.cfg
[defaults]
host_key_checking = False


実行ログ

まずは、Terraformコマンドでリソースを作成します。

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/tls...
- Finding latest version of hashicorp/http...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/tls v4.0.4...
- Installed hashicorp/tls v4.0.4 (signed by HashiCorp)
- Installing hashicorp/http v3.2.1...
- Installed hashicorp/http v3.2.1 (signed by HashiCorp)
- Installing hashicorp/aws v4.44.0...
- Installed hashicorp/aws v4.44.0 (signed by HashiCorp)
()
$ 
$ 
$ terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.mito_ec2 will be created
  + resource "aws_instance" "mito_ec2" {
      + ami                                  = "ami-072bfb8ae2c884cc4"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = true
      + availability_zone                    = (known after apply)

()
  # aws_secretsmanager_secret.asm_secret will be created
  + resource "aws_secretsmanager_secret" "asm_secret" {
      + arn                            = (known after apply)
      + force_overwrite_replica_secret = false
      + id                             = (known after apply)
      + name                           = "test_asm_secret"
      + name_prefix                    = (known after apply)
      + policy                         = (known after apply)
      + recovery_window_in_days        = 30
      + rotation_enabled               = (known after apply)
      + rotation_lambda_arn            = (known after apply)
      + tags_all                       = (known after apply)

      + replica {
          + kms_key_id         = (known after apply)
          + last_accessed_date = (known after apply)
          + region             = (known after apply)
          + status             = (known after apply)
          + status_message     = (known after apply)
        }

      + rotation_rules {
          + automatically_after_days = (known after apply)
        }
    }

  # aws_secretsmanager_secret_version.asm_secret_version will be created
  + resource "aws_secretsmanager_secret_version" "asm_secret_version" {
      + arn            = (known after apply)
      + id             = (known after apply)
      + secret_id      = (known after apply)
      + secret_string  = (sensitive value)
      + version_id     = (known after apply)
      + version_stages = (known after apply)
    }

()
Plan: 6 to add, 0 to change, 0 to destroy.

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

tls_private_key.rsa_4096: Creating...
aws_secretsmanager_secret.asm_secret: Creating...
aws_security_group.sg_mito: Creating...
()

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


AWSコンソールでシークレットマネージャへのSSHキー登録を確認します。


作成したEC2インスタンスのinventory.iniを作成します。

$ cat inventory.ini 
35.79.224.19


Playbookを実行します。

 $ ansible-playbook main.yml -i inventory.ini -u ec2-user --private-key=./mito_key.pem
[DEPRECATION WARNING]: Ansible will require Python 3.8 or newer on the controller starting with Ansible 2.12. Current version: 3.7.10 (default, Jun  3 2021, 00:02:01) [GCC 7.3.1 20180712 (Red Hat 7.3.1-13)]. This feature will be removed from 
ansible-core in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.

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

TASK [create ssh_key] ***************************************************************************
changed: [localhost]

PLAY [all] **************************************************************************************

TASK [install git] ******************************************************************************
[WARNING]: Platform linux on host 35.79.224.19 is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-
core/2.11/reference_appendices/interpreter_discovery.html for more information.
changed: [35.79.224.19]

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

$ 


無事に対象ノードへ接続し、パッケージもインストールできました。

備考

inventory.iniの作成はダイナミックインベントリを使えば不要になるけど手、 SSHキー名などの固定値は、別途管理する必要があります。