概要
前回以下の記事でApexを使ってLambdaを環境別に管理する方法をまとめましたが、
Lambda with Apex: 環境変数で環境別にLambda環境を整える - Qiita
今回はApexでTerraformを使ってLambdaを管理する方法を試してみたいと思います。
バージョン等
- OS: Mac OS X 10.11.1
- Terraform: v0.6.16
- Apex: 0.9.0
- 言語: Go
事前設定
Terraform インストール
Terraformがまだない場合はインストールします。
Download Terraform - Terraform by HashiCorp
Apexのインストール
Apexがまだない場合はインストールします。
curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh
AWS設定
AWSアカウントの用意
今回は環境ごとにAWSアカウントを分けてみたいので、prod用とdev用にAWSアカウントを用意しました。
アカウントを用意したら ~/.aws/credentials
に記述しておきます。
# dev用
[default]
region=ap-northeast-1
aws_access_key_id=XXXXXXXXXXX
aws_secret_access_key=XXXXXXXXXXX
# prod用
[prod]
region=ap-northeast-1
aws_access_key_id=XXXXXXXXXXX
aws_secret_access_key=XXXXXXXXXXX
dev用のprofileををdefaultにしておきました。
S3バケットの用意
Terraformの状態ファイルを保存するためのS3バケットを用意しておきます。今回はapex-terraform-example
という名前にします。
プロジェクト作成
まずディレクトリを作成します。
mkdir apex_terraform
apexコマンドでプロジェクトを作成します。
cd apex_terraform
apex init
対話コマンドで以下のように入力します。
Project name: apex_terraform
Project description:
Would you like to manage infrastructure with Terraform? (yes/no): yes
Environments: dev, prod
Would you like to store Terraform state on S3? (yes/no): yes
S3 bucket name: apex-terraform-example
Terraformを使うかどうかの質問でyes
を選択します。
また、Terraformの状態ファイルをS3に配置するか聞かれるので、yesにしておきます。yesにするとS3のBucket名を聞かれるので指定します。
完了すると以下のようなファイル構成でプロジェクトが作成されます。
├── functions
│ └── hello
│ └── index.js
├── infrastructure
│ ├── dev
│ │ └── main.tf
│ ├── modules
│ │ └── iam
│ │ ├── iam.tf
│ │ └── outputs.tf
│ └── prod
│ └── main.tf
└── project.json
infrastructureの下にdev/prodと環境別にフォルダが作成され、terraformの設定ファイルが配置されています。また、infrastructure/modulesの下にIAMロール作成用のモジュールが配置されています。各環境のmain.tf
ファイルでは以下のように記述されており、このモジュールがIAMロール作成に使用されていることが確認できます。
module "iam" {
source = "../modules/iam"
}
output "lambda_function_role_id" {
value = "${module.iam.lambda_function_role_id}"
}
インフラ構築 (dev)
環境ごとにTerraformを実行してみます。まずplanで実行計画を確認してみます。
$ apex infra plan
Refreshing Terraform state prior to plan...
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
+ module.iam.aws_iam_role.lambda_function
arn: "" => "<computed>"
assume_role_policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"lambda.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n"
name: "" => "$ apex infra plan
Refreshing Terraform state prior to plan...
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
+ module.iam.aws_iam_role.lambda_function
arn: "" => "<computed>"
assume_role_policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"lambda.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n"
name: "" => "apex_lambda_function"
path: "" => "/"
unique_id: "" => "<computed>"
+ module.iam.aws_iam_role_policy.cloudwatchlogs_full_access
name: "" => "cloudwatchlogs_full_access"
policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"logs:*\"\n ],\n \"Effect\": \"Allow\",\n \"Resource\": \"*\"\n }\n ]\n}\n"
role: "" => "${aws_iam_role.lambda_function.id}"
Plan: 2 to add, 0 to change, 0 to destroy."
path: "" => "/"
unique_id: "" => "<computed>"
+ module.iam.aws_iam_role_policy.cloudwatchlogs_full_access
name: "" => "cloudwatchlogs_full_access"
policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"logs:*\"\n ],\n \"Effect\": \"Allow\",\n \"Resource\": \"*\"\n }\n ]\n}\n"
role: "" => "${aws_iam_role.lambda_function.id}"
Plan: 2 to add, 0 to change, 0 to destroy.
CloudWatch Logsへのフルアクセス権限を持つPolicy作成と、apex_lambda_functionという名前のIAM Roleが作成されることがわかります。
確認できたのでTerraformを本実行してみます。
$ apex infra apply
module.iam.aws_iam_role.lambda_function: Creating...
arn: "" => "<computed>"
assume_role_policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"lambda.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n"
name: "" => "apex_lambda_function"
path: "" => "/"
unique_id: "" => "<computed>"
module.iam.aws_iam_role.lambda_function: Creation complete
module.iam.aws_iam_role_policy.cloudwatchlogs_full_access: Creating...
name: "" => "cloudwatchlogs_full_access"
policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"logs:*\"\n ],\n \"Effect\": \"Allow\",\n \"Resource\": \"*\"\n }\n ]\n}\n"
role: "" => "apex_lambda_function"
module.iam.aws_iam_role_policy.cloudwatchlogs_full_access: Creation complete
Apply complete! Resources: 2 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
Outputs:
lambda_function_role_id = arn:aws:iam::588762728270:role/apex_lambda_function
これでIAMロールが作成されました。ただ、現時点ではdev環境のIAM Roleしか作成されていません。
インフラ構築 (prod)
現時点ではdev環境のIAM Roleしか構築されていないため、prod用にも構築を行いたいと思います。
初期状態ではTerraformの設定ファイルはまったく同じものが配置されているため、prod用の設定ファイルを編集して以下を記述します。
variable "aws_region" {
default = "ap-northeast-1"
}
provider "aws" {
profile = "prod"
}
...
そのままだとdevと同じデフォルトのProfile(AWSアカウント)を使用してしまうので、prod
Profileを使うように設定しています。
Terraformの実行ですが、apex infraコマンドでは対象の環境を指定するようなオプションが見当たりませんでした(自分が把握してないだけかも)。。ただ、基本的には通常のTerraformなので、prodディレクトリにいってterraform apply
することにしました。
※2016/06/01変更 - -e
オプションを指定すれば行けました!
$ apex infra -e prod apply
module.iam.aws_iam_role.lambda_function: Creating...
arn: "" => "<computed>"
assume_role_policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": \"lambda.amazonaws.com\"\n },\n \"Action\": \"sts:AssumeRole\"\n }\n ]\n}\n"
name: "" => "apex_lambda_function"
path: "" => "/"
unique_id: "" => "<computed>"
module.iam.aws_iam_role.lambda_function: Creation complete
module.iam.aws_iam_role_policy.cloudwatchlogs_full_access: Creating...
name: "" => "cloudwatchlogs_full_access"
policy: "" => "{\n \"Version\": \"2012-10-17\",\n \"Statement\": [\n {\n \"Action\": [\n \"logs:*\"\n ],\n \"Effect\": \"Allow\",\n \"Resource\": \"*\"\n }\n ]\n}\n"
role: "" => "apex_lambda_function"
module.iam.aws_iam_role_policy.cloudwatchlogs_full_access: Creation complete
Apply complete! Resources: 2 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
Outputs:
lambda_function_role_id = arn:aws:iam::109572732475:role/apex_lambda_function
これでprod用のIAM Roleも作成されました!
function作成(環境変数を指定)
続いて環境変数を使ったLambdaのコードを作成してみます。環境別に環境変数を変えて実行結果が変わるようにしてみます。
*.go
package main
import (
"encoding/json"
"os"
"github.com/apex/go-apex"
)
func main() {
apex.HandleFunc(func(event json.RawMessage, ctx *apex.Context) (interface{}, error) {
return os.Getenv("HOGE"), nil
})
}
main()の中では環境変数のHOGEに指定されている値を返しています。
※Goを使ったコードのサンプルはapex/_examples/go at master · apex/apexで確認できます。
環境変数はdevとprodで切り替えたいため、envファイルをそれぞれ用意します。
.env-dev
HOGE=FUGA
.env-prod
HOGE=PIYO
dev環境にデプロイして実行してみます。環境変数は--set
オプションで指定します。
$ apex deploy hello --set $(cat .env-dev)
• config unchanged function=hello
• code unchanged function=hello
$ apex invoke hello
"FUGA"
環境変数の指定が反映されていることがわかります。
次にprod環境にデプロイしてみます。
$ apex deploy hello -e prod --profile prod --set $(cat .env-prod)
• config unchanged function=hello
• updating function function=hello
• updated alias current function=hello version=2
• function updated function=hello name=apex_terraform_hello version=2
$ apex invoke hello -e prod --profile prod
"PIYO"
こちらもprod用に環境変数が指定されていることが確認できました!