diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4d16d8c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,17 @@
+.git
+.gitignore
+.editorconfig
+
+# Compiled files
+**/.terraform/*
+**/.terragrunt-cache/*
+*.tfstate
+*.tfstate.*
+
+# Module directory
+.terraform
+**/.idea
+**/*.iml
+
+**/.build-harness
+**/build-harness
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..bed3c96
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,14 @@
+# Use this file to define individuals or teams that are responsible for code in a repository.
+# Read more:
+#
+# Order is important: the last matching pattern takes the most precedence
+
+# These owners will be the default owners for everything
+* @cloudposse/engineering @cloudposse/contributors
+
+# Cloud Posse must review any changes to Makefiles
+**/Makefile @cloudposse/engineering
+**/Makefile.* @cloudposse/engineering
+
+# Cloud Posse must review any changes to GitHub actions
+.github/* @cloudposse/engineering
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..f3df96b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: 'bug'
+assignees: ''
+
+---
+
+Found a bug? Maybe our [Slack Community](https://slack.cloudposse.com) can help.
+
+[![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com)
+
+## Describe the Bug
+A clear and concise description of what the bug is.
+
+## Expected Behavior
+A clear and concise description of what you expected to happen.
+
+## Steps to Reproduce
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Run '....'
+3. Enter '....'
+4. See error
+
+## Screenshots
+If applicable, add screenshots or logs to help explain your problem.
+
+## Environment (please complete the following information):
+
+Anything that will help us triage the bug will help. Here are some ideas:
+ - OS: [e.g. Linux, OSX, WSL, etc]
+ - Version [e.g. 10.15]
+
+## Additional Context
+Add any other context about the problem here.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..76ae6d6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,18 @@
+blank_issues_enabled: false
+
+contact_links:
+
+ - name: Community Slack Team
+ url: https://cloudposse.com/slack/
+ about: |-
+ Please ask and answer questions here.
+
+ - name: Office Hours
+ url: https://cloudposse.com/office-hours/
+ about: |-
+ Join us every Wednesday for FREE Office Hours (lunch & learn).
+
+ - name: DevOps Accelerator Program
+ url: https://cloudposse.com/accelerate/
+ about: |-
+ Own your infrastructure in record time. We build it. You drive it.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..39a8686
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,36 @@
+---
+name: Feature Request
+about: Suggest an idea for this project
+title: ''
+labels: 'feature request'
+assignees: ''
+
+---
+
+Have a question? Please checkout our [Slack Community](https://slack.cloudposse.com) or visit our [Slack Archive](https://archive.sweetops.com/).
+
+[![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com)
+
+## Describe the Feature
+
+A clear and concise description of what the bug is.
+
+## Expected Behavior
+
+A clear and concise description of what you expected to happen.
+
+## Use Case
+
+Is your feature request related to a problem/challenge you are trying to solve? Please provide some additional context of why this feature or capability will be valuable.
+
+## Describe Ideal Solution
+
+A clear and concise description of what you want to happen. If you don't know, that's okay.
+
+## Alternatives Considered
+
+Explain what alternative solutions or features you've considered.
+
+## Additional Context
+
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 0000000..e69de29
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..4b8f32d
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,13 @@
+## what
+* Describe high-level what changed as a result of these commits (i.e. in plain-english, what do these changes mean?)
+* Use bullet points to be concise and to the point.
+
+## why
+* Provide the justifications for the changes (e.g. business case).
+* Describe why these changes were made (e.g. why do these commits fix the problem?)
+* Use bullet points to be concise and to the point.
+
+## references
+* Link to any supporting github issues or helpful documentation to add some context (e.g. stackoverflow).
+* Use `closes #123`, if this PR closes a GitHub issue `#123`
+
diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml
new file mode 100644
index 0000000..ed9361f
--- /dev/null
+++ b/.github/workflows/build-and-push.yml
@@ -0,0 +1,23 @@
+name: docker
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ types: [opened, synchronize, reopened]
+
+jobs:
+ update:
+ runs-on: ubuntu-latest
+ steps:
+ - name: checkout
+ uses: actions/checkout@v1
+ - name: docker/build-and-push
+ uses: docker/build-push-action@v1
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_PASSWORD }}
+ repository: ${{ github.repository }}
+ registry: registry-1.docker.io
+ tag_with_ref: true
+ tag_with_sha: true
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..aeb83d5
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,49 @@
+FROM cloudposse/geodesic:0.132.1
+
+# Geodesic message of the Day
+ENV MOTD_URL="https://geodesic.sh/motd"
+
+# Some configuration options for Geodesic
+ENV AWS_SAML2AWS_ENABLED=true
+ENV AWS_VAULT_ENABLED=false
+ENV GEODESIC_TERRAFORM_WORKSPACE_PROMPT_ENABLED=true
+ENV DIRENV_ENABLED=false
+
+ENV DOCKER_IMAGE="cloudposse/reference-architectures"
+ENV DOCKER_TAG="latest"
+ENV NAMESPACE="eg"
+
+# Geodesic banner message
+ENV BANNER="sweet ops"
+
+# Pin kubectl to version 1.15
+RUN apk add kubectl-1.15@cloudposse
+
+# Install terraform
+RUN apk add terraform@cloudposse
+
+# Install helmfile
+RUN apk add helmfile@cloudposse
+
+# Install saml2aws
+# https://github.com/Versent/saml2aws#linux
+RUN apk add saml2aws@cloudposse
+
+# Install assume-role
+RUN apk add assume-role@cloudposse
+
+# Install variant2 overwriting variant
+RUN apk add variant2@cloudposse
+
+# Install the "docker" command to interact with the host's Docker daemon
+RUN apk add -u docker-cli
+
+# Limit Makefile searches set up by Geodesic
+# Allow a single Makefile to serve all child directories
+ENV MAKE_INCLUDES="Makefile.settings ../Makefile.parent Makefile"
+
+COPY rootfs/ /
+
+COPY projects/ /projects/
+
+WORKDIR /projects/
diff --git a/Makefile b/Makefile
index ee22cf7..786b0cb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,38 @@
-# Import the cloudposse/build-harness
-include $(shell curl -sSL -o .build-harness "https://git.io/build-harness"; echo .build-harness)
--include tasks/Makefile.*
+export DOCKER_ORG ?= cloudposse
+export DOCKER_IMAGE ?= $(DOCKER_ORG)/reference-architectures
+export DOCKER_TAG ?= latest
+export DOCKER_IMAGE_NAME ?= $(DOCKER_IMAGE):$(DOCKER_TAG)
+export APP_NAME ?= geodesic.sh
+GEODESIC_INSTALL_PATH ?= /usr/local/bin
+export INSTALL_PATH ?= $(GEODESIC_INSTALL_PATH)
+export SCRIPT = $(INSTALL_PATH)/$(APP_NAME)
# The target called when calling `make` with no arguments
export DEFAULT_HELP_TARGET = help/short
+# Import the cloudposse/build-harness
+-include $(shell curl -sSL -o .build-harness "https://git.io/build-harness"; echo .build-harness)
+
+## Initialize build-harness, install deps, build docker container, install wrapper script and run shell
+all: init deps build install run
+ @exit 0
+
+## Install dependencies (if any)
+deps:
+ @exit 0
+
+## Build docker image
+build:
+ @make --no-print-directory docker/build
+
+## Push docker image to registry
+push:
+ docker push $(DOCKER_IMAGE)
+
+## Install wrapper script from geodesic container
+install:
+ @docker run --rm $(DOCKER_IMAGE_NAME) | bash -s $(DOCKER_TAG) || (echo "Try: sudo make install"; exit 1)
+
+## Start the geodesic shell by calling wrapper script
+run:
+ $(SCRIPT)
diff --git a/README.md b/README.md
index f051c98..a92642b 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,12 @@ It's 100% Open Source and licensed under the [APACHE2](LICENSE).
+## Screenshots
+
+
+![demo](docs/screenshot.png)
+*Example of using the `geodesic` shell as a build a docker image built from the `cloudposse/reference-architectures`*
+
## Introduction
diff --git a/README.yaml b/README.yaml
index 4af7cc5..669adf2 100644
--- a/README.yaml
+++ b/README.yaml
@@ -92,6 +92,12 @@ introduction: |-
The master account owns the top-level DNS zone and then delegates NS authority to each member account.
+# Screenshots
+screenshots:
+ - name: "demo"
+ description: "Example of using the `geodesic` shell as a build a docker image built from the `cloudposse/reference-architectures`"
+ url: "docs/screenshot.png"
+
quickstart: |-
### Assumptions
diff --git a/docs/screenshot.png b/docs/screenshot.png
new file mode 100644
index 0000000..e412ca0
Binary files /dev/null and b/docs/screenshot.png differ
diff --git a/projects/Makefile.parent b/projects/Makefile.parent
new file mode 100644
index 0000000..45c0036
--- /dev/null
+++ b/projects/Makefile.parent
@@ -0,0 +1,70 @@
+init: .terraform/terraform.tfstate
+
+workspace/% %.workspace: conf/%.tfvars .terraform/terraform.tfstate
+ @terraform workspace select $* || terraform workspace new $*
+
+plan:
+ @if [[ -n $${WORKSPACE:=$$(cat .terraform/environment 2>/dev/null)} ]]; then \
+ echo terraform plan -var-file conf/$${WORKSPACE}.tfvars -out $${WORKSPACE}.planfile; \
+ terraform plan -var-file conf/$${WORKSPACE}.tfvars -out $${WORKSPACE}.planfile; \
+ else \
+ echo You must first select a workspace with "'make workspace/...'" >&2; exit 4; \
+ fi
+
+apply:
+ @if [[ -n $${WORKSPACE:=$$(cat .terraform/environment 2>/dev/null)} ]]; then \
+ if [[ -s $${WORKSPACE}.planfile ]]; then \
+ echo terraform apply $${WORKSPACE}.planfile; \
+ terraform apply $${WORKSPACE}.planfile && rm $${WORKSPACE}.planfile; \
+ else \
+ echo "You must first 'make plan'" >&2; exit 3; \
+ fi; \
+ else \
+ echo You must first select a workspace with "'make workspace/...'" >&2; exit 4; \
+ fi
+
+clean:
+ rm -rf .terraform *.planfile
+
+# refresh the terraform state & outputs
+refresh:
+ @if [[ -n $${WORKSPACE:=$$(cat .terraform/environment 2>/dev/null)} ]]; then \
+ echo terraform refresh -var-file conf/$${WORKSPACE}.tfvars; \
+ terraform refresh -var-file conf/$${WORKSPACE}.tfvars; \
+ else \
+ echo You must first select a workspace with "'make workspace/...'" >&2; exit 4; \
+ fi
+
+.terraform/terraform.tfstate:
+ terraform init
+
+## make .plan will always run "terraform plan" to create a planfile for workspace ""
+## make .planfile will run "terraform plan" to create a planfile for workspace "" if it is
+## out of date with respect to source files
+%.plan %.planfile: conf/%.tfvars *.tf workspace/%
+ terraform plan -var-file conf/$*.tfvars -out $*.planfile
+
+## make .apply will run "terraform apply" using an existing ".planfile" file for workspace ""
+%.apply: workspace/%
+ @if [[ -s $*.planfile ]]; then \
+ echo terraform apply $*.planfile; \
+ terraform apply $*.planfile && rm $*.planfile; \
+ else \
+ echo "You must first 'make $*.planfile'" >&2; exit 3; \
+ fi; \
+
+## make .sync will run "terraform plan" then "terraform apply" for workspace ""
+%.sync: conf/%.tfvars workspace/% %.planfile
+ terraform apply $*.planfile && rm $*.planfile
+
+# refresh the terraform state & outputs
+%.refresh: workspace/%
+ terraform refresh -var-file conf/$*.tfvars
+
+# output the terraform state outputs
+%.output: workspace/%
+ @ terraform output
+
+.PHONY: init plan apply clean sync refresh output
+
+.SECONDARY:
diff --git a/projects/README.md b/projects/README.md
new file mode 100644
index 0000000..1ace8ad
--- /dev/null
+++ b/projects/README.md
@@ -0,0 +1,62 @@
+# Infrastructure Projects
+
+Terraform and Kubernetes projects exist in here.
+
+## Terraform workflow
+
+Terraform operates on a "workspace". We have a named workspace for each AWS account.
+In these examples, we will use "dev" as the workspace we are operating on, but "dev"
+can be replaced with any workspace name for which there is a `conf/$workspace.tfvars` file.
+
+### Manual, multi-step workflow
+
+Remember, replace "dev" with whatever valid workspace name you want.
+
+```bash
+make init # initializes Terraform
+make workspace/dev # selects workspace "dev"
+make plan # make planfile for current workspace
+make apply # apply planfile for current workspace, delete planfile on success
+```
+
+### Automatic, single-step workflow
+
+Remember, replace "dev" with whatever valid workspace name you want.
+
+- Generate Terraform planfile (the output of `terraform plan` and input to `terraform apply`), if needed:
+
+ ```bash
+ make dev.planfile
+ ```
+
+- Optionally/alternatively: (re-)generate Terraform planfile regardless of whether there is a current up-to-date one already:
+
+ ```bash
+ make dev.plan
+ ```
+
+- Apply an existing planfile and delete it on success. Will fail if planfile does not exist:
+
+ ```bash
+ make dev.apply
+ ```
+
+## Cold Start
+
+Initiating the project requires a specific order.
+
+* Initialize the [tfstate-backend](tfstate-backend/README.md)
+* Create the [accounts](account/README.md)
+* Configure [SSO](sso/README.md) with a GSuite Admin
+* Configure [primary IAM roles](iam-primary-roles/README.md)
+* Configure [delegated IAM roles](iam-delegated-roles/)
+* Configure [VPCs](vpc/)
+* Configure [CloudTrail bucket](cloudtrail-bucket/) on `master`
+* Configure [CloudTrail](cloudtrail/) per account
+* Configure [primary DNS zones](dns-primary/)
+* Configure [delegated DNS zones](dns-delegated/)
+* Configure [EKS clusters](eks/)
+* Configure [EFS](efs/)
+* Configure [EKS IAM Roles](eks-iam/)
+* Configure [Helm external-dns](helmfiles/external-dns)
+* Configure [Helm metrics-server](helmfiles/kube-state-metrics)
diff --git a/rootfs/.gitignore b/rootfs/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/rootfs/etc/profile.d/aws-saml2aws.sh b/rootfs/etc/profile.d/aws-saml2aws.sh
new file mode 100644
index 0000000..d25ab71
--- /dev/null
+++ b/rootfs/etc/profile.d/aws-saml2aws.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+if [[ $GEODESIC_TRACE =~ saml ]]; then
+ export _GEODESIC_TRACE_SAML=true
+else
+ unset _GEODESIC_TRACE_SAML
+fi
+
+if [ "${AWS_SAML2AWS_ENABLED}" == "true" ]; then
+ [[ -n $_GEODESIC_TRACE_SAML ]] && echo "trace: Executing aws-saml2aws.sh"
+ if command -v saml2aws >/dev/null; then
+ [[ -n $_GEODESIC_TRACE_SAML ]] && green "trace: saml2aws installed"
+ else
+ [[ -n $_GEODESIC_TRACE_SAML ]] && red "trace: saml2aws not installed"
+ exit 1
+ fi
+
+ ln -sf /localhost/.saml2aws ${HOME}
+fi