亚马逊AWS官方博客

为 AWS Service Catalog 服务部署 Terraform Reference Engine

从 2023 年 4 月 3 日开始,除了在 AWS Service Catalog 中使用 AWS CloudFormation,您还可以使用 Hashicorp Terraform 来定义基础设施即代码(IaC)资源。这将允许您选择更符合您的企业流程和专业知识的工具。Terraform 是一个可以支持多种云环境创建 IaC 资源的工具。在使用 Service Catalog 时,可以选择创建 Terraform 模板的 Service Catalog 产品,然后将多个产品整合到 Service Catalog 产品组合中。随后这些产品组合可以分享至不同业务部门和分支机构进行快速部署。在此之前,在 AWS 环境中部署 Terraform 是一项较为复杂的工作,通常需要自行搭建许多 Terraform 环境和发布工具。关于此功能的发布,您可以参考 https://aws.amazon.com/cn/about-aws/whats-new/2023/04/aws-service-catalog-terraform-open-source/

要在 AWS Service Catalog 使用 Terraform 产品需要事先部署 Terraform Reference Engine:https://github.com/aws-samples/service-catalog-engine-for-terraform-os/。该项目由 AWS 在 GitHub 上发布。关于 Terraform Reference Engine 的问题和更新,请使用该链接。

AWS Service Catalog 为企业提供一个自助式的 IT 服务目录平台。该服务通常用于创建和管理企业已获准在 AWS 上使用的 IaC 模板。这些 IaC 模板包括从网络、计算、存储、数据库资源到完整的企业应用的所有内容。企业 IT 管理者可以使用 Service Catalog 根据组织架构和成本预算对这些可部署的 IaC 模板进行精细的权限控制。关于 Service Catalog 的基础使用,请参考 https://docs.aws.amazon.com/servicecatalog/latest/adminguide/catalogs.html

如何创建 Service Catalog 产品

Service Catalog 支持 CloudFormation 和 Terraform 两种产品类型,产品类型需要在创建产品时指定。创建 Terraform open source 产品类型不需要任何前置条件,但 Terraform 产品的部署操作将依赖于 Terraform Reference Engine:

关于创建产品和部署产品的操作和示例,请参考:https://aws.amazon.com/cn/blogs/china/new-self-service-provisioning-of-terraform-open-source-configurations-with-aws-service-catalog/

Terraform Reference Engine 项目架构

在开始之前,建议您先将该 GitHub 项目(https://github.com/aws-samples/service-catalog-engine-for-terraform-os/)下载到一台用于部署的 EC2 实例上。该项目是一个无服务器应用,通过 AWS SAM 进行部署。

您可以观察到,在项目的根目录下有一个名为 template.yaml 的 CloudFormation 模板。该模板中包含了在 Terraform Reference Engine 所包含的 VPC 环境、部署于 EC2 之上的 Terraform CLI 环境、用于存储 Terraform 状态信息的 S3 存储桶、用于与 Terraform 环境交互的 Lambda 函数、AWS Step Functions 状态机、SQS 队列等资源、一些预置的 IAM 角色等等。

该项目包含了名为 ServiceCatalogTerraformOSParameterParser 的 Lambda 函数,用于获取 Terraform 文件并读取模板中的参数,并使用 terraform-config-inspect 工具对 Terraform 配置进行验证。其余 Python 代码用于与 Step Function 结合完成 Service Catalog 产品部署、发送通知等操作。以部署新产品为例,该项目的主要代码逻辑如下:

  1. 用户发起一个 Terraform 产品的部署请求
  2. 该请求将进入 ServiceCatalogTerraformOSProvisionOperationQueue
  3. Lambda 函数 TerraformEngineProvisioningHandlerLambda 会从队列中消费此请求,触发负责部署流程的 Step Functions 状态机
  4. 状态机将触发 Lambda 函数 SelectWorkerHostFunction 试图从 Terraform Auto-Scaling 组中返回一台可用的实例
  5. 如果上一步成功,状态机将继续触发 Lambda 函数 SendApplyCommandFunction 生成 terraform_runner 命令,并在上一步返回的实例中通过 SSM 运行命令。terraform 实例将 assume 部署账户中的 Launch Role
  6. Terraform 实例执行资源部署
  7. 状态机将等待部署结果并执行 Lambda 函数 PollCommandInvocationFunction、GetStateFileOutputsFunction 获取结果,随后执行 Lambda 函数 NotifyProvisionResult 通知 Service Catalog 进行后续操作
  8. Service Catalog 服务 assume 部署账户中的 Launch Role
  9. Service Catalog 将新创建的资源加入到新的 resource group
  10. Resource group 将负责对新创建的资源附加标签

部署 Terraform Reference Engine

Terraform Reference Engine 提供了一个安装脚本,可以用于在 Mac 或 Linux 实例上运行自动安装。您可以直接运行./bin/bash/deploy-tre.sh -r ${AWS_REGION}完成操作。安装脚本将在您的 AWS 账户中部署 Terraform Reference Engine 所需的 Lambda 函数以及 Terraform 实例。Terraform Reference Engine 有以下安装环境要求:

如果选择手动安装,请参考以下安装步骤:

配置环境变量

Terraform Reference Engine 账户需要在每一个 region 分别进行部署。在安装开始前建议配置好 AWS_ACCOUNT_ID 和 AWS_REGION 环境变量。

export AWS_ACCOUNT_ID=<您的 AWS ACCOUNT ID>
export AWS_REGION=<您的 AWS REGION>

创建 bootstrap 存储桶

Bootstrap 存储桶用于保存部署 Terraform Reference Engine 过程中所需的中间结果。Terraform Reference Engine 开源项目中包含了一个名为 cfn-templates/Bootstrap.yaml 的 CloudFormation 模板,其中定义了一个名为 terraform-engine-bootstrap-${AWS::AccountId}-${AWS::Region}的存储桶。运行以下 CLI 命令即可部署:

aws cloudformation create-stack --stack-name Bootstrap-TRE --template-body file://cfn-templates/Bootstrap.yaml --capabilities CAPABILITY_NAMED_IAM

上传 Wrapper Script

通过以下命令可以快速创建一个 Python 3.9 环境

conda create -n python3.9 -y python=3.9
conda activate python3.9
pip --version

接下来将这些打包好的 Python 脚本上传到之前创建的 bootstrap 存储桶。每一个新的 Terraform 实例启动时都会从存储桶中下载 terraform_runner 模块进行安装。

cd service-catalog-engine-for-terraform-os/wrapper-scripts
pip3 install wheel
python3 setup.py bdist_wheel
aws s3 sync dist s3://terraform-engine-bootstrap-$AWS_ACCOUNT_ID-$AWS_REGION/dist

修改默认 CloudFormation 模板

在将 SAM 项目打包部署之前需要事先调整项目的 CloudFormation 模板。该模板默认在 EC2 实例上安装 1.2.8 版本的 Terraform CLI,并将为 Terraform Reference Engine 创建新的 VPC。

使用自定义模板可能会需要使用自定义 CloudFormation 参数,在运行 sam deploy 命令时可以使用–parameter-overrides 选项提供这些参数。常见的自定义部署选项可能包括:

  • 修改 Terraform CLI 版本。默认模板将使用 1.2.8 版本进行部署,您可以直接修改 TerraformCliVersion 参数来部署不同的版本;
  • 修改 Terraform 实例大小。需要注意的是,有些 Region 不支持默认的 t2.micro 实例类型。也可以修改 AutoScaling 组的参数,默认为 MaxSize: 3,MinSize: 1,DesiredCapacity: 1;
  • 将 Terraform Reference Engine 部署在一个已经创建好的现有 VPC 中,而不是创建一个新的 VPC。CloudFormation 将需要传递 VPC、PublicRouteTable、PrivateSubnets、DefaultSecurityGroup 四个参数来决定 Terraform Reference Engine 的部署。

除了一些自定义的修改之外,通常还需要在 template 文件中修改默认 Launch Role 名称。Service Catalog 通常需要在 Launch Constraint 中限定一个 Launch Role 来进行产品的发布、更新和终止。Terraform Reference Engine 中的 IAM 策略赋权默认 Launch Role 的命名规则为“SCLaunch*”,您将需要将其修改为真实 Launch Role 的名称。如果是第一次使用 Service Catalog,请确保在创建 Launch Role 的步骤中,其名称符合之前 CloudFormation 模板中定义的命名规范。关于如何为 Service Catalog 配置 Launch Role,请参考:https://docs.aws.amazon.com/servicecatalog/latest/adminguide/constraints-launch.html#constraints-launch-role

部署 Terraform Reference Engine

在进行此步之前应当确保安装了 AWS SAM CLI。通过以下命令可以进行快速安装:

wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
sudo ./sam-installation/install
sam --version

Terraform 使用 HashiCorp Configuration Language(HCL)来定义其 IaC 资源。和 CloudFormation 语法类似,HCL 提供了一种配置语言来描述云端资源的架构。HCL 是用 Go 语言编写的,可以在 Go 程序中使用 HCL 读取配置文件、检查配置文件语法等。运行以下命令编译 lambda-functions/terraform_open_source_parameter_parser 路径下的 Go 代码:

cd service-catalog-engine-for-terraform-os/lambda-functions/terraform_open_source_parameter_parser
go mod init terraform_open_source_parameter_parser
go env -w GOPROXY=direct
go mod tidy

接下来运行以下命令将 Python Lambda 函数的依赖库安装到目标路径:

cd service-catalog-engine-for-terraform-os
pip3 install -r lambda-functions/state_machine_lambdas/requirements.txt -t lambda-functions/state_machine_lambdas --upgrade

运行 sam 命令打包和部署:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 sam build
sam deploy --s3-bucket terraform-engine-bootstrap-$AWS_ACCOUNT_ID-$AWS_REGION --stack-name TRE --capabilities CAPABILITY_NAMED_IAM --region $AWS_REGION

在多个 Region 部署

您可以对 Terraform Reference Engine 同时在多个 region 部署,这通常只需要修改变量并重复执行一些命令:

export AWS_REGION=<YOUR REGION OF CHOICE>

aws cloudformation create-stack --stack-name Bootstrap-TRE --template-body file://cfn-templates/Bootstrap.yaml --capabilities CAPABILITY_NAMED_IAM

aws s3 sync wrapper-scripts/dist s3://terraform-engine-bootstrap-$AWS_ACCOUNT_ID-$AWS_REGION/dist

sam deploy --s3-bucket terraform-engine-bootstrap-$AWS_ACCOUNT_ID-$AWS_REGION --stack-name TRE --capabilities CAPABILITY_NAMED_IAM --region $AWS_REGION

关于 Terraform 版本更新

如果您更新了 template 文件,重新运行 sam build 和 sam deploy 命令即可对 Terraform Reference Engine 进行更新。AWS CloudFormation 会对现有的 Stack 创建 ChangeSet 执行更新操作。

如果您只需要在环境中更新实例的 Terraform CLI 版本,Terraform Reference Engine 项目在bin/bash/路径下预置了一个名为 replace-ec2-instances.py 的脚本。该脚本可以在更新 CloudFormation Stack 中的版本参数后,安全地将旧版本实例替换为新的版本。

验证 Terraform Reference Engine 的部署

为简单验证部署是否成功,可以尝试部署以下示例应用(请根据区域修改 AMI ID):

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

resource "aws_instance" "app_server" {
  ami = "ami-0f9816f78187c68fb"
  instance_type = "t2.micro"

  tags = {
    Name = "AmazonLinux2023"
  }
}


部署成功后可以看到 Terraform 产品变为可用状态:

常见问题

Service Catalog 提到的 Terraform Open Source 产品指的是什么?

Terraform Open Source 是 Service Catalog 支持的一种产品类型。用户在创建 Service Catalog 产品的时候需要指定这种产品类型,并上传包含 HCL 配置文件的.tar.gz 文件。

Terraform Reference Engine 如何对产品配置的有效性进行验证?

Service Catalog 不会在创建 Terraform Open Source 产品的时候进行验证。只有在用户调用 DescribeProvisioningParameters API 的时候,Service Catalog 才会调用 Lambda 函数获取配置文件中的参数并进行验证。

Terraform Reference Engine 支持哪种类型的配置文件?

Service Catalog 中所有的 Terraform Open Source 产品只接受.tar.gz 格式的上传。其中所有的配置文件必须以.tf 结尾。Terraform Reference Engine 目前不支持.tf.json 格式的配置文件。

Terraform Reference Engine 如何处理 override?

在执行 Terraform CLI 命令前,Terraform Reference Engine 会自动添加一些 override 文件,这包括对 region 和 provider 的 override。建议您不要在产品配置中额外添加 override,否则可能不会生效。

为何我无法获取 S3 中存储的 Terraform 状态文件?

状态文件的加载权限仅限于 GetStateFile Lambda 函数和 Terraform EC2 实例。Service Catalog 管理员只有 list 权限,没有读写权限。

本篇作者

柯逸楠

AWS 解决方案架构师,具有丰富的数据分析和挖掘经验,负责基于 AWS 云平台的解决方案咨询和设计。