mito’s blog

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

[Ansible] Terraformモジュールを使って、terraform applyを実行する

はじめに

AnsibleとTerraformを組み合わせると楽できそうだったので、AnsibleのTerraformモジュールを使ってみました。

community.general.terraform module – Manages a Terraform deployment (and plans) — Ansible Documentation

terraformとansibleを機能と用途から比較してみた、今の感想です。

  • Terraformはtfstateもあり作ったリソース(ガワ)を管理しやすいが、中身の構築はremote-exec(Ansibleのshellモジュール相当)しかない。
  • Ansibleはモジュールが冪等性を持っているので中身を構築しやすいが、リソース(ガワ)の管理は外部に任すことになる。


環境

  • Ubuntu: 22.04.1
  • ansible core: 2.13.6
  • terraform: 1.3.4
  • community.general: 6.0.0


tfファイル

tfファイルは、ubuntuを1台立てます。
outputでパブリックIPを表示させ、どのような感じでログが出力されるかも確認します。

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

resource "aws_instance" "mito-ec2" {
    ami           = "ami-03f4fa076d2981b45"
    instance_type = "t2.micro"
    associate_public_ip_address = "true"
    tags = {
        Name = "mito-ec2"
    }
}

output "ec2_ip"{
    value = aws_instance.mito-ec2.public_ip
}


terraformモジュールを使ったPlaybook

terraform apply相当のPlaybookです。
ログを確認したいため、実行結果をデバッグ表示します。

---
- hosts: localhost
  gather_facts: no

  tasks:
  - name: terraform apply
    community.general.terraform:
      project_path: 'terraform/'   # Terraform ディレクトリのルートへのパス
      force_init: yes      # terraform initの実行
      state: present
    register: result
  
  - name: debug
    debug:
      msg: "{{ result }}"


terraform apply相当のPlaybook実行

Playbook実行前のディレクトリ構造です。
terraformのプラグインはまだダウンロードしていません。

$ tree
.
├── terraform
│   └── main.tf
└── terraform_module.yml

1 directory, 2 files
$ 



terraform apply相当のPlaybookを実行します。

結果、outputsのパブリックIPが無事表示されました。これなら扱いやすいです。
terraformコマンドのようにApply complete!も表示されました。

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

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

TASK [terraform apply] **********************************************************************************
changed: [localhost]

TASK [debug] ********************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": true,
        "command": "/usr/bin/terraform apply -no-color -input=false -auto-approve -lock=true /tmp/tmp8yoeiy_e.tfplan",
        "failed": false,
        "outputs": {
            "ec2_ip": {
                "sensitive": false,
                "type": "string",
                "value": "3.113.21.61"
            }
        },
        "state": "present",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "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 22s [id=i-02d5e9a2f24dc2de8]Apply complete! Resources: 1 added, 0 changed, 0 
        destroyed.Outputs:ec2_ip = \"3.113.21.61\"",
        "stdout_lines": [
            "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 22s [id=i-02d5e9a2f24dc2de8]",
            "",
            "Apply complete! Resources: 1 added, 0 changed, 0 destroyed.",
            "",
            "Outputs:",
            "",
            "ec2_ip= \"3.113.21.61\""
        ],
        "workspace": "default"
    }
}

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

$ 


ディレクトリ構造をみると、プラグインもダウンロードされています。

$ tree -a
.
├── terraform
│   ├── .terraform
│   │   └── providers
│   │       └── registry.terraform.io
│   │           └── hashicorp
│   │               └── aws
│   │                   └── 4.39.0
│   │                       └── linux_amd64
│   │                           └── terraform-provider-aws_v4.39.0_x5
│   ├── .terraform.lock.hcl
│   ├── main.tf
│   └── terraform.tfstate
└── terraform_module.yml

8 directories, 5 files
$ 


terraform apply相当のPlaybook実行(2回目)

Playbookを再実行してみます。

Taskの実行結果はokで、ログNo changesも表示されました。
問題なさそうです。

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

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

TASK [terraform apply] **********************************************************************************
ok: [localhost]

TASK [debug] ********************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": false,
        "command": "/usr/bin/terraform apply -no-color -input=false -auto-approve -lock=true /tmp/
        tmprz34gv4k.tfplan",
        "failed": false,
        "outputs": {
            "ec2_ip": {
                "sensitive": false,
                "type": "string",
                "value": "3.113.21.61"
            }
        },
        "state": "present",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "aws_instance.mito-ec2: Refreshing state... [id=i-02d5e9a2f24dc2de8]No changes. Your 
        infrastructure matches the configuration.Terraform has compared your real infrastructure against 
        your configurationand found no differences, so no changes are needed.",
        "stdout_lines": [
            "aws_instance.mito-ec2: Refreshing state... [id=i-02d5e9a2f24dc2de8]",
            "",
            "No changes. Your infrastructure matches the configuration.",
            "",
            "Terraform has compared your real infrastructure against your configuration",
            "and found no differences, so no changes are needed."
        ],
        "workspace": "default"
    }
}

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

$ 


terraform destroy相当のPlaybook実行

Playbookのstateを、presentからabsentに書き換えて実行します。

結果、applyと同様に、terraformコマンドの結果が表示され、Destroy complete!も確認できました。
plan結果も出力されています。

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

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

TASK [terraform destroy] ********************************************************************************
changed: [localhost]

TASK [debug] ********************************************************************************************
ok: [localhost] => {
    "msg": {
        "changed": true,
        "command": "/usr/bin/terraform destroy -no-color -auto-approve -lock=true",
        "failed": false,
        "outputs": {},
        "state": "absent",
        "stderr": "",
        "stderr_lines": [],
        "stdout": "aws_instance.mito-ec2: Refreshing state... [id=i-02d5e9a2f24dc2de8] Terraform used the 
        selected providers to generate the following executionplan. Resource actions are indicated with 
        the following symbols:  - destroy  Terraform will perform the following actions:  # aws_instance.
        mito-ec2 will be destroyed  - resource \"aws_instance\" \"mito-ec2\" {      - 
        ami                                  = \"ami-03f4fa076d2981b45\" -> null      - 
        (略)
        Plan: 0 to add, 0 to change, 1 to destroy.
        Changes to Outputs:  - ec2_ip = \"3.113.21.61\" -> null aws_instance.mito-ec2: 
        Destroying... [id=i-02d5e9a2f24dc2de8] aws_instance.mito-ec2: Still destroying... 
        [id=i-02d5e9a2f24dc2de8, 10s elapsed] aws_instance.mito-ec2: Still destroying... 
        [id=i-02d5e9a2f24dc2de8, 20s elapsed] aws_instance.mito-ec2: Still destroying... 
        [id=i-02d5e9a2f24dc2de8, 30s elapsed] aws_instance.mito-ec2: Destruction complete after 
        40s Destroy complete! Resources: 1 destroyed.",
        "stdout_lines": [
            "aws_instance.mito-ec2: Refreshing state... [id=i-02d5e9a2f24dc2de8]",
            "",
            "Terraform used the selected providers to generate the following execution",
            "plan. Resource actions are indicated with the following symbols:",
            "  - destroy",
            "",
            "Terraform will perform the following actions:",
            "",
            "  # aws_instance.mito-ec2 will be destroyed",
            "  - resource \"aws_instance\" \"mito-ec2\" {",
            "      - ami                                  = \"ami-03f4fa076d2981b45\" -> null",
            (略)
            "      - root_block_device {",
            "          - delete_on_termination = true -> null",
            "          - device_name           = \"/dev/sda1\" -> null",
            "          - encrypted             = false -> null",
            "          - iops                  = 100 -> null",
            "          - tags                  = {} -> null",
            "          - throughput            = 0 -> null",
            "          - volume_id             = \"vol-0a085b087b420fd22\" -> null",
            "          - volume_size           = 8 -> null",
            "          - volume_type           = \"gp2\" -> null",
            "        }",
            "    }",
            "",
            "Plan: 0 to add, 0 to change, 1 to destroy.",
            "",
            "Changes to Outputs:",
            "  - ec2_ip = \"3.113.21.61\" -> null",
            "aws_instance.mito-ec2: Destroying... [id=i-02d5e9a2f24dc2de8]",
            "aws_instance.mito-ec2: Still destroying... [id=i-02d5e9a2f24dc2de8, 10s elapsed]",
            "aws_instance.mito-ec2: Still destroying... [id=i-02d5e9a2f24dc2de8, 20s elapsed]",
            "aws_instance.mito-ec2: Still destroying... [id=i-02d5e9a2f24dc2de8, 30s elapsed]",
            "aws_instance.mito-ec2: Destruction complete after 40s",
            "",
            "Destroy complete! Resources: 1 destroyed."
        ],
        "workspace": "default"
    }
}

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

$ 


初回実行時にinitしていない場合のエラー

Playbookの初回実行時にforce_init: yesがない場合、プラグインがないため、以下のエラーメッセージが出力されます。

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

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

TASK [terraform apply] **********************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "cmd": "/usr/bin/terraform validate", "msg": "╷Error: 
Missing required providerThis configuration requires provider registry.terraform.io/hashicorp/aws,but 
that provider isn't available. You may be able to install itautomatically by running:  terraform init╵", 
"rc": 1, "stderr": "╷Error: Missing required providerThis configuration requires provider registry.
terraform.io/hashicorp/aws,but that provider isn't available. You may be able to install itautomatically 
by running:  terraform init╵", "stderr_lines": ["", "Error: Missing required provider", "", "This 
configuration requires provider registry.terraform.io/hashicorp/aws,", "but that provider isn't 
available. You may be able to install it", "automatically by running:", "  terraform init", ""], 
"stdout": "", "stdout_lines": []}

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

$ 


備考

Terraformモジュールは、デフォルト設定のパラメータがいくつかあります。
一部のデフォルト値と内容をメモします。

  • force_init: no
    • yesの場合、terraform initを実行する
  • check_destroy: no
    • yesの場合、applyによりdestroyのみが発生する場合は実行しない。ただしdestroyしてaddする場合は実行する
    • yesでも、stateがabsentの場合(terraform destroy)は実行する
  • overwrite_init : yes
    • project_pathに、tfstateが既に存在する場合でも init を実行する