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

首先或首先是

image.png

我计划首先处理以下内容:
– 与VPC相关的部分(VPC、子网、互联网网关、路由表、安全组)
– API 网关
对于其他组件,我打算逐步添加文章进行补充发布(因为会变得很长)。

在前期的配置中,我们已经准备了Public Subnet和Private Subnet作为子网的类型,但是由于前提配置只使用了Public Subnet,所以我们只需要进行Public Subnet的设置。Private Subnet仅在进行部分验证时使用。(详细信息请参考以下文章)

 

基础架构的自动化配置

基本的步骤

首先,确认适用于所有组件的基本流程。

    1. 在Terraform的官方文档中查看资源类型和各个参数。(每个资源页面的底部都有一个Import部分,可以在那里找到执行terraform import的命令。)

 

    1. 在管理资源的.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をコンソールから確認して入力]
image.png

接下来,使用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
image.png

到目前为止,将现有架构转化为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活动中可能会多次参考的地方。

 

广告
将在 10 秒后关闭
bannerAds