はじめに
本記事では、Terraform
で構築したリソースの情報をAWS SAM テンプレート
に引き渡す方法について解説しています。
VPCやサブネット、セキュリティグループなどのネットワーク関連のリソースはTerraform
で管理し、Lambda関数はAWS SAM
で管理するケースをサンプルコードで説明しています。
本ケースの全体構成
本ケースのサンプルコード
ディレクトリ構成
aws-tf-resource-export-sample
├── main.tf
├── provider.tf
├── src
│ └── lambda_function.py
├── template.yaml
└── tf-resource-export.yml
Terraform側のコード
aws_cloudformation_stack
を使って、リソースの情報(ここでは、SunbetId
とSecurityGroupId
)を、CloudFormation のテンプレートのOutputs セクションに組み込んだStackを構築するようにしています。
#
# リソースの情報をCloudformationのOutputs機能を使ってエクスポートする
# このStackでTerraformのバックエンド用のリソースを作成する
#
resource "aws_cloudformation_stack" "this" {
name = "tf-resource-export-sample"
template_body = templatefile("./tf-resource-export.yml", {
# tfstateのリソース作成用
tfstate_backend = {
s3_bucket_name = "tf-resource-export-sample"
dynamodb_for_state_lock = "tf-resource-export-sample"
},
# サブネット情報のOutputs用
network = {
private_subnet_1a_name = "PrivateSubnet1a"
private_subnet_1a_id = module.network.private_subnets[0]
private_subnet_1c_name = "PrivateSubnet1c"
private_subnet_1c_id = module.network.private_subnets[1]
},
# セキュリティグループ情報のOutputs用
security_group = {
name_for_lambda = "HelloWorldFunctionSG"
id_for_lambda = aws_security_group.this.id
}
})
}
#
# Terraformで構築するリソース
#
*** 以下、省略 ***
CloudFormation のテンプレート
AWSTemplateFormatVersion: "2010-09-09"
Description: "Terraform Resource info Export Template"
Resources:
TfStateBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
BucketName: ${tfstate_backend["s3_bucket_name"]}
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
TfStateLockTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${tfstate_backend["dynamodb_for_state_lock"]}
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
AttributeDefinitions:
- AttributeName: "LockID"
AttributeType: "S"
KeySchema:
- AttributeName: "LockID"
KeyType: "HASH"
#
# Terraform で構築したリソースの情報をこのOutputsセクションに組み込みます。
#
Outputs:
PrivateSubnet1a:
Description: "Id for PrivateSubnet 1a"
Export:
Name: ${network["private_subnet_1a_name"]}
Value: ${network["private_subnet_1a_id"]}
PrivateSubnet1c:
Description: "Id for PrivateSubnet 1c"
Export:
Name: ${network["private_subnet_1c_name"]}
Value: ${network["private_subnet_1c_id"]}
HelloWorldFunctionSG:
Description: "Security Group for HelloWorldFunction"
Export:
Name: ${security_group["name_for_lambda"]}
Value: ${security_group["id_for_lambda"]}
※CloudFormationのテンプレートは、Resources
が必須で、Outputs のみを記載することができないため、このサンプルコードでは、Resources
にTerraformのtfstateを管理するためのバックエンド用リソースを構築するようにしています。
AWS SAM側のコード
Fn::ImportValue
組み込み関数 を使って、Terraform側で構築したStackのOutputsの値をクロススタック参照
するようにAWS SAM Template に組み込みます。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: "Lambda Function SAM Template"
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: HelloWorldFunction
Handler: lambda_function.lambda_handler
Runtime: python3.9
CodeUri: ./src/
Timeout: 10
MemorySize: 128
#
# Fn::ImportValue 組み込み関数で値をインポートします。
#
VpcConfig:
SecurityGroupIds:
- !ImportValue HelloWorldFunctionSG
SubnetIds:
- !ImportValue PrivateSubnet1a
- !ImportValue PrivateSubnet1c
Policies:
- AWSLambdaVPCAccessExecutionRole
- AWSLambdaBasicExecutionRole
Outputs:
HelloWorldFunction:
Description: "HelloWorldFunction ARN"
Value: !GetAtt HelloWorldFunction.Arn
これで、Terraformで構築したリソースの情報をAWS SAMに引き渡すことができるようになります。
本ケースのデプロイ手順
実際に、Terraform
とAWS SAM
を使って、本ケースをデプロイする手順について、記載します。
デプロイ手順確認環境
$ terraform -version
Terraform v1.4.4
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v4.62.0
$ sam --version
SAM CLI, version 1.79.0
Terraformの実行
リソースの構築
Terraform でネットワーク関連のリソースとその情報をOutputs セクションに組み込んだStackを構築します。
$ export AWS_PROFILE= [ YOUR AWS ACCOUNT PROFILE NAME ]
$ terraform init
Initializing the backend...
Successfully configured the backend "local"! Terraform will automatically
$ terraform plan
Plan: 9 to add, 0 to change, 0 to destroy.
$ terraform apply
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
tfstateを管理するバックエンドの変更
下記のようにprovider.tf
内のtfstateのバックエンド先をLocalからS3
に修正します。
#backend "local" {
# path = "terraform.tfstate"
#}
backend "s3" {
bucket = "tf-resource-export-sample"
key = "terraform.tfstate"
dynamodb_table = "tf-resource-export-sample"
region = "ap-northeast-1"
}
下記コマンドを実行して、tfstateを管理するバックエンドをLocalからS3
に変更します。
$ terraform init -migrate-state
Initializing the backend...
Terraform detected that the backend type changed from "local" to "s3".
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "local" backend to the
newly configured "s3" backend. No existing state was found in the newly
configured "s3" backend. Do you want to copy this state to the new "s3"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value: yes
下記コマンドを実行して、tfstateが、S3に保存されていることを確認します。
$ aws s3 ls s3://tf-resource-export-sample
9999-99-99 99:99:99 13870 terraform.tfstate
AWS SAMの実行
Lambda関数の構築
下記コマンドを実行して、AWS SAM TemplateでLambda関数をデプロイします。
sam deploy --template-file template.yaml \
--stack-name tf-resource-import-sample \
--resolve-s3 \
--capabilities CAPABILITY_IAM \
--region ap-northeast-1
CloudFormation stack changeset
---------------------------------------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
---------------------------------------------------------------------------------------------------------------------------------
+ Add HelloWorldFunctionRole AWS::IAM::Role N/A
+ Add HelloWorldFunction AWS::Lambda::Function N/A
---------------------------------------------------------------------------------------------------------------------------------
デプロイできました。
CloudFormation outputs from deployed stack
---------------------------------------------------------------------------------------------------------------------------------
Outputs
---------------------------------------------------------------------------------------------------------------------------------
Key HelloWorldFunction
Description HelloWorldFunction ARN
Value arn:aws:lambda:ap-northeast-1:999999999999:function:HelloWorldFunction
---------------------------------------------------------------------------------------------------------------------------------
Successfully created/updated stack - tf-resource-import-sample in ap-northeast-1
動作確認
下記コマンドで、デプロイしたLambda関数を実行します。
$ aws lambda invoke --function-name HelloWorldFunction /dev/stdout
{"statusCode": 200, "body": "{\"message\": \"Hello World!\"}"}
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
クリーンアップ
Lambda関数の削除
下記コマンドで、AWS SAM Template でデプロイしたLambda関数を削除します。
$ sam delete --stack-name tf-resource-import-sample
Are you sure you want to delete the stack tf-resource-import-sample in the region ap-northeast-1 ? [y/N]: y
Do you want to delete the template file 71b91735b453182a1c33df8bceda4df3.template in S3? [y/N]: y
Deleted successfully
tfstateを管理するバックエンドの変更
下記のようにprovider.tf
内のtfstateのバックエンド先をS3からLocal
に修正します。
backend "local" {
path = "terraform.tfstate"
}
#backend "s3" {
# bucket = "tf-resource-export-sample"
# key = "terraform.tfstate"
# dynamodb_table = "tf-resource-export-sample"
# region = "ap-northeast-1"
#}
下記コマンドを実行して、tfstateを管理するバックエンドをS3からLocal
に変更します。
$ terraform init -migrate-state -lock=false
Initializing the backend...
Terraform detected that the backend type changed from "s3" to "local".
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "s3" backend to the
newly configured "local" backend. No existing state was found in the newly
configured "local" backend. Do you want to copy this state to the new "local"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value: yes
リソースの削除
下記コマンドで、各リソースを削除します。
$ terraform destroy
Destroy complete! Resources: 9 destroyed.
下記コマンドで、tfstate用のS3バケットを削除します。
$ aws s3 rb s3://tf-resource-export-sample --force
delete: s3://tf-resource-export-sample/terraform.tfstate
remove_bucket: tf-resource-export-sample
以上です。
さいごに
こちらは、あくまで1例になります。参考になれば幸いです。