🗂

Terraform Checks

2024/02/17に公開

俺とAzureとTerraform ~2024新春バージョン~を読んでて、Checksという機能もあることを知ったので試してみようと思います。

https://developer.hashicorp.com/terraform/language/checks
https://developer.hashicorp.com/terraform/tutorials/configuration-language/checks

検証内容

起動したEC2インスタンスのインスタンスタイプのチェックとインスタンス名のチェックをしてみます。

また前回のソースを使っていきます。

実装

構成はこんな感じです。environments 配下にメインのソース、modules配下にmoduleがあります。ec2_instanceを作成するmoduleになっています。
※valid_ec2.tftest.hclは前回のがそのままあるだけです

-> % tree
.
├── README.md
├── environments
│   └── dev
│       ├── main.tf
│       ├── output.tf
│       └── tests
│           └── valid_ec2.tftest.hcl
├── modules
│   └── ec2
│       ├── README.md
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
└── terraform.tf

5 directories, 9 files

moduleのoutput追加

instance_typeinstance_name をoutputするように追加します。

modules/ec2/outout.tf
output "instance_type" {
  value = aws_instance.example.instance_type
}

output "instance_name" {
  value = aws_instance.example.tags.Name
}

check block追加

check "instance_type"check "name_tag" を追加します。

environments/dev/main.tf
terraform {
  cloud {
    organization = "tsaeki"

    workspaces {
      name = "20240119_aws_dev"
    }
  }
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

    required_version = ">= 1.2.0"
}


provider "aws" {
  region  = "ap-northeast-1"
}

module "ec2" {
  source        = "../../modules/ec2"
  ami_id        = "ami-0506f0f56e3a057a4"
  instance_type = "t2.micro"
  instance_name = "MyEC2Instance"
}

### こちらを追加しました
check "instance_type" {
  assert {
    condition = module.ec2.instance_type == "t2.micro"
    error_message = "The instance type is not t2.micro"
  }
}

check "name_tag" {
  assert {
    condition = module.ec2.instance_name == "MyEC2Instance"
    error_message = "The instance name is not MyEC2Instance"
  }
}

planを実行

terraform plan を実行してみます。問題なく実行されます。

-> % terraform plan
Running plan in Terraform Cloud. Output will stream here. Pressing Ctrl-C
will stop streaming the logs, but will not stop the plan running remotely.

Preparing the remote plan...

The remote workspace is configured to work with configuration at
/environments/dev relative to the target repository.

Terraform will upload the contents of the following directory,
excluding files or directories as defined by a .terraformignore file
at /home/tsaeki/Develop/20240119_aws/.terraformignore (if it is present),
in order to capture the filesystem context the remote workspace expects:
    /home/tsaeki/Develop/20240119_aws

To view this run in a browser, visit:
https://app.terraform.io/app/tsaeki/20240119_aws_dev/runs/run-Rz1pCKz24bXXtX2S

Waiting for the plan to start...

Terraform v1.7.0
on linux_amd64
Initializing plugins and modules...

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:

  # module.ec2.aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-0506f0f56e3a057a4"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + disable_api_stop                     = (known after apply)
      + disable_api_termination              = (known after apply)
      + ebs_optimized                        = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
      + instance_state                       = (known after apply)
      + instance_type                        = "t2.micro"
      + ipv6_address_count                   = (known after apply)
      + ipv6_addresses                       = (known after apply)
      + key_name                             = (known after apply)
      + monitoring                           = (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      + placement_partition_number           = (known after apply)
      + primary_network_interface_id         = (known after apply)
      + private_dns                          = (known after apply)
      + private_ip                           = (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      + secondary_private_ips                = (known after apply)
      + security_groups                      = (known after apply)
      + source_dest_check                    = true
      + subnet_id                            = (known after apply)
      + tags                                 = {
          + "Name" = "MyEC2Instance"
        }
      + tags_all                             = {
          + "Name" = "MyEC2Instance"
        }
      + tenancy                              = (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      + user_data_replace_on_change          = false
      + vpc_security_group_ids               = (known after apply)
    }

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

Changes to Outputs:
  + id = (known after apply)

意図的に間違えてみる

checkの条件を変えて意図的に間違えてみます。

environments/dev/main.tf
### こちらを追加しました
check "instance_type" {
  assert {
    condition = module.ec2.instance_type == "t3.micro" # t3.microに変更
    error_message = "The instance type is not t2.micro"
  }
}

check "name_tag" {
  assert {
    condition = module.ec2.instance_name == "MyEC2InstanceTest" # MyEC2InstanceTestに変更
    error_message = "The instance name is not MyEC2Instance"
  }
}

すると以下のようにWarningが出るようになります。

Waiting for the plan to start...

Terraform v1.7.0
on linux_amd64
Initializing plugins and modules...
╷
│ Warning: Check block assertion failed
│
│   on main.tf line 40, in check "name_tag":40:     condition = module.ec2.instance_name == "MyEC2InstanceTest"
│     ├────────────────
│     │ module.ec2.instance_name is "MyEC2Instance"
│
│ The instance name is not MyEC2Instance
╵
╷
│ Warning: Check block assertion failed
│
│   on main.tf line 33, in check "instance_type":33:     condition = module.ec2.instance_type == "t3.micro"
│     ├────────────────
│     │ module.ec2.instance_type is "t2.micro"
│
│ The instance type is not t2.micro
╵

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:

  # module.ec2.aws_instance.example will be created
  + resource "aws_instance" "example" {
      + ami                                  = "ami-0506f0f56e3a057a4"

こんな感じで事前にリソースのチェックができます。

TestsとChecksの使い分け

俺とAzureとTerraform ~2024新春バージョン~とかTerraformのChecksとTestsの使い分けを参考にするとよさそうです。

ざっくりこんな感じでしょうか。ただ実際に運用してみないとですね・・

Tests

  • modulesのunitテスト
  • integrationテスト

Checks

  • 起動後の動作確認(e2eテスト)

Discussion