この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんにちは、中山です。
TerraformのPRを眺めていたら、個人的に待ち望んでいた aws_cloudformation_stack
データソースと aws_cloudformation_stack
リソースのYAML対応用PRが上がっていたのでご紹介したいと思います。以前TerraformとCloudFromation(以下CFn)を連携させるエントリを書きましたが、その時点ではJSONで書かなければならずちょっと使いにくいという問題がありました。
執筆時点(2017/01/18)ではまだアップストリームにマージされていませんが、今後このPRが取り込まれたらJSON業とさよならできそうですね!
使ってみる
それでは早速使ってみましょう。
インストール
上述したようにこのPRはまだTerraformのアップストリームに取り込まれていません。PR作者の方がフォークしたリポジトリからTerraformのバイナリをコンパイルしましょう。
$ cd $GOPATH/src/github.com/hashicorp/terraform/
$ git remote -v
origin https://github.com/hashicorp/terraform (fetch)
origin https://github.com/hashicorp/terraform (push)
$ git remote add ordinaryexperts/terraform git@github.com:ordinaryexperts/terraform.git
$ git remote -v
ordinaryexperts/terraform git@github.com:ordinaryexperts/terraform.git (fetch)
ordinaryexperts/terraform git@github.com:ordinaryexperts/terraform.git (push)
origin https://github.com/hashicorp/terraform (fetch)
origin https://github.com/hashicorp/terraform (push)
$ git fetch ordinaryexperts/terraform
<snip>
$ git checkout feature/aws-cloudformation-yaml-support
Branch feature/aws-cloudformation-yaml-support set up to track remote branch feature/aws-cloudformation-yaml-support from fork.
Switched to a new branch 'feature/aws-cloudformation-yaml-support'
$ git log -1
commit 5977eaa23633437aa6f9d8e3c63b37ea55b849a0
Author: Dylan Vaughn <dylancvaughn@gmail.com>
Date: Mon Jan 9 13:59:11 2017 -0800
don't normalize YAML templates
$ make dev
<snip>
$ $GOPATH/bin/terraform version
Terraform v0.8.3-dev (5977eaa23633437aa6f9d8e3c63b37ea55b849a0)
Your version of Terraform is out of date! The latest version
is 0.8.4. You can update by downloading from www.terraform.io
コード
コンパイルが完了したら早速使ってみましょう。今回はみんな大好きLambda-Backed Custom Resourceを定義したテンプレートを aws_cloudformation_stack
リソースで作成し、 aws_cloudformation_stack
データソースで出力された値をTerraformで取得してみたいと思います。
main.tf
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_cloudformation_stack" "test" {
name = "test-stack"
template_body = "${file("${path.module}/template.yml")}"
capabilities = ["CAPABILITY_IAM"]
parameters {
Name = "amzn-ami-hvm-*"
}
}
data "aws_cloudformation_stack" "test" {
name = "test-stack"
}
output "ami_id" {
value = "${data.aws_cloudformation_stack.test.outputs["AMIId"]}"
}
CFnのテンプレートはTerraformと分けたいので file
関数で読み込む形にしています。後述しますが、テンプレートでIAM Roleを作成しているため、 CAPABILITY_IAM
を設定しています。また、パラメータとして文字列を指定できるようにしました。 aws_cloudformation_stack
データソースでは先程作成したスタックからアウトプットで出力された内容(AMI Id)を出力しています。もちろん aws_cloudformation_stack
リソースの属性から参照することも可能です。今回はリソース/データソース両方共使いたかったのでこういった形にしています。
template.yml
AWSTemplateFormatVersion : 2010-09-09
Description: Test Template
Parameters:
Name:
Description: Input Name
Type: String
Resources:
LambdaAMIInfoRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: LambdaBasicExecRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: DescribeImagePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:DescribeImages
Resource: "*"
LambdaAMIInfo:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import boto3
import cfnresponse
def handler(event, context):
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
response_data = {}
filters = [
{'Name': 'architecture', 'Values': ['x86_64']},
{'Name': 'root-device-type', 'Values': ['ebs']},
{'Name': 'name', 'Values': [event['ResourceProperties']['Name']]},
{'Name': 'virtualization-type', 'Values': ['hvm']},
{'Name': 'block-device-mapping.volume-type', 'Values': ['gp2']}]
try:
images = boto3.client('ec2').describe_images(Owners=['amazon'], Filters=filters)
except Exception as e:
response_data['Error'] = e
cfnresponse.send(event, context, cfnresponse.FAILED, response_data)
for i in sorted([image for image in images['Images']], key=lambda x: x['Name']):
if i['Name'].lower().count('beta') > 0 or i['Name'].lower().count('.rc') > 0:
continue
response_data['Id'] = i['ImageId']
cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data)
Handler: index.handler
Role: !GetAtt LambdaAMIInfoRole.Arn
Runtime: python2.7
Timeout: 20
CustomAMIInfo:
Type: Custom::AMIInfo
Version: 1.0
Properties:
ServiceToken: !GetAtt LambdaAMIInfo.Arn
Name: !Ref Name
Outputs:
AMIId:
Value: !GetAtt CustomAMIInfo.Id
今回のCFnテンプレートはLambda-Backed Custom Resourceでパラメータで渡された文字列を元にAMI Idを検索するという内容にしました。まぁ、aws_ami データソース使えば同等のことはできるのですが。。。アウトプットで取得したAMI Idを出力して、Terraformからその値を参照可能にしています。
実行
Terraform実行時点ではテスト用スタックが作成されていないので -target
オプションで特定のリソースのみ作成します。
$ $GOPATH/bin/terraform plan -target=aws_cloudformation_stack.test
$ $GOPATH/bin/terraform apply -target=aws_cloudformation_stack.test
aws_cloudformation_stack.test: Creating...
capabilities.#: "" => "1"
capabilities.1328347040: "" => "CAPABILITY_IAM"
name: "" => "test-stack"
outputs.%: "" => "<computed>"
parameters.%: "" => "1"
parameters.Name: "" => "amzn-ami-hvm-*"
policy_body: "" => "<computed>"
<snip>
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.
State path: terraform.tfstate
上記のようにTerraformの作成が完了したら、 -target
オプション抜きで全てのコードを実行します。
$ $GOPATH/bin/terraform plan
$ $GOPATH/bin/terraform apply
aws_cloudformation_stack.test: Refreshing state... (ID: arn:aws:cloudformation:ap-northeast-1:/754159e0-dd53-11e6-8f47-503a369c8836)
data.aws_cloudformation_stack.test: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
ami_id = ami-9f0c67f8
AWS CLIでもスタックの状態を確認してみます。
$ aws cloudformation describe-stacks \
--stack-name test-stack
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:ap-northeast-1:************:stack/test-stack/754159e0-dd53-11e6-8f47-503a369c8836",
"Description": "Test Template",
"Parameters": [
{
"ParameterValue": "amzn-ami-hvm-*",
"ParameterKey": "Name"
}
],
"Tags": [],
"Outputs": [
{
"OutputKey": "AMIId",
"OutputValue": "ami-9f0c67f8"
}
],
"CreationTime": "2017-01-18T07:55:20.076Z",
"Capabilities": [
"CAPABILITY_IAM"
],
"StackName": "test-stack",
"NotificationARNs": [],
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false
}
]
}
正常にスタックが作成されているようです。やりましたね。
まとめ
いかがだったでしょうか。
TerraformからCFnを利用する際にYAMLで記述できるPRをご紹介しました。早くマージしてくれ!!!!1111
本エントリがみなさんの参考になれば幸いに思います。