About Code: Should it be infrastructure?

Yes.

Setup Ansible

Here's how I set up Ansible on Ubuntu.

python -m venv ansible_venv
source ansible_venv/bin/activate
pip3 install ansible
ansible-galaxy collection install community.aws
pip3 install boto3

Serve a static website from an S3 Bucket

I created setup.s3site.yml:

---
- name: Set up static site buckets on AWS as static s3 bucket
  hosts: localhost
  connection: local
  gather_facts: no

  tasks:
  - name: Create S3 bucket
    amazon.aws.s3_bucket:
      name: 'www.{{s3bucket}}'
      state: present
      versioning: yes
  - name: Setup S3 Bucket as website
    community.aws.s3_website:
      name: 'www.{{s3bucket}}'
      state: present
      suffix: index.html
      error_key: errors/404.html
  - name: Add pages
    community.aws.s3_sync:
      bucket: 'www.{{s3bucket}}'
      file_root: '../files/{{s3bucket}}'
    tags:
      - debug
  - name: create ACL file
    template:
      dest: /tmp/www.{{s3bucket}}.acl.json
      src: ../files/www.public_s3.j2
  - name: Clear access controls www.
    shell:
      aws s3api put-bucket-policy --bucket 'www.{{s3bucket}}' --policy 'file:///tmp/www.{{s3bucket}}.acl.json'
  - name: Create empty apex redirect bucket
    amazon.aws.s3_bucket:
      name: '{{s3bucket}}'
      state: present
      versioning: no
  - name: Setup redirect from apex
    community.aws.s3_website:
      name: '{{s3bucket}}'
      redirect_all_requests: 'www.{{s3bucket}}'
      state: present
  - name: create apex ACL file
    template:
      dest: /tmp/{{s3bucket}}.acl.json
      src: ../files/public_s3.j2
  - name: Clear access controls apex
    shell:
      aws s3api put-bucket-policy --bucket '{{s3bucket}}' --policy 'file:///tmp/{{s3bucket}}.acl.json'
  - name: Create logs bucket
    amazon.aws.s3_bucket:
      name: 'logs.{{s3bucket}}'
      state: present
      versioning: no
  - name: setup logging
    community.aws.s3_logging:
      name: '{{s3bucket}}'
      state: present
      target_bucket: 'logs.{{s3bucket}}'

This play requires crafting an IAM access policy to allow serving the S3 bucket publicly:

{
"Version": "2012-10-17",
"Statement": [
		{
		"Sid": "PublicReadGetObject",
		"Effect": "Allow",
		"Principal": "*",
		"Action": "s3:GetObject",
				"Resource": "arn:aws:s3:::www.{{s3bucket}}/*"
		}
	]
}

Note: www.public_s3.j2 is a Jinja2 template, which takes s3bucket as a variable from our playbook. We will specify this variable on the command line so that the play can be re-used for future buckets.

Which I run with this command:

ansible-playbook -e "s3bucket=edthe.dev" setup.s3site.yml

Then I did a bunch of non-repetivite stuff manually in the AWS Web Console:

    ns-number.awsdns-number.com.
    ns-number.awsdns-number.org.
    ns-number.awsdns-number.co.uk.
    ns-number.awsdns-number.net.

Tip: Do not remove the certificate ownership DNS records. I have heard it is helpful to still have when the SSL Certificate needs renewed.

    www.edthe.dev	A	Simple	-	1stbunchofletters.cloudfront.net.
    www.edthe.dev	AAAAA	Simple	-	1stbunchofletters.cloudfront.net.
    edthe.dev	A	Simple	-	2ndbunchofletters.cloudfront.net.
    edthe.dev	A	Simple	-	2ndbunchofletters.cloudfront.net.

Now my site exists, but I still need a way to modify my AWS S3 Bucket contents.

When it was time to set this up a second time for the domain I share with my spouse, I decided it would be wise to delegate access to manage AWS Route 53 DNS to my spouse.