TerraformのModuleの基本的な使い方を試す

TerraformでModuleを使ってみる。

ドキュメントによるとメイン作業ディレクトリ内の.tfファイルはすべてroot moduleになるとのこと。
リソースを別なmoduleに分割することで再利用可能な状態にできる模様。

Moduleを試す

早速試してみる。
今回はVPCを作成し、そこにEC2を1台起動するTerraformのサンプルとする。

ディレクトリ構成は下記としてみる。

terraform(作業ディレクトリ)以下にmodulesディレクトリとdevelopmentディレクトリを作成する。
modulesディレクトリ配下には ec2, vpc といったリソース単位のディレクトリを作成し、その下にmain.tfファイルを設置する。
vpcディレクトリ配下にはoutput.tfというファイルを設置する。これはec2リソースのmain.tfからvpcリソース設定情報を読み込むための設定情報を出力するためのもの。

developmentディレクトリ配下にも必要な.tfファイルを設置する。
developmentディレクトリ配下でterraform applyを実行すると、modules配下の.tfファイルが読み込まれて実行されるような流れを想定している。

terraform/
├── modules/
|     ├── ec2/
|     |     └── main.tf
|     └── vpc/
|            ├── main.tf
|            └── output.tf
|
└── development/
       ├── main.tf
       ├── variables.tf
       └── config.tfvars

development以下の構成

development/main.tfを下記とする。

module ブロックにmodule module_name(ラベル) {}の形でmodule/vpcmodule/ec2を呼び出せるように指定をする。
ここでのmodule_name名(ラベル)はローカルのterraform内で使う値であり、呼び出す側のモジュール(ここではmodule_ec2)はmodule_name名を使って他のモジュールの内容(module_vpc内の変数)を参照できる。

provider "aws" {
  region  = "${var.region}"
  profile = "${var.profile}"
}

# VPC
module "module_vpc" {
  source = "../modules/vpc"
}

module "module_ec2" {
  source = "../modules/ec2"

  vpc_id = "${module.module_vpc.vpc_id}"
  subnet_public_a_id = "${module.module_vpc.subnet_public_a_id}"
  subnet_public_c_id = "${module.module_vpc.subnet_public_c_id}"
}

provider情報は下記のように切り出したがここは本質ではないのでどうでも良い。

  • development/variables.tf
  • variable "region" {}
    variable "profile" {}
    
  • development/config.tfvars
  • region = "ap-northeast-1"
    profile = "default"
    

modules以下の構成

modules/vpc/以下の構成

modules/vpc/main.tfにVPCなど必要なリソースを作成するように定義する。

  • modules/vpc/main.tf
  • # VPC
    resource "aws_vpc" "this" {
      cidr_block           = "10.1.0.0/16"
      instance_tenancy     = "default"
      enable_dns_support   = "true"
      enable_dns_hostnames = "true"
    
      tags {
        Name = "tf-sample-vpc"
      }
    }
    
    # InternetGateway
    resource "aws_internet_gateway" "this" {
      vpc_id = "${aws_vpc.this.id}"
    }
    
    # RouteTable
    resource "aws_route_table" "public" {
      vpc_id = "${aws_vpc.this.id}"
    
      route {
        cidr_block = "0.0.0.0/0"
        gateway_id = "${aws_internet_gateway.this.id}"
      }
    
      tags {
        Name = "tf-sample-public-rt"
      }
    }
    
    # Subnet
    resource "aws_subnet" "public_a" {
      vpc_id            = "${aws_vpc.this.id}"
      cidr_block        = "10.1.1.0/24"
      availability_zone = "ap-northeast-1a"
    
      tags {
        Name = "tf-sample-public-a-subnet"
      }
    }
    
    resource "aws_subnet" "public_c" {
      vpc_id            = "${aws_vpc.this.id}"
      cidr_block        = "10.1.2.0/24"
      availability_zone = "ap-northeast-1c"
    
      tags {
        Name = "tf-sample-public-c-subnet"
      }
    }
    
    resource "aws_subnet" "public_d" {
      vpc_id            = "${aws_vpc.this.id}"
      cidr_block        = "10.1.3.0/24"
      availability_zone = "ap-northeast-1d"
    
      tags {
        Name = "tf-sample-public-d-subnet"
      }
    }
    
    # SubnetRouteTableAssociation
    resource "aws_route_table_association" "public_a" {
      subnet_id      = "${aws_subnet.public_a.id}"
      route_table_id = "${aws_route_table.public.id}"
    }
    
    resource "aws_route_table_association" "public_c" {
      subnet_id      = "${aws_subnet.public_c.id}"
      route_table_id = "${aws_route_table.public.id}"
    }
    

modules/vpc/output.tfで他のmoduleから参照したい情報をoutput output_name(ラベル) {}の形で定義する。
value変数は、modules/vpc/mian.tfの中の取得したいリソースタイプ.リソース名(ラベル).属性の形で${resource_type.resource_name.attributes}の形式として指定すれば格納される。

  • modules/vpc/output.tf
  • output "vpc_id" {
      value = "${aws_vpc.this.id}"
    }
    
    output "subnet_public_a_id" {
      value = "${aws_subnet.public_a.id}"
    }
    
    output "subnet_public_c_id" {
      value = "${aws_subnet.public_c.id}"
    }
    

今回のケースでは、modules/ec2/で、modules/vpc/main.tfresource "aws_vpc" "this" {}で作成したvpcのidを取得して利用したいので、output.tfで以下のような形式で指定する。

output "vpc_id" {
  value = "${aws_vpc.this.id}"
}

VPCのIDはmodules/vpc/main.tfでは明示的に指定されていないが、下記を参照のこと。

こうしておけば、development/main.tf内のec2モジュールの指定のところで、${module.MODULE_NAME.OUTPUT_NAME} の形、VPCだと${module.module_vpc(development/main.tfのvpcのモジュール名).vpc_id(modules/vpc/output.tfのOutputラベル名)}でを受け取れるようにしておけば、ec2モジュールでvpcのidが利用可能になる。
サブネットに関しても同様。

module "module_ec2" {
  source = "../modules/ec2"

  vpc_id = "${module.module_vpc.vpc_id}"
  subnet_public_a_id = "${module.module_vpc.subnet_public_a_id}"
  subnet_public_c_id = "${module.module_vpc.subnet_public_c_id}"
}
modules/ec2/以下の構成

modules/ec2/main.tfは下記となる。

variable "vpc_id" {}
variable "subnet_public_a_id" {}
variable "subnet_public_c_id" {}

# Security Group
resource "aws_security_group" "this" {
  name   = "tf-example-sg"
  vpc_id = "${var.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"]
  }

  description = "tf-example-sg"
}

# EC2
resource "aws_instance" "this_t2" {
  ami                     = "ami-2a69be4c"
  instance_type           = "t2.micro"
  disable_api_termination = false
  key_name                = "aws-key-pair"
  vpc_security_group_ids  = ["${aws_security_group.this.id}"]
  subnet_id               = "${var.subnet_public_a_id}"

  tags {
    Name = "tf-example-ec2"
  }
}

resource "aws_eip" "this" {
  instance = "${aws_instance.this_t2.id}"
  vpc      = true
}

modules/ec2/main.tfでVPCの情報が必要な場合は、development/main.tfで指定した変数(今回で言えば、vpc_id, subnet_public_a_id, subnet_public_c_id の値)を用いる。

  vpc_id = "${module.module_vpc.vpc_id}"
  subnet_public_a_id = "${module.module_vpc.subnet_public_a_id}"
  subnet_public_c_id = "${module.module_vpc.subnet_public_c_id}"

これでOK。

実行

下記の形で実行してみる。

$ cd ~/terraform/development

$ terraform init -var-file=config.tfvars

$ terraform plan -var-file=config.tfvars

$ terraform apply -var-file=config.tfvars

削除したい場合は以下を実行する。

$ cd ~/terraform/development

$ terraform destroy

まとめ

TerraformのModule機能を試した。

.tfファイルを分割でき、環境ごとなどでもある程度ファイルを統一化できそうなところは良さそう。
しかし、変数宣言する部分が増えたこと、それにより複雑化するときの管理コストの増加が懸念ではある。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です