Terraformのリファクタリング楽しいですね。ついついいろんなハードコードを変数化するのに熱中してしまいます。
AWSをTerraformで構築・運用するときAWSのアカウントIDが必要になることがあります。
特にVPC Peering、Private Link, SNS, S3のBucket Policyなど、クロスアカウントで利用できるリソースなどは別アカウントのIDが欲しくなる事があります。
一つならまだしも2つ以上アカウントIDと連携したりすると混乱するので、ここにハードコードしたIDではなくわかりやすい変数名、しかもリモートステートで別アカウントのIDが参照できれば良いなと思いました。
自アカウントであれば、aws_caller_identityを利用すれば簡単に取得することができます。
1
| data "aws_caller_identity" "self" { }
|
どこかに上記のような記述をすると、下記のような形でアカウントIDを参照できます。
1
| "${data.aws_caller_identity.self.account_id}"
|
これにリモートステートの仕組みをプラスすることで、別アカウントのIDを変数化できました。
参照される側のアカウントをA, 参照する側のアカウントをBとしています。
参照される側のアカウントの設定
まずは参照されるアカウントAの設定です。
別アカウントBに変数を公開するにはoutputとして登録する必要があります。
今回は変数名をaccount_idとして、AWSのアカウントIDを公開しています。
1
2
3
4
5
| data "aws_caller_identity" "self" {}
output "account_id" {
value = "${data.aws_caller_identity.self.account_id}"
}
|
また、S3のバケットポリシーを編集して、TerraformのStateをアカウントBが閲覧できるように設定をする必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ExampleStatement1",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::[アカウントBのID]:root",
]
},
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::[アカウントAのStateが入っているBucket]",
"arn:aws:s3:::[アカウントAのStateが入っているBucket]"/*"
]
}
]
}
|
参照される側のアカウントの設定
参照する側のアカウントBでは参照するアカウントAのStateを参照する設定を追加します。
1
2
3
4
5
6
7
8
9
| data "terraform_remote_state" "account_a" {
backend = "s3"
config {
bucket = "[アカウントAのStateが入っているBucket]"
key = "state"
region = "ap-northeast-1"
}
}
|
例えばアカウントAとBのVPC Peeringの設定とかで利用できます。
1
2
3
4
5
6
|
resource "aws_vpc_peering_connection" "acount_a_b" {
peer_owner_id = "${data.terraform_remote_state.account_a.account_id}"
peer_vpc_id = "[アカウントAのVPC ID]"
vpc_id = "[アカウントBのVPC ID]"
}
|
アカウントAのVPC IDもリモートステートで取得する手もありますが、今回は割愛しました。
まとめ
Terraformを書いていていろんなアカウントと連携する設定を書くときに、アカウントID部分が数字の羅列だと混乱するので変数化してみました。
クロスアカウントの機能は充実していますし機能拡張もされ続けています。
何かと使い所はあるのではないでしょうか。
独自のAMIを作ってたら、どうもうまく行かず。Try and Errorの様相を呈してきた。
何度もSnapshot作ってAMIを作ってLaunchってやり始めたので、Terraformでこの辺をやるようにした。
ただ、EBSボリュームからSnapshotを作る部分は見つけられなかったので、Snapshotを作るところは手動です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| resource "aws_ami" "origin-base" {
name = "origin-base-20161124"
virtualization_type = "hvm"
root_device_name = "/dev/sda1"
ebs_block_device {
device_name = "/dev/sda1"
snapshot_id = "snap-60559eee"
volume_size = 10
}
}
resource "aws_instance" "gside-origin" {
ami = "${aws_ami.origin-base.id}"
availability_zone = "${aws_subnet.main.availability_zone}"
ebs_optimized = false
associate_public_ip_address = false
instance_type = "t2.nano"
monitoring = false
key_name = "${aws_key_pair.gside-key.key_name}"
vpc_security_group_ids = ["${aws_security_group.basic.id}"]
associate_public_ip_address = true
disable_api_termination = "true"
source_dest_check = "false"
subnet_id = "${aws_subnet.main.id}"
root_block_device {
volume_type = "gp2"
volume_size = 10
delete_on_termination = true
}
tags {
"Name" = "gside-origin"
}
}
|
とあるインスタンスに追加でEBS VolumeをAttacheしたかったのですが、
現在のTerraform(Ver 0.7.9)では新規にインスタンスを作成し直さないとできないようです。
残念。
ebs_block_deviceの箇所で追加のEBSについて記載しています。
サイズとマウントポイントを記載するだけ。
簡単なんだけどやはりインスタンス作りなおしなのは惜しいですね。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| resource "aws_instance" "gside" {
ami = "ami-0c11b26d"
availability_zone = "${aws_subnet.main.availability_zone}"
ebs_optimized = false
associate_public_ip_address = false
instance_type = "t2.nano"
monitoring = false
key_name = "${aws_key_pair.gside-key.key_name}"
vpc_security_group_ids = ["${aws_security_group.basic.id}"]
associate_public_ip_address = true
private_ip = "10.0.1.10"
disable_api_termination = "true"
source_dest_check = "false"
subnet_id = "${aws_subnet.main.id}"
root_block_device {
volume_type = "gp2"
volume_size = 10
delete_on_termination = true
}
ebs_block_device {
device_name = "/dev/xvdb"
volume_size = 10
}
tags {
"Name" = "gside"
}
}
|
インスタンスにログインしてディスクの状況を確認します。
1
2
3
4
5
| [ec2-user@ip-10-0-1-10 ~]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 10G 0 disk
└─xvda1 202:1 0 10G 0 part /
xvdb 202:16 0 10G 0 disk
|
AMIを作る時に必要なSSHのキーですが、
Terraformでは鍵のインポートのみサポートしています。
確かに秘密鍵をダウンロードするより、公開鍵をアップロードするほうが健全ですね。
鍵の作成
手元のPCで秘密鍵と公開鍵のペアを作成します。
1
2
3
4
5
6
7
8
| $ ssh-keygen -t rsa -b 2048
Generating public/private rsa key pair.
Enter file in which to save the key
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in
Your public key has been saved in
The key fingerprint is:
|
リソースにaws_key_pairを使って、公開鍵をアップロードします。
key_nameの参照には${aws_key_pair.gside-key.key_name}を使います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| resource "aws_key_pair" "gside-key" {
key_name = "gside-key"
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCvGSftV0pe4Pu4AA6CIwZ5QwUnVmO1YZ6LnkUuY1oti0OBuwNhvKE2gJ7eELwUmXLixq5OsccItAeUyIstp8u86AJqaO4DeZBE6gHwaBlrKKG+0b0jFsNCtfFu/jFsmnTuED5I/MpggUk0NKV4BFveqX9Wi7fxaOt5XEsx4XR9mJD+RrtrVAuzSoSK3y3jJgLKpBku1TqcaPZutE6fHIE6OalPRY0JCrN9WzQmFWXL+whTe9KaPMKs6PdHeFG+KBpnY9VjQxxc+lPmMfcID1t/xAuYpi5TZQbtB+YH5Qrn4uz+v1FyL9N/GYmcX8dVz9d9HMgXDUgzgEZU3JpWd6uf gside"
}
resource "aws_instance" "gside" {
ami = "ami-0c11b26d"
availability_zone = "${aws_subnet.main.availability_zone}"
ebs_optimized = false
associate_public_ip_address = false
instance_type = "t2.nano"
monitoring = false
key_name = "${aws_key_pair.gside-key.key_name}"
vpc_security_group_ids = ["${aws_security_group.basic.id}"]
associate_public_ip_address = true
private_ip = "10.0.1.10"
disable_api_termination = "true"
source_dest_check = "false"
subnet_id = "${aws_subnet.main.id}"
root_block_device {
volume_type = "gp2"
volume_size = 10
delete_on_termination = true
}
tags {
"Name" = "gside"
}
}
|
まとめ
これで秘密鍵をダウンロードする気持ち悪さからもおさらばです。
先日入門したTerraform、Route53のURL監視も入れてみました。
ポイントとしては、Route53のCloudWatchアラームはN.Virginiaのリージョンで作る必要がある点です。
それ以外のリージョンは現在はサポートされていません。
Route53のヘルスチェックを作成する
Route53のヘルスチェックを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
| resource "aws_route53_health_check" "gside" {
fqdn = "gside.org"
port = 80
type = "HTTP"
resource_path = "/blowg/b"
failure_threshold = "3"
request_interval = "30"
tags = {
Name = "gside"
}
}
|
Cloudwatchアラームを作成する
前述したように、N.VirginiaのリージョンにClouwdWatchアラームを作成します。
dimensionsには先程作成したRoute53のヘルスチェックのIDを指定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| provider "aws" {
region = "us-east-1"
alias = "virginia"
}
resource "aws_cloudwatch_metric_alarm" "gside-healthcheck" {
provider = "aws.virginia"
alarm_name = "gside-healthcheck"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "HealthCheckStatus"
namespace = "AWS/Route53"
period = "60"
statistic = "Minimum"
threshold = "1"
alarm_description = "This metric monitor gside url healthcheck"
dimensions {
HealthCheckId="${aws_route53_health_check.gside.id}"
}
alarm_actions = ["arn:aws:sns:xxxxxxx"]
}
|
まとめ
ClouwdWatchアラームをN.Virgnia以外で作成して、なかなかRoute53ヘルスチェックと関連づかずハマりましたが、
それ以外は問題なく作成できる内容でした。
ちなみにアラート時のEmail送信用SNSを作るところもTerraform化しようとしましたが、
Emailは送信者認証が入るところがTerraformのモデルに合わず未サポートだそうです。