Edited at

さらに一歩楽しむterraform. moduleでIAM UserとPolicy管理を簡素化しよう

More than 3 years have passed since last update.

俺です。

世界70億のterraform職人の皆様こんばんは。teraform職人の夜は遅いですね。

さてmodule使ってますか?

最近といってもつい数時間前から今まで自分が作ったterraform tfファイルのリファクタリングを始めました。

※同じ組織の方はGHEにpushしてるorenoシリーズリポジトリみてね(業務連絡)

今回はIAM Userとpolicyをmodule化して、

楽々使いまわせるようにする例を載せちゃいます。


参考というかこたえ

terraformのcommunity-moduleリポジトリがあるのでズバリこれで。

https://github.com/terraform-community-modules

さてやってみましょー


moduleの作成

moduleとなるterraform tfファイルを作ります

例として以下の様なディレクトリ/ファイル構成にしておきます。

1tfにまとめてもいいんじゃねって思うのですが

まあーresourceはながーくなることもあるし、community-modulesがこんな感じになってるので

この辺は準拠しておいたほうがよさそうな感じですかねー。

各プロジェクトでルール作ってしまえば良いと思いますね。


moduleのファイル用途

ファイル名
用途

variables.tf
変数定義

iam_***.tf
resource定義

outputs.tf
戻り値定義


moduleのディレクトリ/ファイル構造

orenomac$ tree module/iam

module/iam
├── group
│   ├── iam_group.tf
│   ├── outputs.tf
│   └── variables.tf
├── policy
│   ├── aws_iam_policy.tf
│   ├── outputs.tf
│   └── variables.tf
├── role
└── user
├── aws_iam_user.tf
├── outputs.tf
└── variables.tf


IAM Group用モジュールを作る


  • variables.tf

module resourceの引数に指定するvariableを定義します。

variable "aws_iam_groupname" {

description = "iam group name"
}

variable "aws_iam_grouppath" {
description = "iam group path(/users/)"
default = "/"
}


  • iam_group.tf

moduleで動作するresourceを定義します

resource "aws_iam_group" "iam-group" {

name = "${var.aws_iam_groupname}"
path = "${var.aws_iam_grouppath}"
}


  • outputs.tf

module外から参照されるresourceでmodule内で定義された変数を参照するための戻り値を定義します。

outputを定義しておかないと、moduleで作られたresource内variableを、外部resourceで参照することができません。

output "name" {

value = "${aws_iam_group.iam-group.name}"
}


IAM User用モジュールを作る


  • variables.tf

variable "aws_iam_username" {

description = "iam user name"
}
variable "aws_iam_grouppath" {
description = "iam group path(/users/)"
default = "/"
}


  • aws_iam_user.tf

resource "aws_iam_user" "iam-user" {

name = "${var.aws_iam_username}"
path = "${var.aws_iam_grouppath}"
}


  • outputs.tf

output "name" {

value = "${aws_iam_user.iam-user.name}"
}


IAM Policy用モジュールを作る


  • variables.tf

variable "aws_iam_policy_name" {

description = "Policy Name"
default = "iam-policy"
}

variable "aws_iam_policy_path" {
description = "iam policy path"
default = "/policy/"
}

variable "aws_iam_policy_description" {
description = "iam policy description"
default = "iam policy"
}
variable "aws_iampolicy_json" {
description = "iampolicy json filename"
default = "iam_policy.json"
}


  • aws_iam_policy.tf

policyにはjsonファイルをそのまま放り込めるように組み込み関数fileを使います。

resource "aws_iam_policy" "iam-policy" {

name = "${var.aws_iam_policy_name}"
path = "${var.aws_iam_policy_path}"
description = "${var.aws_iam_policy_description}"
policy = <<EOF
${file("${var.aws_iampolicy_json}")}
EOF
}


  • outputs.tf

arnを返すようにします。

output "name" {

value = "${aws_iam_policy.iam-policy.name}"
}

output "arn" {
value = "${aws_iam_policy.iam-policy.arn}"
}


moduleを利用するtfの作成

こんなかんじです

orenomac$ tree examples/

examples/
├── datawanwan_policy.json
└── test_terraform.tf


  • test_terraform.tf

メインとなるtfではmoduleに変数指定して呼び出すだけです。

sourceには使用するmoduleが格納されているディレクトリまでのパスを記述します。

本例ではディレクトリパスを指定してますが、github/bitbucketなどのパスを指定することもできます。

同僚の皆様GHEにpushしとくので使って下さいww(業務連絡

provider "aws" {

region = "ap-northeast-1"
}

module "iam-group" {
source = "../module/iam/group"
aws_iam_groupname = "oreore"
aws_iam_grouppath = "/oreore/"
}

module "iam-user" {
source = "../module/iam/user"
aws_iam_username = "iam-user"
aws_iam_grouppath = "/oreore/users/"
}

module "iam-datawanwan" {
source = "../module/iam/user"
aws_iam_username = "iam-datawanwan"
aws_iam_grouppath = "/oreore/users/"
}

module "iam-datawanwan_policy" {
source = "../module/iam/policy/"
aws_iam_policy_name = "datawanwan"
aws_iam_policy_path = "/policy/"
aws_iam_policy_description = "datawanwan policy"
aws_iampolicy_json = "datawanwan_policy.json"
}

/* moduleで定義されたresourceのvariablesを外部resourceから参照する例 */

resource "aws_iam_policy_attachment" "datawanwan" {
name = "datawanwan"
users = ["${module.iam-datawanwan.name}"]
policy_arn = "${module.iam-datawanwan_policy.arn}"
}


  • datawanwan_policy.json

datadogのドキュメントに書いてる通りのポリシーを準備しておきます。

module

{

"Version": "2012-10-17",
"Statement": [
{
"Action": [
"autoscaling:Describe*",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResources",
"cloudformation:GetTemplate",
"cloudfront:Get*",
"cloudfront:List*",
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:DescribeTable",
"dynamodb:ListTables",
"ec2:Describe*",
"elasticache:Describe*",
"elasticbeanstalk:Check*",
"elasticbeanstalk:Describe*",
"elasticbeanstalk:List*",
"elasticbeanstalk:RequestEnvironmentInfo",
"elasticbeanstalk:RetrieveEnvironmentInfo",
"elasticloadbalancing:Describe*",
"iam:List*",
"iam:Get*",
"route53:Get*",
"route53:List*",
"rds:Describe*",
"rds:ListTagsForResource",
"s3:List*",
"sdb:GetAttributes",
"sdb:List*",
"sdb:Select*",
"ses:Get*",
"ses:List*",
"sns:Get*",
"sns:List*",
"sqs:GetQueueAttributes",
"sqs:ListQueues",
"sqs:ReceiveMessage"
],
"Effect": "Allow",
"Resource": "*"
}
]
}


いざterraform plan/apply..のまえに

terraform getでmoduleを取得します

orenomac$ cd <path>/<to>/examples

orenomac$ terraform get
Get: file:///orenoterraform/module/iam/group
Get: file:///orenoterraform/module/iam/user
Get: file:///orenoterraform/module/iam/user
Get: file:///orenoterraform/module/iam/policy


plan

planを実行します

orenomac$ terraform 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.

+ aws_iam_policy_attachment.datawanwan
name: "" => "datawanwan"
policy_arn: "" => "${module.iam-datawanwan_policy.arn}"
users.#: "" => "1"
users.XXXXXXXXXXX: "" => "iam-datawanwan"

+ module.iam-datawanwan_policy.aws_iam_policy.iam-policy
arn: "" => "<computed>"
description: "" => "datawanwan policy"
name: "" => "datawanwan"
..省略..


apply

apply叩く前にACCESS_KEYとSECRET_KEYは設定しておきましょうー。

direnvとか環境変数のexportで。

orenomac$ terraform apply

module.iam-user.aws_iam_user.iam-user: Creating...
arn: "" => "<computed>"
name: "" => "iam-user"
path: "" => "/oreore/users/"
unique_id: "" => "<computed>"
module.iam-datawanwan_policy.aws_iam_policy.iam-policy: Creating...
arn: "" => "<computed>"
..省略..

orenomac$ terraform show

aws_iam_policy_attachment.datawanwan:
id = datawanwan
groups.# = 0
name = datawanwan
policy_arn = arn:aws:iam::XXXXXXXXXXX:policy/policy/datawanwan
roles.# = 0
users.# = 1
users.XXXXXXXXXXXX = iam-datawanwan

ちなみに

moduleのoutputは、terraform outputで参照することはできません。

orenomac$ terraform output

The state file has no outputs defined. Define an output
in your configuration with the `output` directive and re-run
`terraform apply` for it to become available.

みんなterraform module使って楽しましょう的~