It’s really easy to to set up a website on AWS’s s3 service, but if you want to use a certificate (you do), it’s just a bit more work.

With Ansible as my hammer, everything is a nail. Here’s a quick playbook I used to set up my site jxn.is to use cloudfront.

This playbook creates the bucket, sets up an s3 website in the bucket, creates a cloudfront distribution with reasonable parameters, enables an existing Certificate Manager certificate on the site, and points DNS records at the cloudfront distribution.

Before you do this, you need the following set up: 1. Ansible installed locally 2. AWS credentials with a profile (or environment vars, and remove the ‘profile: ‘ properties from the playbook 3. Your Domain and Nameservers set to a zone in Route53 4. A certificate with an ARN in AWS Certificate Manager. This is free and easy to verify if you’re already using Route53.

-
  hosts: localhost
  gather_facts: false
  vars:
    aws_profile: YOUR_AWS_PROFILE
    aws_region: us-west-2
    site_domains:
    - jxn.is
    - www.jxn.is
    domain_zone: jxn.is
    domain_zone_id: YOUR_DOMAIN_ZONE_ID_HERE
    bucket: jxnis
    cloudfront_error_responses:
    - error_code: 404
      response_page_path: /404.html
      response_code: 404
      error_caching_min_ttl: 30
    cloudfront_cert:
      acm_certificate_arn: YOUR_CERT_ARN_HERE
      certificate_source: acm
      minimum_protocol_version: TLSv1.2_2018
      ssl_support_method: sni-only

  tasks:
  - name: create site bucket
    s3_bucket:
      name: "{{ bucket }}"
      profile: "{{ aws_profile }}"
      region: "{{ aws_region }}"
      state: present
  - name: create s3 website from bucket
    s3_website:
      name: "{{ bucket }}"
      profile: "{{ aws_profile }}"
      region: "{{ aws_region }}"
      state: present
      suffix: index.html
    register: s3_website_result
  - name: create cloudfront distribution
    cloudfront_distribution:
      aliases: "{{ site_domains }}"
      comment: Static website for {{ domain_zone }}.  This distribution is managed by ansible.
      custom_error_responses: "{{ cloudfront_error_responses }}"
      default_root_object: index.html
      ipv6_enabled: true
      # NOTE: use the "website" s3 url, not the bare bucket url
      origins: ['domain_name': "{{ bucket }}.s3-website-{{ aws_region }}.amazonaws.com"]
      price_class: PriceClass_100
      profile: "{{ aws_profile }}"
      purge_aliases: true
      region: "{{ aws_region }}"
      state: present
      default_cache_behavior: # options for cloudfront cache lifetimes
        min_ttl: 30
        default_ttl: 900
        max_ttl: 1500
      viewer_certificate: "{{ cloudfront_cert }}"
    register: cloudfront_result
  - name: create r53 records pointing to cloudfront distribution
    route53:
      alias: true
      alias_hosted_zone_id: "{{ domain_zone_id }}"
      profile: "{{ aws_profile }}"
      state: present
      ttl: 300
      type: A
      record: "{{ item }}"
      value: "{{ cloudfront_result.domain_name }}"
      zone: "{{ domain_zone }}"
      overwrite: true
    with_items: "{{ site_domains }}"

You’ll definitely need to change the following parameters for your needs: aws_profile, site_domains, domain_zone, bucket, cloudfront_cert.acm_certificate_arn, domain_zone_id. You’ll probably also want to change the region and some caching values.

Then, don’t forget to copy your site’s static html files into your new bucket.