てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Terraform/AAP] Terraform で Automation Controller を設定する ansible/aap プロバイダーを試す

はじめに

Terraform から AAP (厳密には Automation Controller)の設定を操作する ansible/aap プロバイダー 1.0.0 が 2024/05/01 にリリースされました。

registry.terraform.io

これまでも、Terraform から Playbook を呼び出す ansible/ansible プロバイダーや、逆に Ansible から Terraform を呼び出す cloud.terraform コレクションがありました。

今回リリースされた Terraform の ansible/aap プロバイダーは既存のものとは別用途で、 Automation Controller の設定を Terraform から行うものです。Ansiblle でいうと ansible.controller / awx.awx コレクションにあたるものかなと思います。

簡単ですが、ためしてみます。

  • 環境
    • ansible/aap プロバイダー 1.0.0
    • Automation Controller 4.1.0
    • Terraform v1.8.2

ansible/aap プロバイダーの機能

バージョン 1.0.0 での機能は以下のとおりです。現在のところは限定的です。

おためし

構成

インベントリーを1つ作り、その中にグループ、その中に1つのホストを作ります。

インベントリー network_devices
  グループ ios
    ホスト ios01

tf ファイル

一通り tf ファイルを作成します。

terraform.tf

terraform {
  required_providers {
    aap = {
      source = "ansible/aap"
      version = "1.0.0"
    }
  }
}

providers.tf

provider "aap" {
  host                             = "https://192.168.1.99"  # Automation Controller の アドレス
  insecure_skip_verify = true
}

ほか、本プロバイダーの provider ブロックでは、usernamepassword指定できます

ベタ書きを避けるため、ここではそれぞれ環境変数 AAP_USERNAMEAAP_PASSWORD を利用します。この方法は 1.0.0 現在、ドキュメント上には README.md の Acceptance tests でのみ記載があります。

もっと一般的な方法としては、別途 variable を定義して export TF_VAR_変数名="値" で設定しておき username = 変数名 のようにしておくのも良いかなと思います。

main.tf

各種リソースの定義をします。

resource "aap_inventory" "network_devices" {
  name        = "network_devices"
  description = "Managed by Terraform"
}

resource "aap_group" "ios" {
  name         = "ios"
  description  = "Managed by Terraform"
  inventory_id = aap_inventory.network_devices.id
  variables = jsonencode(
    {
      "ansible_network_os" : "cisco.ios.ios"
    }
  )
}

resource "aap_host" "ios01" {
  name         = "ios01"
  description  = "Managed by Terraform"
  inventory_id = aap_inventory.network_devices.id
  groups       = [aap_group.ios.id]
  variables = jsonencode(
    {
      "ansible_host" : "192.168.1.11"
    }
  )
}

data "aap_inventory" "network_devices" {
  id = aap_inventory.network_devices.id
}

aap_inventory リソースで、organization を指定していませんが、その場合はデフォルトの組織になるそうです。

最後の aap_inventory データソースはあまり意味はないですが、どんな情報が得られるか確認するために定義します。id は インベントリー ID を指定します。今回は Terraform 管理化のインベントリーの情報を参照しているため、〜.id という形で指定しています。Terraform 管理外のものの場合はあらかじめ ID を調べておく必要があります。

outputs.tf

おまけ的に定義した aap_inventory データーソースで得られる情報の中身を確認するため、output ブロックも定義します。

output "inventory_details" {
  value = data.aap_inventory.network_devices
}

認証情報の設定

provider ブロックで直接 usernamepassword を指定しなかった代わりに、環境変数を指定しています。

 export AAP_USERNAME="admin"
 export AAP_PASSWORD="dummy_password_xxx"

init

% terraform init

Initializing the backend...

Initializing provider plugins...
- Finding ansible/aap versions matching "1.0.0"...
- Installing ansible/aap v1.0.0...
- Installed ansible/aap v1.0.0 (self-signed, key ID 41F01D0480007165)

...()...

Terraform has been successfully initialized!
...()...

plan

% terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

  # data.aap_inventory.network_devices will be read during apply
  # (config refers to values not yet known)
 <= data "aap_inventory" "network_devices" {
      + description  = (known after apply)
      + id           = (known after apply)
      + name         = (known after apply)
      + organization = (known after apply)
      + url          = (known after apply)
      + variables    = (known after apply)
    }

  # aap_group.ios will be created
  + resource "aap_group" "ios" {
      + description  = "Managed by Terraform"
      + id           = (known after apply)
      + inventory_id = (known after apply)
      + name         = "ios"
      + url          = (known after apply)
      + variables    = jsonencode(
            {
              + ansible_network_os = "cisco.ios.ios"
            }
        )
    }

  # aap_host.ios01 will be created
  + resource "aap_host" "ios01" {
      + description  = "Managed by Terraform"
      + enabled      = true
      + groups       = [
          + (known after apply),
        ]
      + id           = (known after apply)
      + inventory_id = (known after apply)
      + name         = "ios01"
      + url          = (known after apply)
      + variables    = jsonencode(
            {
              + ansible_host = "192.168.1.11"
            }
        )
    }

  # aap_inventory.network_devices will be created
  + resource "aap_inventory" "network_devices" {
      + description  = "Managed by Terraform"
      + id           = (known after apply)
      + name         = "network_devices"
      + organization = (known after apply)
      + url          = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + inventory_details = {
      + description  = (known after apply)
      + id           = (known after apply)
      + name         = (known after apply)
      + organization = (known after apply)
      + url          = (known after apply)
      + variables    = (known after apply)
    }

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.

apply

% terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 <= read (data resources)

Terraform will perform the following actions:

  # data.aap_inventory.network_devices will be read during apply
  # (config refers to values not yet known)
 <= data "aap_inventory" "network_devices" {
      + description  = (known after apply)
      + id           = (known after apply)
      + name         = (known after apply)
      + organization = (known after apply)
      + url          = (known after apply)
      + variables    = (known after apply)
    }

  # aap_group.ios will be created
  + resource "aap_group" "ios" {
      + description  = "Managed by Terraform"
      + id           = (known after apply)
      + inventory_id = (known after apply)
      + name         = "ios"
      + url          = (known after apply)
      + variables    = jsonencode(
            {
              + ansible_network_os = "cisco.ios.ios"
            }
        )
    }

  # aap_host.ios01 will be created
  + resource "aap_host" "ios01" {
      + description  = "Managed by Terraform"
      + enabled      = true
      + groups       = [
          + (known after apply),
        ]
      + id           = (known after apply)
      + inventory_id = (known after apply)
      + name         = "ios01"
      + url          = (known after apply)
      + variables    = jsonencode(
            {
              + ansible_host = "192.168.1.11"
            }
        )
    }

  # aap_inventory.network_devices will be created
  + resource "aap_inventory" "network_devices" {
      + description  = "Managed by Terraform"
      + id           = (known after apply)
      + name         = "network_devices"
      + organization = (known after apply)
      + url          = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + inventory_details = {
      + description  = (known after apply)
      + id           = (known after apply)
      + name         = (known after apply)
      + organization = (known after apply)
      + url          = (known after apply)
      + variables    = (known after apply)
    }

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

aap_inventory.network_devices: Creating...
aap_inventory.network_devices: Creation complete after 0s [name=network_devices]
data.aap_inventory.network_devices: Reading...
aap_group.ios: Creating...
data.aap_inventory.network_devices: Read complete after 0s [name=network_devices]
aap_group.ios: Creation complete after 0s [name=ios]
aap_host.ios01: Creating...
aap_host.ios01: Creation complete after 1s [name=ios01]

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

Outputs:

inventory_details = {
  "description" = "Managed by Terraform"
  "id" = 4
  "name" = "network_devices"
  "organization" = 1
  "url" = "/api/v2/inventories/4/"
  "variables" = tostring(null)
}

処理はすぐに終わりました。

Outputs: には aap_inventory データソースで取得したインベントリの値が表示されました。

確認

無事に、インベントリー、グループ、ホストが作成されていました。

インベントリー

グループ

ホスト

destroy

% terraform destroy
aap_inventory.network_devices: Refreshing state... [name=network_devices]
data.aap_inventory.network_devices: Reading...
aap_group.ios: Refreshing state... [name=ios]
data.aap_inventory.network_devices: Read complete after 0s [name=network_devices]
aap_host.ios01: Refreshing state... [name=ios01]

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:

  # aap_group.ios will be destroyed
  - resource "aap_group" "ios" {
      - description  = "Managed by Terraform" -> null
      - id           = 3 -> null
      - inventory_id = 4 -> null
      - name         = "ios" -> null
      - url          = "/api/v2/groups/3/" -> null
      - variables    = jsonencode(
            {
              - ansible_network_os = "cisco.ios.ios"
            }
        ) -> null
    }

  # aap_host.ios01 will be destroyed
  - resource "aap_host" "ios01" {
      - description  = "Managed by Terraform" -> null
      - enabled      = true -> null
      - groups       = [
          - 3,
        ] -> null
      - id           = 4 -> null
      - inventory_id = 4 -> null
      - name         = "ios01" -> null
      - url          = "/api/v2/hosts/4/" -> null
      - variables    = jsonencode(
            {
              - ansible_host = "192.168.1.11"
            }
        ) -> null
    }

  # aap_inventory.network_devices will be destroyed
  - resource "aap_inventory" "network_devices" {
      - description  = "Managed by Terraform" -> null
      - id           = 4 -> null
      - name         = "network_devices" -> null
      - organization = 1 -> null
      - url          = "/api/v2/inventories/4/" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Changes to Outputs:
  - inventory_details = {
      - description  = "Managed by Terraform"
      - id           = 4
      - name         = "network_devices"
      - organization = 1
      - url          = "/api/v2/inventories/4/"
      - variables    = null
    } -> null

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aap_host.ios01: Destroying... [name=ios01]
aap_host.ios01: Destruction complete after 1s
aap_group.ios: Destroying... [name=ios]
aap_group.ios: Destruction complete after 0s
aap_inventory.network_devices: Destroying... [name=network_devices]
aap_inventory.network_devices: Destruction complete after 0s

Destroy complete! Resources: 3 destroyed.

画面を確認すると、無事にインベントリー、グループ、ホストが削除されていました。もちろん、今回作成する前に存在していたその他のリソースは残ったままです。

おわりに

簡単ですが、Terraform の ansible/aap プロバイダーを使って、インベントリー、グループ、ホストの作成と削除をためしてみました。

バージョン 1.0.0 がリリースされたばかりということで、クレデンシャルやジョブテンプレートの管理には対応していなくて、まだまだ機能は限定的です。ですが、Automation Controller の設定と Terraform の使用感は相性がいいようにも思いました。たとえば、インベントリーとグループ、ホストの関係を、暗黙的な依存で表現でき、かつ tf ファイルの記述順自体は意識しなくてよい点です。

今後も ansible/aap プロバイダーには注目したいと思います。

今のところ、Automation Controller / AWX を設定の自動化は、機能が充実している Ansible の ansible.controller / awx.awx コレクションを使うのが良いと思います。