使用Terraform将现有的AWS架构转化为基础设施即代码(VPC、API Gateway)
首先或首先是

我计划首先处理以下内容:
– 与VPC相关的部分(VPC、子网、互联网网关、路由表、安全组)
– API 网关
对于其他组件,我打算逐步添加文章进行补充发布(因为会变得很长)。
在前期的配置中,我们已经准备了Public Subnet和Private Subnet作为子网的类型,但是由于前提配置只使用了Public Subnet,所以我们只需要进行Public Subnet的设置。Private Subnet仅在进行部分验证时使用。(详细信息请参考以下文章)
基础架构的自动化配置
基本的步骤
首先,确认适用于所有组件的基本流程。
-
- 在Terraform的官方文档中查看资源类型和各个参数。(每个资源页面的底部都有一个Import部分,可以在那里找到执行terraform import的命令。)
-
- 在管理资源的.tf文件(例如:aws_vpc.tf)中准备所需的资源部分。
执行terraform import。
使用terraform state show命令来查看现有配置。
在现有配置中,将所需的参数和设置值记录在.tf文件中。
使用terraform fmt或terraform validate对内容进行整理和确认。
使用terraform plan命令来确认现有配置是否有差异(如需进行添加或更改,则有差异是可以接受的)。
如果发现意外的差异,重新执行第4至7步进行内容确认。如果有预期的差异,则使用terraform apply命令将更改应用到环境中。
VPC相关服务
虚拟私有云 (VPC)
首先需要确认资源的类型和基本流程,因为这是最初的资源。可以从官方文档中查找资源类型等信息。VPC的设置如下所示。
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
}
这次我们将现有环境转化为基础架构代码(IaC),所以资源类型保持不变,将VPC名称改为现有VPC名称,并且不需要写入cidr_block等内容,按照以下示例准备.tf文件。
resource "aws_vpc" "pubsub-vpc" {
}
请确认公式文档中有关使用terraform import的示例如下所示。
使用terraform import命令,通过VPC id导入VPC。比如:
% 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
如果以上都没问题,可以使用terraform plan来检查所述内容与现有配置之间的差异。
terraform plan

到目前为止,将现有架构转化为IaC已经完成。如果现有架构存在配置差异,可以使用terraform apply命令来进行配置的更新。
子网
将来的资源仅保留经过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"
}
}
互联网网关
#----------------------------------------
# Create Internet Gateway
#----------------------------------------
resource "aws_internet_gateway" "pubsub-igw" {
vpc_id = aws_vpc.pubsub-vpc.id
tags = {
Name = "pubsub-igw"
}
}
路由表
#----------------------------------------
# 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
}
安全组
#----------------------------------------
# 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网关
接下来将使用Terraform将API Gateway的配置部分转化为基础架构即代码。
# #----------------------------------------
# # 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中进行了描述。我们希望能够通过Terraform来管理Lambda的实际内容,但对于内容的具体设置,我们将在Lambda的基础设施即代码(IaC)部分进行。当前情况下,我们只限于能够引用现有的代码(存在于控制台上)。
data "aws_lambda_function" "AuthForPubSub" {
function_name = "AuthForPubSub"
}
data "aws_lambda_function" "deliverTopicToSNS_Test01" {
function_name = "deliverTopicToSNS_Test01"
}
一旦完成VPC和API Gateway的基础架构即可。下一步计划是通过Terraform顺利管理现有的Lambda部分。
总结
一旦实施到这一步确实花了相当多的时间。由于将现有配置导入并实现无差异的基础设施即代码(IaC)化需要耗费一定的努力,所以我认为从一开始就以IaC为目标构建是最好的选择。如果在中途开始进行IaC化,应考虑IaC化带来的优势和成本效益等因素进行评估。未来,我希望使用Terraform对Lambda部分和其他现有配置进行IaC化。
参考网站
我会将这个放在今后的Terraform活动中可能会多次参考的地方。