47
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

S3 + CloudFront + Aws Certificate Manager + Route53 + Middleman + terraformを使ってHTTPS対応した静的Webを100円/月で構築する

Last updated at Posted at 2016-05-26

概要

タイトルが長い。

個人プロジェクトでSSL対応したWebサイトを作る機会があって、折角なので無料のACM(Aws Certificate Manager)を使ってみました。
そこそこ安く、SSL対応したWebサイトを構築します。

インフラはほぼAWSを使っています。

なぜこれらの技術を採用したか

S3

WebサイトはWordPress等で構築でも良かったのですが、サーバ費用をかけたくなかったため。
middlemanでビルドした静的WebをS3にホストしています。

CloudFront

S3 + ACMの組み合わせだと必須だったため、仕方なく使っています。
わざわざCloudFrontを使うほどのトラフィックではない。
S3で直接ACM使えたらいいのになぁ。

Aws Certificate Manager

Let’s Encryptでも良かったけど、サーバの運用とかしたくなかったため。
というか、そもそもACMを使ってみたいというモチベーションだった。

Route53

何かと相性が良いので。
ドメインはAWS外のサービスで取りました。

Middleman

静的サイトジェネレーターです。
https://middlemanapp.com/jp/

要件的には静的WebでOKかつ自分しか記事更新しないので、middleman-blogを採用しました。
もし非エンジニアの人が記事書くようなプロジェクトだと、middlemanは採用しづらいですね...

terraform

INFRASTRUCTURE AS CODEです。
https://www.terraform.io/

AWSの管理画面でぽちぽちするのがあまり好きでは無いので、なるだけterraformでAWSリソースの構築をしました。

記事時点では v0.6.15 を使っています。

構成

クライアント → CloudFront → S3 ← middleman ← おれ

terraformのtf

こちらを大変参考にさせて頂きました。
クラメソさんの記事いつもお世話になっております:bangbang:

そしてこの記事の一番価値あるのはここだよ:bangbang:

terraform.tf
variable "name" { default = "bucket-name.here" }

resource "aws_cloudfront_origin_access_identity" "sample" {
  comment = "${var.name}"
}
 
resource "template_file" "sample_s3_policy" {
  template = "${file(concat(path.module, "/sample_s3_policy.json.tpl"))}"
 
  vars {
    bucket_name            = "${var.name}"
    origin_access_identity = "${aws_cloudfront_origin_access_identity.sample.id}"
  }
}

resource "aws_s3_bucket" "sample" {
  bucket = "${var.name}"
  acl    = "private"
  policy = "${template_file.sample_s3_policy.rendered}"

  website {
    index_document = "index.html"
    error_document = "404.html"
  }
}

resource "aws_cloudfront_distribution" "sample" {
  enabled             = true
  comment             = "${var.name}"
  default_root_object = "index.html"
  price_class         = "PriceClass_200"
  retain_on_delete    = true
  aliases = ["${var.name}", "www.${var.name}"]
 
  origin {
    domain_name = "${aws_s3_bucket.sample.website_endpoint}"
    origin_id   = "${var.name}"
 
    custom_origin_config {
      http_port = "80"
      https_port = "443"
      origin_protocol_policy = "http-only"
      origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"]
    }
  }
 
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "${aws_s3_bucket.sample.id}"
    compress         = true
 
    forwarded_values {
      query_string = false
 
      cookies {
        forward = "none"
      }
    }
 
    viewer_protocol_policy = "redirect-to-https"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }
 
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
 
  viewer_certificate {
    acm_certificate_arn = "arn:aws:acm:us-east-1:xxxxxxxxxxxx:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    ssl_support_method = "sni-only"
    minimum_protocol_version = "TLSv1"
  }
}

resource "aws_route53_zone" "sample" {
  name = "${var.name}"
}

resource "aws_route53_record" "sample" {
  zone_id = "${aws_route53_zone.sample.zone_id}"
  name    = "${var.name}"
  type    = "A"

  alias {
    name    = "${aws_cloudfront_distribution.sample.domain_name}"
    zone_id = "Z2FDTNDATAQYW2"
    evaluate_target_health = false
  }
}

resource "aws_route53_record" "www-sample" {
  zone_id = "${aws_route53_zone.sample.zone_id}"
  name    = "www.${var.name}"
  type    = "A"

  alias {
    name    = "${aws_cloudfront_distribution.sample.domain_name}"
    zone_id = "Z2FDTNDATAQYW2"
    evaluate_target_health = false
  }
}
sample_s3_policy.json.tpl
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadForGetBucketObjects",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${origin_access_identity}"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::${bucket_name}/*"
    }
  ]
}

クラメソさんの記事をベースに作っていますが、いくつか変更点があります。

  • CloudFrontのindexDocumentの設定
    • bucket-name.here/foo/index.htmlに対して、bucket-name.here/foo だけでアクセスできるようにしたかった
    • そのためcustom_origin_configを設定している
  • ACMを利用するためにCloudFrontのviewer_certificateの設定をしている

terraform管理外のリソース

できるだけterraformで完結したかったのですが、ACMに関してはまだterraformから扱えないようでしたので、管理画面からぽちぽちつくりました。

今回はwwwあり/なしそれぞれのドメインをあてているので、certificateもこの2つを含むものを作成しました。

また、ACM設定の際にメールの受信設定等が必要です。
メール受信するためにSESも使うのですが、同じくterraformから管理できないので管理画面からぽちぽちしました。
こちらを参考にさせて頂きました。

無事にACMの設定が完了するとarnがわかるので、その値をterraformのacm_certificate_arnにセットします。

費用

月に100円〜200円程度でしょうか。

月額(ざっくり)
ドメイン 10 〜 100円
Route53 60円
S3 10円
CloudFront 20円

まとめ

middlemanの話は無くても良かったですね...
(werckerの話も最初書いたけど関係ないから消した)

S3 + CloudFront + ACM を terraform で実現したい場合の参考になれば幸いです。

47
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
47
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?