はじめに
クラウド環境におけるインフラの自動化において、「Terraformでリソースを作成し、AnsibleでOSやミドルウェアを設定する」 という組み合わせをしているのをよく目にします。
しかし実装方法によっては、以下のような課題が発生します。
-
terraform apply完了後、Ansibleの動的インベントリの更新やPlaybookの手動実行をする手間が発生する - CI/CD パイプライン上で、Terraform 実行後にPlaybook(またはAAP API)を呼び出す処理を実装する必要がある
そこで登場したのが、2025年秋に HashiCorp と Red Hat から発表された 「Terraform actions」 です。
Terraform actions を活用すると、.tf ファイル内にアクションを定義しておくだけで、terraform apply でEC2 インスタンスが作成された直後(after_create)に、Ansible Automation Platform(以下、AAP)上の Job Template を自動で呼び出し、設定完了まで待機するという一気通貫の処理が実現できます。
本記事では、「AWS上へのEC2インスタンスの作成 ~ AAPのJob Template実行」 までの実装手順を解説します。
TL;DR
この記事を読めば、以下のことができるようになります
- Terraform actions の概要と aap プロバイダーの書き方
-
terraform applyの中で、リソース作成 ~ AAPの Job Template まで自動実行させる方法
前提条件
- Ansible実行環境が、EC2インスタンスの作成権限以上を所有していること
- 本記事では、検証用にAmazonEC2FullAccessを所有しています
- AAPにアクセスするユーザーがAAPオブジェクトの操作権限以上を所有していること
- 本記事では、検証用にadminユーザーを使用しています
- SSH接続用のキーペアがAWS上に作成済みであること
実行環境
- Ansible Automation Platform: 2.7
- Automation Controller: 4.8.6
- Ansible
- ansible-core: 2.19.8
詳細
1. フォルダ構造
フォルダ構造は下記です。ファイルの中身は後程解説します。
.
├── aap.tf
├── ec2.tf
├── main.tf
├── outputs.tf
├── terraform.tfvars
└── variables.tf
2. ファイル説明
./main.tf
ここでは、TerraformのバージョンやAWS/AAPプロバイダーの定義のみをします。
EC2インスタンスの作成に必要な情報やAAPの情報は別ファイルにわけることで視認性の向上に役立ちます。
terraform {
required_version = ">= 1.14.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
aap = {
source = "ansible/aap"
version = "~> 1.4.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
provider "aap" {}
./ec2.tf
作成対象となるEC2インスタンス本体を定義するファイルです。
resource "aws_instance" "my_instance" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = aws_subnet.my_subnet.id
vpc_security_group_ids = [aws_security_group.my_sg.id]
associate_public_ip_address = true
key_name = var.key_name
root_block_device {
volume_size = 8
volume_type = "gp3"
tags = {
Name = "${var.instance_name}-root-volume"
}
}
depends_on = [
aws_internet_gateway.my_igw,
aws_security_group.my_sg
]
tags = {
Name = "${var.instance_name}-instance"
}
}
resource "aws_vpc" "my_vpc" {
cidr_block = var.vpc_cidr
tags = {
Name = "${var.instance_name}-vpc"
}
}
resource "aws_subnet" "my_subnet" {
vpc_id = aws_vpc.my_vpc.id
cidr_block = var.subnet_cidr
map_public_ip_on_launch = true
availability_zone = "ap-northeast-1a"
tags = {
Name = "${var.instance_name}-subnet"
}
}
resource "aws_internet_gateway" "my_igw" {
vpc_id = aws_vpc.my_vpc.id
tags = {
Name = "${var.instance_name}-igw"
}
}
resource "aws_route_table" "my_rt" {
vpc_id = aws_vpc.my_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.my_igw.id
}
tags = {
Name = "${var.instance_name}-rt"
}
}
resource "aws_route_table_association" "my_rta" {
subnet_id = aws_subnet.my_subnet.id
route_table_id = aws_route_table.my_rt.id
}
resource "aws_security_group" "my_sg" {
name = "${var.instance_name}-sg"
description = "Security group for ${var.instance_name}"
vpc_id = aws_vpc.my_vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.instance_name}-sg"
}
}
./aap.tf
Job Templateの自動実行(action)を定義する、本記事の核心となるファイルです。
actionであるaap_job_launchの詳細は、TerraformのドキュメントTerraform Registry > aap_job_launchで確認可能です。
data "aap_organization" "organization" {
name = var.aap_organization_name
}
data "aap_inventory" "inventory" {
name = var.aap_inventory_name
organization_name = data.aap_organization.organization.name
}
data "aap_job_template" "job_template" {
name = var.aap_job_template_name
organization_name = data.aap_organization.organization.name
}
resource "aap_host" "host" {
inventory_id = data.aap_inventory.inventory.id
name = aws_instance.my_instance.tags["Name"]
variables = jsonencode({
ansible_host = aws_instance.my_instance.public_ip
})
depends_on = [aws_route_table_association.my_rta]
lifecycle {
replace_triggered_by = [aws_instance.my_instance]
action_trigger {
events = [after_create]
actions = [action.aap_job_launch.job_launch]
}
}
}
action "aap_job_launch" "job_launch" {
config {
job_template_id = data.aap_job_template.job_template.id
wait_for_completion = true
wait_for_completion_timeout_seconds = var.aap_job_timeout_seconds
ignore_job_results = false
}
}
ポイントは以下の通りです。
-
resource "aap_host"における、variablesの活用- 作成された EC2 インスタンスのホスト名や IP アドレス(ansible_host)などの情報を、AAP 上のインベントリに登録します。
-
resource "aap_host"における、depends_onの活用- ルーティング設定(aws_route_table_association)の完了を待つことで、EC2作成直後にAAPから到達不能になるエラーを防いでいます。
-
resource "aap_host"における、lifecycle.action_trigger.eventsの活用- EC2インスタンスの作成が完了した直後(
after_create)に、aap_job_launchを自動呼び出しします。
- EC2インスタンスの作成が完了した直後(
./terraform.tfvars
環境ごとに固有となる変数の具体的な値を定義するファイルです。
instance_name = "test-ec2"
key_name = "test_key"
aap_inventory_name = "inv_terraform"
aap_job_template_name = "Terraform Test Job"
./variables.tf
本設定で利用する各変数の型やデフォルト値を定義するファイルです。
# vars for EC2
variable "ami_id" {
description = "The AMI to use for the instance."
type = string
default = "ami-XXXXXXXXXXXXXXXXX"
}
variable "instance_type" {
description = "The type of instance."
type = string
default = "t2.micro"
}
variable "instance_name" {
description = "Name of the EC2 instance."
type = string
default = "test-ec2-instance"
}
variable "vpc_cidr" {
description = "The CIDR block for the VPC."
type = string
default = "10.0.0.0/16"
}
variable "subnet_cidr" {
description = "The CIDR block for the subnet."
type = string
default = "10.0.1.0/24"
}
variable "key_name" {
description = "Key name for the EC2 instance."
type = string
}
# vars for AAP
variable "aap_organization_name" {
description = "AAP organization containing the job template."
type = string
default = "Default"
}
variable "aap_inventory_name" {
description = "Existing AAP inventory name in aap_organization_name. Terraform does not create or delete this inventory."
type = string
}
variable "aap_job_template_name" {
description = "Existing AAP job template with a machine credential matching key_name."
type = string
}
variable "aap_job_timeout_seconds" {
description = "Maximum time to wait for the AAP job to complete."
type = number
default = 600
}
./outputs.tf
terraform apply 完了後に、作成されたEC2インスタンスのIDやパブリックIPを表示するための出力定義ファイルです。
output "instance_id" {
description = "The ID of the EC2 instance."
value = aws_instance.my_instance.id
}
output "public_ip" {
description = "The public IP address of the EC2 instance."
value = aws_instance.my_instance.public_ip
}
3. AAPにTerraform連携用オブジェクトを作成
Inventoryの作成
Ansible実行対象の情報はTerraformのaap_hostによって登録されるので、インベントリの箱だけを作っておきます。
- AAPにて
Automation Execution>Infrastructure>インベントリーを選択 - [インベントリーの作成]の後、さらに[インベントリーの作成]を選択
- 以下を入力して、[インベントリーの作成]を選択
- 名前: inv_terraform
- 組織: Default
Credentialの作成
Ansible実行対象への接続に必要な認証情報を定義します。
- AAPにて
Automation Execution>Infrastructure>認証情報を選択 - [認証情報の作成]を選択
- 以下を入力して、[認証情報の作成]を選択
- 名前: cred_target_node
- 組織: Default
- ユーザー名: ec2-user
- SSH Private Key: 秘密鍵の中身を添付
Job Templateの作成
- AAPにて
Automation Execution>テンプレートを選択 - [テンプレートの作成]の後、さらに[ジョブテンプレートの作成]を選択
- 以下を入力して、[ジョブテンプレートの作成]を選択
- 名前: Terraform Test Job
- インベントリー: inv_terraform
- プロジェクト: 任意のプロジェクト
- Playbook: 任意のPlaybook
- サンプル用Playbook(display_facts.yml)は後述
- 認証情報: cred_target_node
今回のサンプル用Playbook(display_facts.yml)は以下のように、ファクトを収集してDistributionを表示します。
---
- hosts: all
gather_facts: false
tasks:
- name: Gather minimal facts
ansible.builtin.setup:
gather_subset:
- '!all'
- name: Display distribution
ansible.builtin.debug:
var: ansible_facts['distribution']
4. Terraformの実行
-
Terraformを実行する環境で、環境変数を定義します
export AAP_HOST=https://<aap_ip_address> export AAP_USERNAME=admin export AAP_PASSWORD=<admin_password> export AAP_INSECURE_SKIP_VERIFY=true -
Terraformを実行します
$ terraform init $ terraform plan $ terraform apply
5. 実行結果の確認
Terraform実行画面
実行結果の最後の方を見ると、Action startedでAAPのJob Templateが実行されたことが見受けられます。
Action started: action.aap_job_launch.job_launch (triggered by aap_host.host)
Action complete: action.aap_job_launch.job_launch (triggered by aap_host.host)
╷
│ Warning: Unable to determine EDA API endpoint
│
│ with provider["registry.terraform.io/ansible/aap"],
│ on main.tf line 20, in provider "aap":
│ 20: provider "aap" {}
│
│ The EDA endpoint is missing from response
╵
Apply complete! Resources: 8 added, 0 changed, 0 destroyed. Actions: 1 invoked.
Outputs:
instance_id = "i-XXXXXXXXXXXXXXXXX"
public_ip = "XXX.XXX.XXX.XXX"
実行結果でWarningが出ていますが、今回はEDAを使用していないので無視して問題ありません
Job Template実行結果
Terraform actionsによってJob Templateが実行されて、ファクトを収集できていることが確認できます。
PLAY [all] ********************************************************************* 13:30:50
TASK [Gather minimal facts] **************************************************** 13:30:50
ok: [test-ec2-instance]
TASK [Display distribution] **************************************************** 13:30:52
ok: [test-ec2-instance] => {
"ansible_facts['distribution']": "Amazon"
}
PLAY RECAP *********************************************************************
test-ec2-instance : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
さいごに
本記事では、Terraform actionsとaap プロバイダーを活用し、terraform applyでEC2 インスタンスの作成から AAP による Playbook の実行までを一気通貫で自動化する手順を解説しました。
Terraform actions を活用すれば、複雑な CI/CD パイプラインを組んだりすることなく、インフラの構築からJob Templateの実行までを1つのワークフローとしてシンプルに完結できます。
今後インフラの構築から初期設定までを一気に自動化したいと考えている方は、ぜひ今回の Terraform actionsを試してみてください!
参考URL
-
HashiCorp と Ansible Automation Platform のスタートガイド
- TerraformでAAPを実行する際の実装例・ポイントなど
-
Terraform & Ansible: Unifying infrastructure provisioning and configuration management
- TerraformからAAPを直接呼び出して運用を自動化する新機能「Terraform actions」の紹介
-
Terraform Registry > aap_job_launch
- actionである
aap_job_launchの公式ドキュメント
- actionである