はじめに
現在の以下の構成をTerraformでIaC化していきたいと思います。
内容としては
・VPC関連(VPC,Subnet,InternetGateway,RouteTable,SecurityGroup)
・API Gateway
の部分をまずはやっていきたいと思います。その他コンポーネントについては順次記事を追加投稿していきたいと思います。(長くなってしまうので)
※Subnetの種類としてPublic SubnetとPrivate Subnetを用意していますが、前提構成ではPublic Subnetしか利用していないので基本はPublic Subnetの設定を実施していきます。(Private Subnetは一部の検証を実施する際に利用した。詳細は以下記事)
IaC化
基本の流れ
まずは全てのコンポーネントに対して適用される基本の流れを確認。
- Terraformの公式Docでリソースタイプや個別パラメータなどを確認する。(各リソースページの一番下には
Import
セクションが記載されているので、terafform import
する際のコマンドも確認できる。) - リソースを管理する
.tf
ファイル(例:aws_vpc.tf)に必要なリソースセクションを用意する。 -
terraform import
を実行。 -
terraform state show
で既存構成を確認。 - 既存構成のうち、必要なパラメータや設定値を
.tf
ファイルに記載する。 -
terraform fmt
やterraform validate
で内容を整備・確認。 -
terraform plan
で既存構成と差分がないかを確認(追加変更する場合は差分があってOK)。 - もし意図しない差分があったら、再度4~7を実施して内容確認。意図した差分があるなら
terraform apply
で変更を環境に反映する。
VPC関連サービス
VPC
最初のリソースなので基本の流れの内容を順を追って残しておく。
まずは公式Docからリソースタイプなどを確認。VPCは以下のようなブロックとなる。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
今回は既存環境をIaC化するので、リソースタイプはそのままにしてVPC名は既存のVPC名に書き換え、cidr_blockなども記載なしにした.tfファイルを以下のように用意する。
resource "aws_vpc" "pubsub-vpc" {
}
公式Docにはterraform import
する際の例も以下のように記載されているので確認。
Using terraform import, import VPCs using the VPC id. For example:
% terraform import aws_vpc.test_vpc vpc-a01106c2
上記のコマンド例に沿って自分のIaC化実施環境でもコマンドを打つ
terraform import aws_vpc.pubsub-vpc [既存のVPCIDをコンソールから確認して入力]
次にterraform state show
で既存構成を確認する。
terraform state show aws_vpc.pubsub-vpc
出力された構成情報のうち、デフォルト値を除いた必須項目などをtfファイルに記載していく。
#----------------------------------------
# Create VPC
#----------------------------------------
resource "aws_vpc" "pubsub-vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "pubsub-vpc"
}
}
フォーマットの整備や、(念のため)構文エラーなどもないかを確認。
terraform fmt
terraform validate
上記OKであれば、terraform plan
で記載内容と既存構成内容の差分を確認。
terraform plan
ここまでで既存構成のIaC化は完了。既存構成に対して構成差分がある場合はterraform apply
で内容を反映する。
Subnet
以降のリソースはIaC化された後の状態のtfファイルのみ残しておく。VPC関連のサービスは同じファイルに記載して管理する。
#----------------------------------------
# Create Public Subnet
#----------------------------------------
resource "aws_subnet" "pubsub-subnet-public1-ap-northeast-1a" {
vpc_id = aws_vpc.pubsub-vpc.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.0.0/20"
tags = {
Name = "pubsub-subnet-public1-ap-northeast-1a"
}
}
resource "aws_subnet" "pubsub-subnet-public2-ap-northeast-1c" {
vpc_id = aws_vpc.pubsub-vpc.id
availability_zone = "ap-northeast-1c"
cidr_block = "10.0.16.0/20"
tags = {
Name = "pubsub-subnet-public2-ap-northeast-1c"
}
}
#----------------------------------------
# Create Private Subnet
#----------------------------------------
resource "aws_subnet" "pubsub-subnet-private1-ap-northeast-1a" {
vpc_id = aws_vpc.pubsub-vpc.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.128.0/20"
tags = {
Name = "pubsub-subnet-private1-ap-northeast-1a"
}
}
resource "aws_subnet" "pubsub-subnet-private2-ap-northeast-1c" {
vpc_id = aws_vpc.pubsub-vpc.id
availability_zone = "ap-northeast-1c"
cidr_block = "10.0.144.0/20"
tags = {
Name = "pubsub-subnet-private2-ap-northeast-1c"
}
}
Internet Gateway
#----------------------------------------
# Create Internet Gateway
#----------------------------------------
resource "aws_internet_gateway" "pubsub-igw" {
vpc_id = aws_vpc.pubsub-vpc.id
tags = {
Name = "pubsub-igw"
}
}
Route Table
#----------------------------------------
# Create RouteTable
#----------------------------------------
# RouteTable for Public Subnet
resource "aws_route_table" "pubsub-rtb-public" {
vpc_id = aws_vpc.pubsub-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.pubsub-igw.id
}
tags = {
Name = "pubsub-rtb-public"
}
}
# RouteTable for Private Subnet
resource "aws_route_table" "pubsub-rtb-private" {
vpc_id = aws_vpc.pubsub-vpc.id
tags = {
Name = "pubsub-rtb-private"
}
}
#----------------------------------------
# Attach RouteTable to Subnet
#----------------------------------------
resource "aws_route_table_association" "public_rt_assoc" {
subnet_id = aws_subnet.pubsub-subnet-public1-ap-northeast-1a.id
route_table_id = aws_route_table.pubsub-rtb-public.id
}
resource "aws_route_table_association" "public_rt_assoc2" {
subnet_id = aws_subnet.pubsub-subnet-public2-ap-northeast-1c.id
route_table_id = aws_route_table.pubsub-rtb-public.id
}
resource "aws_route_table_association" "private1_rt_assoc" {
subnet_id = aws_subnet.pubsub-subnet-private1-ap-northeast-1a.id
route_table_id = aws_route_table.pubsub-rtb-private.id
}
resource "aws_route_table_association" "private2_rt_assoc" {
subnet_id = aws_subnet.pubsub-subnet-private2-ap-northeast-1c.id
route_table_id = aws_route_table.pubsub-rtb-private.id
}
Security Group
#----------------------------------------
# Create Security Group
#----------------------------------------
resource "aws_security_group" "pubsub-public-sg" {
name = "pubsub-public-sg"
vpc_id = aws_vpc.pubsub-vpc.id
description = "public security group"
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
self = true
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "pubsub-public-sg"
}
}
API Gateway
次にAPI Gatewayの構成部分をTerraformでIaC化していく。
# #----------------------------------------
# # Create API Gateway
# #----------------------------------------
resource "aws_api_gateway_rest_api" "pubsubApi" {
name = "pubsubApi"
description = "Api to call publisher lambda"
endpoint_configuration {
types = ["REGIONAL"]
}
}
resource "aws_api_gateway_resource" "pubsubApi" {
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
parent_id = aws_api_gateway_rest_api.pubsubApi.root_resource_id
path_part = "publishtopic"
}
resource "aws_api_gateway_authorizer" "pubsubApi" {
name = "PubSubLambdaAuthorizer"
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
authorizer_uri = data.aws_lambda_function.AuthForPubSub.invoke_arn
authorizer_result_ttl_in_seconds = 0
type = "REQUEST"
}
resource "aws_api_gateway_method" "pubsubApi" {
http_method = "POST"
authorization = "CUSTOM"
authorizer_id = aws_api_gateway_authorizer.pubsubApi.id
resource_id = aws_api_gateway_resource.pubsubApi.id
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
}
resource "aws_api_gateway_deployment" "pubsubApi" {
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
}
resource "aws_api_gateway_stage" "pubsubApi" {
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
stage_name = "dev"
deployment_id = aws_api_gateway_deployment.pubsubApi.id
description = "dev stage for pubsub api"
}
resource "aws_api_gateway_integration" "pubsubApi" {
rest_api_id = aws_api_gateway_rest_api.pubsubApi.id
resource_id = aws_api_gateway_resource.pubsubApi.id
http_method = "POST"
type = "AWS_PROXY"
uri = data.aws_lambda_function.deliverTopicToSNS_Test01.invoke_arn
content_handling = "CONVERT_TO_TEXT"
integration_http_method = "POST"
}
上記で参照しているdata.aws_lambda_function.AuthForPubSub.invoke_arn
やdata.aws_lambda_function.deliverTopicToSNS_Test01.invoke_arn
の部分については別ファイルaws_pubsub_lambda.tf
で記述している。実際のLambdaの中身自体も同様にTerraformで管理できるようにしたいが、その内容についてはLambdaのIaC化セクションで行うこととし、現状では既存のコード(コンソール上にある)を参照できるところまでにしておく。
data "aws_lambda_function" "AuthForPubSub" {
function_name = "AuthForPubSub"
}
data "aws_lambda_function" "deliverTopicToSNS_Test01" {
function_name = "deliverTopicToSNS_Test01"
}
一旦ここまででVPC回りとAPI Gateway回りのIaCはできている(はず)。次回は既存のLambdaの部分をTerraformでスムーズに管理できるところを実施していきたい。
おわりに
一旦ここまで実施するのにも割と時間がかかりました。既存の構成をimportして差分が無いようにIaC化するのには労力かかる面もあるので、やはり最初からIaCを念頭に構築できればベストだと思います。途中からIaC化する場合は、IaC化することによるメリットや費用対効果なども考慮しながら検討する必要がありそうです。次回以降はLambda部分やその他の既存構成部分をTerraformでIaC化していきたいと思います。
参考サイト
今後のTerraform活動でも何回か参照しそうなので載せておく。