mito’s blog

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

[Ansible x Terraform] local-execでPlaybookを実行してみた


この記事は、Ansible Advent Calendar 2022 の2日目のエントリです。

はじめに

今回はAnsibleのTerraformモジュールを使うのではなく、TerraformでEC2インスタンスを作成し、local-execでPlaybookを実行してパッケージをインストールします。
AnsibleのAmazonEC2のインベントリプラグインは使わず、出来る限りTerraformで実装してみます。

Provisioner: local-exec | Terraform | HashiCorp Developer



結論

Playbookを更新しても一度それを実行するnull_resourceが成功していれば、terraform applyでは差異なしと判断され、null_resource(Playbook)は再実行されません。
また、Terrafromが出力するPlaybookのログがきれいに表示されないので、AnsibleからTerraformを呼び出したほうが便利そうです。


環境

  • terraform: v1.3.4
  • ansible core: v2.13.6
  • aws cli: v1.22.34


用意するファイル

  • main.tf
    • 実装内容
      • EC2インスタンスの作成
      • SSHキーの作成
      • セキュリティグループの作成
      • リソースhttpでホストのパブリックIPを取得する
      • リソースlocal_fileでinventory.iniの作成
      • リソースnull_resourceのlocal-execでPlaybookを実行する
  • main.yml
    • 実装内容
      • gitのインストール
  • ansible.cfg
    • 実装内容
      • host_key_checkingの無効


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

# 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 "local_file" "private_key_pem" {
  content  = tls_private_key.rsa_4096.private_key_pem
  filename = "/home/ubuntu/.ssh/mito_key.pem"  # ログインユーザに合わせる
}

# セキュリティグループの作成
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"
}

# 対象ノードのプライベートIPが記載されたinventory.iniを作成する
resource "local_file" "inventory" {
  content  = aws_instance.mito_ec2.public_ip
  filename = "./inventory.ini"
  file_permission = "0644"
}

# Playbookの実行
resource "null_resource" "playbook" {
  depends_on = [
    aws_instance.mito_ec2  # インスタンス作成後に実行
  ]

  # 対象ノードの起動を待ち、Playbookを実行する
  provisioner "local-exec" {
    interpreter = ["/usr/bin/bash", "-c"]
    command     = <<-EOT
      chmod 600 ${local_file.private_key_pem.filename}
      aws ec2 wait instance-status-ok --instance-ids ${aws_instance.mito_ec2.id}
      ansible-playbook main.yml -i inventory.ini -u ec2-user --private-key=${local_file.private_key_pem.filename}
    EOT
  }
}


main.yml
---
- hosts: all
  gather_facts: no
  become: yes

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


ansible.cfg
[defaults]
host_key_checking = False


実行ログ

99行目からPlaybookのログが表示されていますが、ログの順番が変わっています。

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/null from the dependency lock file
- Reusing previous version of hashicorp/tls from the dependency lock file
- Finding latest version of hashicorp/http...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/local from the dependency lock file
- Using previously-installed hashicorp/null v3.2.1
- Using previously-installed hashicorp/tls v4.0.4
- Installing hashicorp/http v3.2.1...
- Installed hashicorp/http v3.2.1 (signed by HashiCorp)
- Using previously-installed hashicorp/aws v4.43.0
- Using previously-installed hashicorp/local v2.2.3

()
$ 
$ 
$ terraform apply
data.http.myip: Reading...
data.http.myip: Read complete after 0s [id=http://ipv4.icanhazip.com]

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)
() 

  # local_file.inventory will be created
  + resource "local_file" "inventory" {
      + content              = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0644"
      + filename             = "./inventory.ini"
      + id                   = (known after apply)
    }

  # local_file.private_key_pem will be created
  + resource "local_file" "private_key_pem" {
      + content              = (sensitive)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "/home/ubuntu/.ssh/mito_key.pem"
      + id                   = (known after apply)
    }

  # null_resource.playbook will be created
  + resource "null_resource" "playbook" {
      + id = (known after apply)
    }

()
Plan: 7 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_security_group.sg_mito: Creating...
tls_private_key.rsa_4096: Creation complete after 1s [id=6d5549db0XXXXXXXXXXXXXXXXX]
local_file.private_key_pem: Creating...
aws_key_pair.deployer: Creating...
local_file.private_key_pem: Creation complete after 0s [id=9f5e11483XXXXXXXXXXXXXXXXXXXX]
aws_key_pair.deployer: Creation complete after 0s [id=mito_key]
aws_security_group.sg_mito: Creation complete after 3s [id=sg-091fbXXXXXXXXXXXX]
aws_instance.mito_ec2: Creating...
aws_instance.mito_ec2: Still creating... [10s elapsed]
aws_instance.mito_ec2: Still creating... [20s elapsed]
aws_instance.mito_ec2: Creation complete after 21s [id=i-02fb1XXXXXXXXXXXX]
local_file.inventory: Creating...
null_resource.playbook: Creating...
local_file.inventory: Creation complete after 0s [id=fecdbeXXXXXXXXXXXXXXXXXXXXXXXXXXXXX]
null_resource.playbook: Provisioning with 'local-exec'...
null_resource.playbook (local-exec): Executing: ["/usr/bin/bash" "-c" "chmod 600 /home/ubuntu/.ssh/mito_key.pem\naws ec2 wait instance-status-ok --instance-ids i-02fb1149af69d72ce\nansible-playbook main.yml -i inventory.ini -u ec2-user --private-key=/home/ubuntu/.ssh/mito_key.pem\n"]
null_resource.playbook: Still creating... [10s elapsed]
null_resource.playbook: Still creating... [20s elapsed]
null_resource.playbook: Still creating... [30s elapsed]
null_resource.playbook: Still creating... [40s elapsed]
null_resource.playbook: Still creating... [50s elapsed]
null_resource.playbook: Still creating... [1m0s elapsed]
null_resource.playbook: Still creating... [1m10s elapsed]
null_resource.playbook: Still creating... [1m20s elapsed]
null_resource.playbook: Still creating... [1m30s elapsed]
null_resource.playbook: Still creating... [1m40s elapsed]

null_resource.playbook (local-exec): PLAY [all] *********************************************************************

null_resource.playbook (local-exec): TASK [install git] *************************************************************
null_resource.playbook: Still creating... [1m50s elapsed]
null_resource.playbook (local-exec): [WARNING]: Platform linux on host xx.xx.xx.xx is using the discovered Python
null_resource.playbook (local-exec): interpreter at /usr/bin/python3.7, but future installation of another Python
null_resource.playbook (local-exec): interpreter could change the meaning of that path. See
null_resource.playbook (local-exec): https://docs.ansible.com/ansible-
null_resource.playbook (local-exec): core/2.13/reference_appendices/interpreter_discovery.html for more information.
null_resource.playbook (local-exec): changed: [xx.xx.xx.xx]

null_resource.playbook (local-exec): PLAY RECAP *********************************************************************
null_resource.playbook (local-exec): xx.xx.xx.xx  : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

null_resource.playbook: Creation complete after 1m54s [id=6559XXXXXXXXXXXXXX]

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