TerraformでAWS IaC実践 第2回 HCL2構文基礎

今回は、記述言語であるHCL2の基礎と、Terraformファイルの概要についてアウトプットしていきます。環境構築等は前回のコラムを参照してください。

camelrush.hatenablog.com

HCL2

HCL2(HashiCorp Language 2)言語とは

HCL(HashiCorp Configuration Language)2は、HashiCorpによって開発された設定言語の第二世代です。Terraformや他のHashiCorp製品で広く使用されている言語で、インフラストラクチャーの設定や管理を宣言的に記述するために設計されています。

<HCL2 例>

# ------------------------
# VPC
# ------------------------
resource "aws_vpc" "vpc" {
  cidr_block                       = "192.168.0.0/20"
  instance_tenancy                 = "default"
  enable_dns_support               = true
  enable_dns_hostnames             = true
  assign_generated_ipv6_cidr_block = false
  # Tagづけ
  tags = {
    Name    = "${var.project}-${var.environment}-vpc"
    Project = var.project
    Env     = var.environment
  }

ブロック構造で記述されているため、パッと見はJSONに少し似ていますが、途中で # によるコメントを差し込めたりするなど、独自の構文となっています。

データ型

HCL2では、次のデータ型が使用できます。

データ型 概要
string 文字列を表します。
number 数値を表します。
bool 真偽値(true/false)を表します。
list 配列やリストを表します。
map キーと値のペアのマップを表します。
object オブジェクトや構造体を表します。
tuple 固定長の要素を持つタプルを表します。
set 一意な値のセットを表します。

Terraformファイル

Terraformの管理ファイルには、主に以下のファイルが存在します。

  • *.tfファイル:インフラリソース等の定義ファイル。ファイル内容はresource、data等、いくつかのブロックに分けて記述する(後述)
  • terraform.tfvars:変数定義ファイル。Terraform全体で使用する変数を定義する。ファイル名は固定。
  • terraform.tfstate:Terraformで生成されたリソースの状態管理ファイル。Terraform Applyによって固定のファイル名で自動生成される。

ディレクトリ構造については、現在も多くの議論が交わされているようですが、tfファイルはリソースごとに分離することは共通しているようです。

上記のうち、tfファイルについてご説明します。ファイル内容は、HCL2のテキスト形式で記述し、いくつかのブロックに分けて作成します。

ブロックタイプ

tfファイルは、ブロックと呼ばれる次の記述に分かれています。

ブロックタイプ 概要
locals 変数。外部から参照できないもの
variable 変数。外部から参照できるもの
terraform Terraformの設定
provider プロバイダ
data Terraform管理していないリソースの取り込み
resource Terraformの管理対象となるリソース
outputs 外部から参照できるようにする値

ブロック詳細

locals

localsは、tfファイル内で使用するローカル変数を定義するものです。

# localsブロックで変数をKey = valueで定義する
locals {
    env = "dev"
}

resource "aws_instance" "hello-world" {
  ami           = "ami-04e0b6d6cfa432943"
  instance_type = "t2.micro"

  # 変数は ${local.変数名}で使用する
  tags = {
    Name = "HelloWorld"
    Env = "test-ec2-${local.env}"
  }
}

variables

variablesは、localsと同様、変数を定義します。

定義した変数は、ファイルの外部から参照できます。

# 変数ごとにvariableブロックで変数を定義する
# type にデータ型を指定する
variable "env" {
  type    = string
  default = "stg"
}

resource "aws_instance" "hello-world" {
  ami           = "ami-04e0b6d6cfa432943"
  instance_type = "t2.micro"

  # 変数は ${var.変数名}で使用する
  tags = {
    Name = "HelloWorld"
    Env = "test-ec2-${var.env}"
  }
}

上記で使用するデータ型には、以下のものが存在します。

  • プリミティブ型
    • string:Unicode文字列
    • number:数値。整数と小数の両方を表現。
    • bool:true/falseの2値
  • 構造体
    • object({<NAME=, ...}):キーバリュー型データ
    • tuple([<TYPE], ...):各列の型が決まっている配列
  • コレクション
    • list([TYPE]):特定の型で構成される配列
    • map([TYPE]):キーが文字列の配列
    • set([TYPE]):値の重複がない配列

変数 variablesは、以下に示すいくつかの方法で値を書き換えることができます。

  1. OS環境変数で値を設定する。

    • bash等の環境変数として、環境変数名に接頭詞 TF_VAR_ をつけて定義し、値を設定します。

      • main.tf

        variable "env" {
          type    = string
          default = "stg"
        }
        
      • Terraform 実行時のShell

        $ export TF_VAR_env="prd"
        $ terraform apply
        
    • 環境変数で指定した変数値はログに残らないため、秘匿性のある変数はこちらを使用します。

  2. 変数ファイルterraform.tfvarsで値を設定する。

    • terraform.tfvars(ファイル名固定)を定義し、その中で値を設定します。
      • terraform.tfvars

         env = "debug"
        
  3. terraformコマンドの引数 -var-var-file [ファイル名]で値を設定する

    • terraform applyコマンドの引数として値を設定します。
      • 実行コマンド

         $ terraform apply -var env="test"
        

terraform

terraformブロックは、terraform自体の設定に関する値を指定します。代表的な例としては、required_versionでterraformの動作バージョン等を指定します。

# このtfファイルでは、terraform 1.8.2 以上のバージョンで動作することを定義
terraform {
  required_version = "~>1.8.2"
}

また、Terraformのリソース状態を管理するファイルである .tfstateファイルを管理する場所を指定する場合は、backend をこちらに記述します。

# backendブロックで、tfstateをS3上に保存するよう指示
terraform {
  backend "s3" {
    bucket  = "tfstate-bucket-camelrush"
    key     = "dev.tfstate"
    region  = "ap-northeast-1"
    profile = "terraform"
  }
}

provider

providerには、Terraformで使用するプロバイダを指定します。ここには、Terraformが対応しているプロバイダリストの中から選択して指定します。今回はawsですが、azuregoogleのほか、Oracle Cloudといった多くのプロバイダが公開されています。詳細は以下をご確認ください。

registry.terraform.io

なお、awsプロバイダに指定できるパラメタはこちらを参照ください。

最低限の記述としては、AWSへのアクセス情報を設定します。

# -----------------------------
# Provider
# -----------------------------
provider "aws" {
  profile = "terraform"
  region  = "ap-northeast-1"
}

access_keysecret_keyを指定する方法もありますが、セキュリティ上は非推奨であり、tfファイル管理もしづらくなりますので、ここはprofileを指定しましょう。

別途、AWS CLIaws configure -profile xxxxx(xxxxxをprofile名と一致させる)を実行して、ローカルプロファイル名(上記の場合は terraform )にアクセスキー等を設定しておきます。

data

dataブロックは、少し特殊な使い方をします。こちらには、Terraform管理外のリソースを、tfファイル等で使用するためのデータ・リストとして取り込む役割があります。

管理外とは、具体的にはAWSプロバイダが提供するID情報などが挙げられます。

以下では、EC2インスタンスを作成するために使用するAMI(PCのイメージ)のIDを指定するため、AWSが提供するAMIのリストから、希望する「Amazon Linux2の最新イメージ」をdataとして取得し、それを元にEC2を作成しています。

# --- data block ---
data "aws_ami" "app" {
  most_recent = true
  owners      = ["self", "amazon"]
  # filter.valuesに*を入れて複数をピックアップしたリストを取得
  # 前述の`most_recent=true`でリストから最新を取得
  filter {
    name   = "name"
    values = ["amzn2-ami-kernel-5.10-hvm-2.0.*.0-x86_64-gp2"]
  }
}

# --- resource block ---
resource "aws_instance" "app_server" {
  # amiには、前述で定義したdataブロックのAMIのIDを指定
  ami = data.aws_ami.app.id

  # 以下、省略
  :
  :
}

dataブロックに使用できる項目は、リファレンスの各サービス下にData Sources として書かれていますので、参照してください。

resource

resourceブロックには、Terraformで作成する各種サービスリソースを定義します。以下の様に記述します。

resource <リソース種類> <リソース名> {
  <各種パラメタ>
}

以下に例を記述します。

# ------------------------
# VPC
# ------------------------
resource "aws_vpc" "vpc" {
  cidr_block                       = "192.168.0.0/20"
  instance_tenancy                 = "default"
  enable_dns_support               = true
  enable_dns_hostnames             = true
  assign_generated_ipv6_cidr_block = false
}

# ------------------------
# Subnet
# ------------------------
resource "aws_subnet" "public_subnet_1a" {
  vpc_id                  = aws_vpc.vpc.id
  availability_zone       = "ap-northeast-1a"
  cidr_block              = "192.168.1.0/24"
  map_public_ip_on_launch = true
}

作成できるAWSリソースと、リソース種類別のパラメタについては、こちらのドキュメントを参照してください。

output

outputは、Terraformの設定間での明示的なデータ共有、状態の可視化、および外部ツールとの統合を容易にするために使用します。

<ec2.tf>

resource "aws_instance" "app_server" {
  ami = data.aws_ami.app.id
    :
}

# 自動で割り当てられたパブリックIPアドレスを出力
output "public_ip" {
  value = aws_instance.app_server.public_ip
}

上記のように定義することで、Terraformコマンドを行ったオペレータに対して、「状態が可視化」されます。

<terraform apply コマンド>

$ irohani-mba:terraform shinya.takada$ terraform apply -auto-approve
random_string.db_password: Refreshing state... [id=gKCzffFsy6bmZDQl]
aws_db_option_group.mysql_standalone_optiongroup: Refreshing state... [id=tastylog-dev-mysql-standalone-optiongroup]
data.aws_prefix_list.s3_pl: Reading...
  :
  :
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

public_ip = "54.249.38.103"

outputはリソース作成自体には、影響を与えません。Terraformは、実行フォルダ配下のtfファイルを全て参照して依存関係を見つけるため、outputを記述しなくても、エラーにはなりません。

外部ツールとの統合では、CI/CDツールとの連携などにも活用されます。

Terraformのファイル構成

Terraformプロジェクトでは、いくつかの主要なファイルとディレクトリが使用されます。これらのファイルは、インフラストラクチャをコードとして管理する際に重要な役割を果たします。以下に、一般的なTerraformプロジェクトのファイル構成の例と、それぞれのファイルの役割を説明します。

一般的なディレクトリ構成

my-terraform-project/
├ main.tf
├ variables.tf
├ outputs.tf
├ terraform.tfvars
├ terraform.tfstate
├ terraform.tfstate.backup
├ .terraform/
 ├ modules/
 └ example_module/
├ main.tf
├ variables.tf
└ outputs.tf

ファイルの説明

  • main.tf

    • Terraformの主要な設定ファイルで、リソースの定義やプロバイダーの設定が含まれます。
  • variables.tf

    • 変数の定義ファイルです。Terraformで使用する変数をここで宣言します。
  • outputs.tf

    • 出力値を定義するファイルです。Terraformが生成する値を他の場所で参照できるようにします。
  • terraform.tfvars

    • 変数の値を設定するファイルです。環境ごとに異なる設定を行う場合に使用します。
  • terraform.tfstate

    • Terraformの状態ファイルで、インフラストラクチャの現在の状態が記録されます。このファイルはTerraformによって自動的に管理されます。
  • terraform.tfstate.backup

    • 状態ファイルのバックアップです。変更を行う前の状態が保存されます。
  • .terraform/

  • modules/

    • モジュールを保存するディレクトリです。再利用可能なTerraformコードをモジュールとしてまとめます。

モジュールの説明

モジュールは、Terraformのコードを再利用可能な単位にまとめたもので、プロジェクト全体の構造を整理しやすくします。例えば、上記の例では example_module というモジュールがあり、以下のファイルが含まれます。

  • modules/example_module/main.tf

    • モジュール内の主要な設定ファイルです。
  • modules/example_module/variables.tf

    • モジュール内で使用する変数を定義します。
  • modules/example_module/outputs.tf

    • モジュールの出力値を定義します。

このようなファイル構成を採用することで、Terraformプロジェクトを整理しやすくし、再利用性を高めることができます。

まとめ

少し長くなってしまいましたので、一旦解説はこの辺にして、次回は、terraformを使用したEC2ネットワークの構成を作っていきます。