All Articles

AWS S3で静的ウェブサイトを公開する際のterraform

構成

aws_s3_website

準備

  • code commit にコードを入れて code build が実行される環境を作っておく
  • code build 実行ユーザーにはterraformで様々な編集ができるように強権限を付与する
  • route 53 でドメインを取得しておく

コード

フォルダ構成

$ tree .
.
├── buildspec.yml
├── module
│   └── s3_website
│       ├── cloud_front.tf
│       ├── route53.tf
│       ├── s3.tf
│       └── variables.tf
├── terraform.tf
└── s3_website.tf

設定

buildspec.yml

version: 0.2
 
env:
  variables:
    AWS_REGION: "ap-northeast-1"
    TERRAFORM_VERSION: 0.11.9
    TERRAFORM_WORKSPACE: prd
phases:
  install:
    commands:
      - echo pre install
      - wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
      - unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip
      - mv terraform /usr/bin
  pre_build:
    commands:
      - echo pre build command.
      - terraform version
      - terraform init
#      - terraform workspace new ${TERRAFORM_WORKSPACE}
  build:
    commands:
      - echo run build command.
      - terraform workspace select ${TERRAFORM_WORKSPACE}
      - terraform plan -parallelism=50 -no-color
  post_build:
    commands:
      - echo post build command.
      - terraform workspace select ${TERRAFORM_WORKSPACE}
      - terraform apply -auto-approve -parallelism=50 -no-color

terraform.tf

terraform {
  required_version = ">= 0.11.0"
 
  backend "s3" {
    bucket = "STATE_FILE_S3_BUCKET"
    key    = "STATE_FILE_NAME.tfstate"
    region = "ap-northeast-1"
  }
}
 
provider "aws" {
  region = "ap-northeast-1"
}
 
# ACMをssl証明書として使う場合us-east-1で作成する必要があるため、
# us-east-1用のaws providerを定義しておく
provider "aws" {
  # us-east-1 instance
  region = "us-east-1"
  alias = "ue1"
}

s3_website.tf

module "s3-website_dancer" {
  source = "./module/s3_website"
  prj_name = "YOUR_PROJECT_NAME"
}

モジュール内

cloud_front.tf

resource "aws_cloudfront_distribution" "s3_website" {
  enabled = true
  aliases = ["${var.prj_name}.example.com"]
  comment = "${terraform.workspace}-${var.prj_name}"
  default_root_object = "index.html"
  price_class = "PriceClass_All"
 
  origin {
    domain_name = "${aws_s3_bucket.s3_website_html_bucket.bucket_domain_name}"
    # target_origin_idと合わせる必要があります
    origin_id = "S3-${aws_s3_bucket.s3_website_html_bucket.bucket}"
 
    s3_origin_config {
      origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
    }
  }
 
  default_cache_comhavior {
    allowed_methods = [
      "GET",
      "HEAD"]
    cached_methods = [
      "GET",
      "HEAD"] 
    # origin_idと合わせる必要があります
    target_origin_id = "S3-${aws_s3_bucket.s3_website_html_bucket.bucket}"
 
    forwarded_values {
      query_string = true
      cookies {
        forward = "none"
      }
    }
 
    viewer_protocol_policy = "https-only"
    min_ttl = 0
    default_ttl = 3600
    max_ttl = 86400
  }
 
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
  
  viewer_certificate {
    acm_certificate_arn = "${aws_acm_certificate.example_com.arn}"
    ssl_support_method = "sni-only"
    minimum_protocol_version = "TLSv1"
  }
}
  
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
  comment = "YOUR_COMMENT"
}

route53.tf

# もともとroute 53でドメインを持っている場合を想定しています
# (持っていない場合はドメインの入手から始める必要があります)
data "aws_route53_zone" "example_com" {
  zone_id = "${var.route53_zone_id}"
}
 
resource "aws_route53_record" "example_com" {
  zone_id = "${data.aws_route53_zone.example_com.zone_id}"
  name = "${var.prj_name}.example.com"
  type = "CNAME"
  ttl = "300"
  records = [
    "${aws_cloudfront_distribution.s3_website.domain_name}"]
}
 
resource "aws_route53_record" "cert_validation" {
  zone_id = "${data.aws_route53_zone.example_com.zone_id}"
  name = "${aws_acm_certificate.example_com.domain_validation_options.0.resource_record_name}"
  type = "${aws_acm_certificate.example_com.domain_validation_options.0.resource_record_type}"
  ttl = 60
  allow_overwrite = true
  records = [
    "${aws_acm_certificate.example_com.domain_validation_options.0.resource_record_value}"]
}
 
resource "aws_acm_certificate" "example_com" {
  # cloud frontから使えるようus-east-1に作成します(us-east-1のawsプロバイダー使用)
  provider = "aws.ue1"
  domain_name = "${var.prj_name}.example.com"
  subject_alternative_names = []
  validation_method = "DNS"
}
 
resource "aws_acm_certificate_validation" "example_com_validation" {
  provider = "aws.ue1"
  certificate_arn = "${aws_acm_certificate.example_com.arn}"
  validation_record_fqdns = [
    "${aws_route53_record.cert_validation.0.fqdn}"]
}

s3.tf

resource "aws_s3_bucket" "s3_website_html_bucket" {
  bucket = "s3-website-${var.prj_name}"
  acl = "private"
}

variable.tf

variable "prj_name" {}
variable "route53_zone_id" {
  default = "YOUR_ROUTE53_ZONE_ID"
}

注意点

  • ACMはcloud frontから使用できるよう us-east-1 に作成する必要があります
  • ACMの検証に時間がかかるため、初回のapplyは失敗します(以下ACMコンソールメッセージ一部抜粋)

    成功
    DNS レコードは Route 53 ホストゾーンに書き込まれました。
    変更が反映され、AWS がドメインを検証して証明書を発行するまでに最大で 30 分以上かかる場合があります。