TerraformでACMの証明書を取得する方法

AWS Certificate Manager (ACM) で証明書を発行する方法は2017年の11月までは
メールアドレスによる認証が必要でした。現在ではDNSを使ったドメイン認証が出来るように
なっています。
Route53でDNSを管理している場合にはより簡単にACMで証明書を発行する
ことが出来るようになしました。
今回はACMの証明書発行リクエストとRoute53への検証レコードをTerraformのみで実現
してみます。

# ACMのリクエストの作成

ACMでの証明書発行リクエストには[aws_acm_certificate](https://www.terraform.io/docs/providers/aws/r/acm_certificate.html)のリソースを
作成します。
validation_method = "DNS"と定義することにより、DNSを使ったドメイン認証が使われます。


```acm.tf
resource "aws_acm_certificate" "example-cert" {
  domain_name = "example.net"
  subject_alternative_names  = ["foo.example.net"]
  validation_method = "DNS"
  tags {
    Environment = "production"
  }
}
```

上記の例のファイルをterraform applyして実行すると[domain_validation_options](https://www.terraform.io/docs/providers/aws/r/acm_certificate.html#domain_validation_options)というattributeにドメイン認証に必要な情報が格納されたstateが出来ます。
terraform consoleで内容を確認すると以下のような情報が得られます。

```
$ terraform console
> aws_acm_certificate.example-cert.domain_validation_options
[
  {
    "domain_name" = "example.net"
    "resource_record_name" = "_22222222222222222222222222222222.example.net."
    "resource_record_type" = "CNAME"
    "resource_record_value" = "_dddddddddddddddddddddddddddddddd.acm-validations.aws."
  },
  {
    "domain_name" = "foo.example.net"
    "resource_record_name" = "_33333333333333333333333333333333.foo.example.net."
    "resource_record_type" = "CNAME"
    "resource_record_value" = "_eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.acm-validations.aws."
  },
]
```
これらの情報をもとにRoute53のレコードを作成できれば、DNSでの検証を行うことが出来ます。

# Route53での検証用レコードの作成
前段落で得た情報をもとにRoute53でレコードを作成してDNSによる検証を行います。
[aws_route53_record](https://www.terraform.io/docs/providers/aws/r/route53_record.html)のリソースを使います。１つ１つレコードをする記述を書くことも出来ますが、
countやlookupを使うことにより、SANがあった場合でも一回の定義で複数のレコードを作成すること出来ます。

```route53.tf
resource "aws_route53_record" "example_acm_public" {
  count = "${length(aws_acm_certificate.example-cert.domain_validation_options)}"
  zone_id = "${var.example_zone_id}"
  name = "${lookup(aws_acm_certificate.example-cert.domain_validation_options[count.index],"resource_record_name")}"
  type = "${lookup(aws_acm_certificate.example-cert.domain_validation_options[count.index],"resource_record_type")}"
  ttl = "300"
  records = ["${lookup(aws_acm_certificate.example-cert.domain_validation_options[count.index],"resource_record_value")}"]
}
```

# まとめ
以上がTerraformでACMの証明書のリクエストとDNS認証を行う方法でした。
発行された証明書は[aws_alb_listener](https://www.terraform.io/docs/providers/aws/r/lb_listener.html)のcertification_arnに以下のように設定することが出来ます。
またメールアドレスで認証を行っていたときのようにデータソースの[aws_acm_certificate](https://www.terraform.io/docs/providers/aws/d/acm_certificate.html)から
arnを取得することも出来ます。


``` alb.tf
...
data "aws_acm_certificate" "example-acm" {
  domain   = "example.net"
}

resource "aws_lb_listener" "front_end" {
  load_balancer_arn = "${aws_lb.front_end.arn}"
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2015-05"
  certificate_arn   = "${aws_acm_certificate.example-cert.arn}"
  #certificate_arn   = "${data.aws_acm_certificate.example-acm.arn}"
  
  default_action {
    target_group_arn = "${aws_lb_target_group.front_end.arn}"
    type             = "forward"
  }
}
...
```

ドメイン認証が行える前はメールアドレスで認証を行う必要があり、terraformで管理することは
出来ませんでしたが、DNS認証を使うことにより、ACMでの証明書発行もterraformのコードで
管理出来るようになったのは大きなメリットだと思います。