├── .dockerignore ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── auto-release.yml ├── settings.yml └── workflows │ ├── auto-release.yml │ ├── docker.yml │ └── validate-codeowners.yml ├── .gitignore ├── 01-geodesic ├── README.md └── main.tf ├── 02-atmos ├── README.md ├── atmos.yaml ├── components │ └── terraform │ │ ├── fetch-location │ │ └── main.tf │ │ ├── fetch-weather │ │ └── main.tf │ │ └── output-results │ │ ├── main.tf │ │ └── weather-report.tpl └── stacks │ ├── catalog │ └── example.yaml │ └── workflows │ └── example.yaml ├── 03-first-aws-environment ├── README.md ├── atmos.yaml ├── bin │ └── random-pet.sh ├── components │ └── terraform │ │ ├── static-site │ │ ├── context.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── variables.tf │ │ └── versions.tf │ │ └── tfstate-backend │ │ ├── README.md │ │ ├── context.tf │ │ ├── default.auto.tfvars │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── providers.tf │ │ ├── tfstate-context.tf │ │ ├── variables.tf │ │ └── versions.tf └── stacks │ ├── catalog │ ├── globals.yaml │ └── uw2-globals.yaml │ ├── ue2-root.yaml │ ├── uw2-dev.yaml │ └── uw2-prod.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── docs ├── schema │ └── stack-config-schema.json └── targets.md └── rootfs └── etc └── motd /.dockerignore: -------------------------------------------------------------------------------- 1 | 2 | # Things that could be checked into Git but need not be in the Docker image 3 | .git 4 | .editorconfig 5 | 6 | # Below here should be pretty much a copy of .gitignore 7 | .build-harness 8 | build-harness/ 9 | aws-assumed-role/ 10 | .idea/ 11 | *.iml 12 | 13 | # Compiled and auto-generated files 14 | **/nohup.out 15 | **/.terraform/* 16 | **/*.tfstate 17 | **/*.tfstate.* 18 | **/planfile 19 | **/*.planfile 20 | **/*.kubeconfig 21 | **/terraform.tfvars.json 22 | **/terraform.auto.tfvars.json 23 | **/*.terraform.tfvars.json 24 | **/*.terraform.auto.tfvars.json 25 | **/*.helmfile.vars.yaml 26 | 27 | # Module directory 28 | **/.terraform/ 29 | **/.module/ 30 | **/.helmfile/ 31 | 32 | 33 | # Draft or auto-saved version 34 | # Note that the leading "**/" appears necessary for Docker even if not for Git 35 | **/*.draft.* 36 | **/*.orig 37 | **/*.bak 38 | **/*~ 39 | 40 | # macOS special files and folders 41 | **/.DS_Store 42 | **/.CFUserTextEncoding 43 | **/.Trash/ 44 | **/$RECYCLE.BIN/ -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Use this file to define individuals or teams that are responsible for code in a repository. 2 | # Read more: 3 | # 4 | # Order is important: the last matching pattern has the highest precedence 5 | 6 | # These owners will be the default owners for everything 7 | * @cloudposse/engineering @cloudposse/contributors 8 | 9 | # Cloud Posse must review any changes to Makefiles 10 | **/Makefile @cloudposse/engineering 11 | **/Makefile.* @cloudposse/engineering 12 | 13 | # Cloud Posse must review any changes to GitHub actions 14 | .github/* @cloudposse/engineering 15 | 16 | # Cloud Posse must review any changes to standard context definition, 17 | # but some changes can be rubber-stamped. 18 | **/*.tf @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers 19 | README.yaml @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers 20 | README.md @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers 21 | docs/*.md @cloudposse/engineering @cloudposse/contributors @cloudposse/approvers 22 | 23 | # Cloud Posse Admins must review all changes to CODEOWNERS or the mergify configuration 24 | .github/mergify.yml @cloudposse/admins 25 | .github/CODEOWNERS @cloudposse/admins 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Found a bug? Maybe our [Slack Community](https://slack.cloudposse.com) can help. 11 | 12 | [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) 13 | 14 | ## Describe the Bug 15 | A clear and concise description of what the bug is. 16 | 17 | ## Expected Behavior 18 | A clear and concise description of what you expected to happen. 19 | 20 | ## Steps to Reproduce 21 | Steps to reproduce the behavior: 22 | 1. Go to '...' 23 | 2. Run '....' 24 | 3. Enter '....' 25 | 4. See error 26 | 27 | ## Screenshots 28 | If applicable, add screenshots or logs to help explain your problem. 29 | 30 | ## Environment (please complete the following information): 31 | 32 | Anything that will help us triage the bug will help. Here are some ideas: 33 | - OS: [e.g. Linux, OSX, WSL, etc] 34 | - Version [e.g. 10.15] 35 | 36 | ## Additional Context 37 | Add any other context about the problem here. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | 5 | - name: Community Slack Team 6 | url: https://cloudposse.com/slack/ 7 | about: |- 8 | Please ask and answer questions here. 9 | 10 | - name: Office Hours 11 | url: https://cloudposse.com/office-hours/ 12 | about: |- 13 | Join us every Wednesday for FREE Office Hours (lunch & learn). 14 | 15 | - name: DevOps Accelerator Program 16 | url: https://cloudposse.com/accelerate/ 17 | about: |- 18 | Own your infrastructure in record time. We build it. You drive it. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'feature request' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Have a question? Please checkout our [Slack Community](https://slack.cloudposse.com) or visit our [Slack Archive](https://archive.sweetops.com/). 11 | 12 | [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) 13 | 14 | ## Describe the Feature 15 | 16 | A clear and concise description of what the bug is. 17 | 18 | ## Expected Behavior 19 | 20 | A clear and concise description of what you expected to happen. 21 | 22 | ## Use Case 23 | 24 | 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. 25 | 26 | ## Describe Ideal Solution 27 | 28 | A clear and concise description of what you want to happen. If you don't know, that's okay. 29 | 30 | ## Alternatives Considered 31 | 32 | Explain what alternative solutions or features you've considered. 33 | 34 | ## Additional Context 35 | 36 | Add any other context or screenshots about the feature request here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudposse-archives/tutorials/a18ace97c7c3a19d129de724f9ba7b1bb6e189e8/.github/ISSUE_TEMPLATE/question.md -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## what 2 | * Describe high-level what changed as a result of these commits (i.e. in plain-english, what do these changes mean?) 3 | * Use bullet points to be concise and to the point. 4 | 5 | ## why 6 | * Provide the justifications for the changes (e.g. business case). 7 | * Describe why these changes were made (e.g. why do these commits fix the problem?) 8 | * Use bullet points to be concise and to the point. 9 | 10 | ## references 11 | * Link to any supporting github issues or helpful documentation to add some context (e.g. stackoverflow). 12 | * Use `closes #123`, if this PR closes a GitHub issue `#123` 13 | 14 | -------------------------------------------------------------------------------- /.github/auto-release.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: '$RESOLVED_VERSION' 3 | version-template: '$MAJOR.$MINOR.$PATCH' 4 | version-resolver: 5 | major: 6 | labels: 7 | - 'major' 8 | minor: 9 | labels: 10 | - 'minor' 11 | - 'enhancement' 12 | patch: 13 | labels: 14 | - 'auto-update' 15 | - 'patch' 16 | - 'fix' 17 | - 'bugfix' 18 | - 'bug' 19 | - 'hotfix' 20 | default: 'minor' 21 | 22 | categories: 23 | - title: '🚀 Enhancements' 24 | labels: 25 | - 'enhancement' 26 | - 'patch' 27 | - title: '🐛 Bug Fixes' 28 | labels: 29 | - 'fix' 30 | - 'bugfix' 31 | - 'bug' 32 | - 'hotfix' 33 | - title: '🤖 Automatic Updates' 34 | labels: 35 | - 'auto-update' 36 | 37 | change-template: | 38 |
39 | $TITLE @$AUTHOR (#$NUMBER) 40 | 41 | $BODY 42 |
43 | 44 | template: | 45 | $CHANGES 46 | 47 | replacers: 48 | # Remove irrelevant information from Renovate bot 49 | - search: '/(?<=---\s)\s*^#.*(Renovate configuration|Configuration)(?:.|\n)*?This PR has been generated .*/gm' 50 | replace: '' 51 | # Remove Renovate bot banner image 52 | - search: '/\[!\[[^\]]*Renovate\][^\]]*\](\([^)]*\))?\s*\n+/gm' 53 | replace: '' 54 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by https://probot.github.io/apps/settings/ 2 | extends: cloudposse/.github 3 | 4 | repository: 5 | # A URL with more information about the repository 6 | homepage: https://cloudposse.com 7 | 8 | # Either `true` to enable projects for this repository, or `false` to disable them. 9 | # If projects are disabled for the organization, passing `true` will cause an API error. 10 | has_projects: false 11 | 12 | # Either `true` to enable the wiki for this repository, `false` to disable it. 13 | has_wiki: false 14 | -------------------------------------------------------------------------------- /.github/workflows/auto-release.yml: -------------------------------------------------------------------------------- 1 | name: auto-release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | - production 9 | 10 | jobs: 11 | auto: 12 | uses: cloudposse/.github/.github/workflows/shared-auto-release.yml@main 13 | with: 14 | publish: true 15 | secrets: inherit 16 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: "docker" 2 | on: 3 | pull_request: 4 | types: [opened, synchronize, reopened] 5 | release: 6 | types: 7 | # "released" excludes pre-releases 8 | # "published" is either a release or a pre-release 9 | - published 10 | jobs: 11 | build-and-push: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout source code at current commit 15 | uses: actions/checkout@v2 16 | 17 | # Based off of Geodesic docker workflow: 18 | # https://github.com/cloudposse/geodesic/blob/master/.github/workflows/docker.yml 19 | - name: Prepare tags for Docker image 20 | id: prepare 21 | run: | 22 | echo ::set-output name=publish::${{ (github.event_name == 'release' && github.event.action == 'published') || (github.event.pull_request.head.repo.full_name == github.repository) }} 23 | if [[ $GITHUB_REF == refs/tags/* ]]; then 24 | VERSION=${GITHUB_REF#refs/tags/} 25 | fi 26 | 27 | # By default, we tag our image with the short sha on all PR pushes 28 | TAGS="${{ github.repository }}:sha-${GITHUB_SHA:0:7}" 29 | 30 | # If this is a tagged release, then we tag w/ the semver tag + latest 31 | if [[ -n $VERSION ]]; then 32 | TAGS="$TAGS,${{ github.repository }}:${VERSION},${{ github.repository }}:latest" 33 | fi 34 | 35 | printf "Tagging with %s\n" "${TAGS}" 36 | echo "tags=${TAGS}" >> $GITHUB_OUTPUT 37 | - name: Set up Docker Buildx 38 | uses: docker/setup-buildx-action@v1 39 | - name: Login to DockerHub 40 | if: steps.prepare.outputs.publish == 'true' 41 | uses: docker/login-action@v1 42 | with: 43 | username: ${{ secrets.DOCKERHUB_USERNAME }} 44 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 45 | - name: "Build and push docker image to DockerHub" 46 | id: docker_build 47 | uses: docker/build-push-action@v2 48 | with: 49 | push: ${{ steps.prepare.outputs.publish == 'true' }} 50 | tags: ${{ steps.prepare.outputs.tags }} 51 | file: ./Dockerfile 52 | -------------------------------------------------------------------------------- /.github/workflows/validate-codeowners.yml: -------------------------------------------------------------------------------- 1 | name: Validate Codeowners 2 | on: 3 | workflow_dispatch: 4 | 5 | pull_request: 6 | 7 | jobs: 8 | validate-codeowners: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: "Checkout source code at current commit" 12 | uses: actions/checkout@v2 13 | # Leave pinned at 0.7.1 until https://github.com/mszostok/codeowners-validator/issues/173 is resolved 14 | - uses: mszostok/codeowners-validator@v0.7.1 15 | if: github.event.pull_request.head.repo.full_name == github.repository 16 | name: "Full check of CODEOWNERS" 17 | with: 18 | # For now, remove "files" check to allow CODEOWNERS to specify non-existent 19 | # files so we can use the same CODEOWNERS file for Terraform and non-Terraform repos 20 | # checks: "files,syntax,owners,duppatterns" 21 | checks: "syntax,owners,duppatterns" 22 | owner_checker_allow_unowned_patterns: "false" 23 | # GitHub access token is required only if the `owners` check is enabled 24 | github_access_token: "${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}" 25 | - uses: mszostok/codeowners-validator@v0.7.1 26 | if: github.event.pull_request.head.repo.full_name != github.repository 27 | name: "Syntax check of CODEOWNERS" 28 | with: 29 | checks: "syntax,duppatterns" 30 | owner_checker_allow_unowned_patterns: "false" 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .build-harness 2 | build-harness/ 3 | aws-assumed-role/ 4 | .idea/ 5 | *.iml 6 | 7 | # Compiled and auto-generated files 8 | # Note that the leading "**/" appears necessary for Docker even if not for Git 9 | **/nohup.out 10 | **/*.tfstate 11 | **/*.tfstate.* 12 | **/planfile 13 | **/*.planfile 14 | **/*.kubeconfig 15 | **/terraform.tfvars.json 16 | **/terraform.auto.tfvars.json 17 | **/*.terraform.tfvars.json 18 | **/*.terraform.auto.tfvars.json 19 | **/*.helmfile.vars.yaml 20 | **/vendir.lock.yml 21 | **/.terraform.lock.hcl 22 | **/backend.tf.json 23 | 24 | # Module directory 25 | # Note that the leading "**/" appears necessary for Docker even if not for Git 26 | **/.terraform/ 27 | **/.module/ 28 | **/.helmfile/ 29 | 30 | # Draft or auto-saved version 31 | # Note that the leading "**/" appears necessary for Docker even if not for Git 32 | **/*.draft.* 33 | **/*.draft 34 | **/*.orig 35 | **/*.bak 36 | **/*~ 37 | 38 | # macOS special files and folders 39 | **/.DS_Store 40 | **/.CFUserTextEncoding 41 | **/.Trash/ 42 | **/$RECYCLE.BIN/ 43 | -------------------------------------------------------------------------------- /01-geodesic/README.md: -------------------------------------------------------------------------------- 1 | ## Tutorial #1: Getting started with Geodesic 2 | 3 | The code in this directory is an accompaniment to the [Getting started with Geodesic](https://docs.cloudposse.com/tutorials/geodesic-getting-started/) tutorial in our SweetOps documentation. -------------------------------------------------------------------------------- /01-geodesic/main.tf: -------------------------------------------------------------------------------- 1 | data "http" "star_wars" { 2 | url = "https://swapi.dev/api/people/1" 3 | request_headers = { 4 | Accept = "application/json" 5 | } 6 | } 7 | 8 | output "star_wars_data" { 9 | value = jsondecode(data.http.star_wars.body) 10 | description = "Star wars data that we output as part of this simple example project" 11 | } 12 | -------------------------------------------------------------------------------- /02-atmos/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial #2: Getting started with Atmos 2 | 3 | The code in this directory is an accompaniment to the [Getting started with Atmos](https://atmos.tools/tutorials/atmos-getting-started/) tutorial in our SweetOps documentation. 4 | -------------------------------------------------------------------------------- /02-atmos/atmos.yaml: -------------------------------------------------------------------------------- 1 | # CLI config is loaded from the following locations (from lowest to highest priority): 2 | # system dir ('/usr/local/etc/atmos' on Linux, '%LOCALAPPDATA%/atmos' on Windows) 3 | # home dir (~/.atmos) 4 | # current directory 5 | # ENV vars 6 | # Command-line arguments 7 | # 8 | # It supports POSIX-style Globs for file names/paths (double-star '**' is supported) 9 | # https://en.wikipedia.org/wiki/Glob_(programming) 10 | 11 | # Base path for components, stacks and workflows configurations. 12 | # Can also be set using 'ATMOS_BASE_PATH' ENV var, or '--base-path' command-line argument. 13 | # Supports both absolute and relative paths. 14 | # If not provided or is an empty string, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' 15 | # are independent settings (supporting both absolute and relative paths). 16 | # If 'base_path' is provided, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' 17 | # are considered paths relative to 'base_path'. 18 | base_path: "." 19 | 20 | components: 21 | terraform: 22 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_BASE_PATH' ENV var, or '--terraform-dir' command-line argument 23 | # Supports both absolute and relative paths 24 | base_path: "components/terraform" 25 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE' ENV var 26 | apply_auto_approve: false 27 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT' ENV var, or '--deploy-run-init' command-line argument 28 | deploy_run_init: true 29 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE' ENV var, or '--init-run-reconfigure' command-line argument 30 | init_run_reconfigure: true 31 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE' ENV var, or '--auto-generate-backend-file' command-line argument 32 | auto_generate_backend_file: false 33 | 34 | stacks: 35 | # Can also be set using 'ATMOS_STACKS_BASE_PATH' ENV var, or '--config-dir' and '--stacks-dir' command-line arguments 36 | # Supports both absolute and relative paths 37 | base_path: "stacks" 38 | # Can also be set using 'ATMOS_STACKS_INCLUDED_PATHS' ENV var (comma-separated values string) 39 | included_paths: 40 | - "**/*" 41 | # Can also be set using 'ATMOS_STACKS_EXCLUDED_PATHS' ENV var (comma-separated values string) 42 | excluded_paths: 43 | - "**/_defaults.yaml" 44 | # Can also be set using 'ATMOS_STACKS_NAME_PATTERN' ENV var 45 | name_pattern: "{stage}" 46 | 47 | workflows: 48 | # Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line arguments 49 | # Supports both absolute and relative paths 50 | base_path: "stacks/workflows" 51 | 52 | logs: 53 | verbose: false 54 | colors: true 55 | -------------------------------------------------------------------------------- /02-atmos/components/terraform/fetch-location/main.tf: -------------------------------------------------------------------------------- 1 | 2 | # Invoke an the ipapi to get information on the client's IP Address. 3 | data "http" "fetch_location" { 4 | url = "https://ipwhois.app/json/" 5 | request_headers = { 6 | Accept = "application/json" 7 | } 8 | } 9 | 10 | locals { 11 | location_response = jsondecode(data.http.fetch_location.body) 12 | location_map = { 13 | lat = local.location_response["latitude"], 14 | lon = local.location_response["longitude"], 15 | city = local.location_response["city"], 16 | region = local.location_response["region"], 17 | country = local.location_response["country_code"], 18 | } 19 | location_str = "${local.location_map.city}, ${local.location_map.region}, ${local.location_map.country}" 20 | } 21 | 22 | output "users_location_map" { 23 | value = local.location_map 24 | } 25 | 26 | output "users_location" { 27 | value = local.location_str 28 | } 29 | -------------------------------------------------------------------------------- /02-atmos/components/terraform/fetch-weather/main.tf: -------------------------------------------------------------------------------- 1 | variable "hourly_forecast" { 2 | type = bool 3 | description = "Whether or not to retrieve hourly weather data" 4 | default = false 5 | } 6 | 7 | data "terraform_remote_state" "location" { 8 | backend = "local" 9 | 10 | config = { 11 | path = "${path.root}/../fetch-location/terraform.tfstate.d/example/terraform.tfstate" 12 | } 13 | } 14 | 15 | locals { 16 | # Pulls Longitude and Latitude from the Remote State of fetch-location 17 | users_location_map = data.terraform_remote_state.location.outputs.users_location_map 18 | lat = local.users_location_map.lat 19 | lon = local.users_location_map.lon 20 | 21 | # Curls Weather API for Location Data 22 | location_url = "https://api.weather.gov/points/${local.lat},${local.lon}" 23 | location_data = jsondecode(data.http.fetch_location.body).properties 24 | 25 | # Curls Weather API for Forecast Data 26 | weather_url = (var.hourly_forecast) ? local.location_data.forecastHourly : local.location_data.forecast 27 | weather_data = jsondecode(data.http.fetch_weather.body).properties.periods[0] 28 | } 29 | 30 | data "http" "fetch_location" { 31 | url = local.location_url 32 | } 33 | 34 | data "http" "fetch_weather" { 35 | url = local.weather_url 36 | } 37 | 38 | output "location_data" { 39 | value = local.location_data 40 | } 41 | 42 | output "weather_data" { 43 | value = local.weather_data 44 | } 45 | -------------------------------------------------------------------------------- /02-atmos/components/terraform/output-results/main.tf: -------------------------------------------------------------------------------- 1 | variable "print_users_weather_enabled" { 2 | default = false 3 | type = bool 4 | description = "Whether or not to pretty print the weather results" 5 | } 6 | 7 | data "terraform_remote_state" "weather" { 8 | backend = "local" 9 | 10 | config = { 11 | path = "${path.root}/../fetch-weather/terraform.tfstate.d/example/terraform.tfstate" 12 | } 13 | } 14 | 15 | data "terraform_remote_state" "location" { 16 | backend = "local" 17 | 18 | config = { 19 | path = "${path.root}/../fetch-location/terraform.tfstate.d/example/terraform.tfstate" 20 | } 21 | } 22 | 23 | locals { 24 | users_location = data.terraform_remote_state.location.outputs.users_location 25 | 26 | weather_data = data.terraform_remote_state.weather.outputs.weather_data 27 | weather_start_time = local.weather_data.startTime 28 | weather_end_time = local.weather_data.endTime 29 | weather_temp = local.weather_data.temperature 30 | weather_temp_unit = local.weather_data.temperatureUnit 31 | weather_description = local.weather_data.shortForecast 32 | } 33 | 34 | data "template_file" "weather_report" { 35 | template = file("${path.root}/weather-report.tpl") 36 | vars = { 37 | users_location = local.users_location 38 | weather_temp = local.weather_temp 39 | weather_temp_unit = local.weather_temp_unit 40 | weather_description = local.weather_description 41 | weather_start_time = local.weather_start_time 42 | weather_end_time = local.weather_end_time 43 | } 44 | } 45 | 46 | resource "null_resource" "print" { 47 | count = var.print_users_weather_enabled ? 1 : 0 48 | 49 | triggers = { 50 | weather_report = data.template_file.weather_report.rendered 51 | } 52 | 53 | provisioner "local-exec" { 54 | command = "echo '${data.template_file.weather_report.rendered}'" 55 | } 56 | } 57 | 58 | output "weather_temp" { 59 | value = local.weather_temp 60 | } 61 | 62 | output "weather_description" { 63 | value = local.weather_description 64 | } 65 | 66 | output "weather_date" { 67 | value = "${local.weather_start_time} through ${local.weather_end_time}" 68 | } 69 | 70 | output "weather_report" { 71 | value = data.template_file.weather_report.rendered 72 | } 73 | -------------------------------------------------------------------------------- /02-atmos/components/terraform/output-results/weather-report.tpl: -------------------------------------------------------------------------------- 1 | === Your Weather Report === 2 | ============================ 3 | Location: ${users_location} 4 | Temperature: ${weather_temp}°${weather_temp_unit} 5 | Weather: ${weather_description} 6 | Time: ${weather_start_time} to ${weather_end_time} 7 | ============================ 8 | -------------------------------------------------------------------------------- /02-atmos/stacks/catalog/example.yaml: -------------------------------------------------------------------------------- 1 | import: [] 2 | vars: 3 | stage: example 4 | 5 | terraform: 6 | vars: {} 7 | 8 | helmfile: 9 | vars: {} 10 | 11 | components: 12 | terraform: 13 | fetch-location: 14 | vars: {} 15 | 16 | fetch-weather: 17 | vars: {} 18 | 19 | output-results: 20 | vars: 21 | print_users_weather_enabled: true 22 | 23 | helmfile: {} 24 | -------------------------------------------------------------------------------- /02-atmos/stacks/workflows/example.yaml: -------------------------------------------------------------------------------- 1 | workflows: 2 | deploy-all: 3 | description: Deploy terraform projects in order 4 | steps: 5 | - command: terraform deploy fetch-location 6 | - command: terraform deploy fetch-weather 7 | - command: terraform deploy output-results 8 | -------------------------------------------------------------------------------- /03-first-aws-environment/README.md: -------------------------------------------------------------------------------- 1 | # Tutorial #3: You first AWS Environment with Atmos 2 | 3 | The code in this directory is an accompaniment to the [Your first environment on AWS](https://atmos.tools/tutorials/first-aws-environment/) tutorial in our SweetOps documentation. 4 | -------------------------------------------------------------------------------- /03-first-aws-environment/atmos.yaml: -------------------------------------------------------------------------------- 1 | # CLI config is loaded from the following locations (from lowest to highest priority): 2 | # system dir ('/usr/local/etc/atmos' on Linux, '%LOCALAPPDATA%/atmos' on Windows) 3 | # home dir (~/.atmos) 4 | # current directory 5 | # ENV vars 6 | # Command-line arguments 7 | # 8 | # It supports POSIX-style Globs for file names/paths (double-star '**' is supported) 9 | # https://en.wikipedia.org/wiki/Glob_(programming) 10 | 11 | # Base path for components, stacks and workflows configurations. 12 | # Can also be set using 'ATMOS_BASE_PATH' ENV var, or '--base-path' command-line argument. 13 | # Supports both absolute and relative paths. 14 | # If not provided or is an empty string, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' 15 | # are independent settings (supporting both absolute and relative paths). 16 | # If 'base_path' is provided, 'components.terraform.base_path', 'components.helmfile.base_path', 'stacks.base_path' and 'workflows.base_path' 17 | # are considered paths relative to 'base_path'. 18 | base_path: "." 19 | 20 | components: 21 | terraform: 22 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_BASE_PATH' ENV var, or '--terraform-dir' command-line argument 23 | # Supports both absolute and relative paths 24 | base_path: "components/terraform" 25 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_APPLY_AUTO_APPROVE' ENV var 26 | apply_auto_approve: false 27 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_DEPLOY_RUN_INIT' ENV var, or '--deploy-run-init' command-line argument 28 | deploy_run_init: true 29 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_INIT_RUN_RECONFIGURE' ENV var, or '--init-run-reconfigure' command-line argument 30 | init_run_reconfigure: true 31 | # Can also be set using 'ATMOS_COMPONENTS_TERRAFORM_AUTO_GENERATE_BACKEND_FILE' ENV var, or '--auto-generate-backend-file' command-line argument 32 | auto_generate_backend_file: false 33 | 34 | stacks: 35 | # Can also be set using 'ATMOS_STACKS_BASE_PATH' ENV var, or '--config-dir' and '--stacks-dir' command-line arguments 36 | # Supports both absolute and relative paths 37 | base_path: "stacks" 38 | # Can also be set using 'ATMOS_STACKS_INCLUDED_PATHS' ENV var (comma-separated values string) 39 | included_paths: 40 | - "**/*" 41 | # Can also be set using 'ATMOS_STACKS_EXCLUDED_PATHS' ENV var (comma-separated values string) 42 | excluded_paths: 43 | - "**/_defaults.yaml" 44 | # Can also be set using 'ATMOS_STACKS_NAME_PATTERN' ENV var 45 | name_pattern: "{environment}-{stage}" 46 | 47 | workflows: 48 | # Can also be set using 'ATMOS_WORKFLOWS_BASE_PATH' ENV var, or '--workflows-dir' command-line arguments 49 | # Supports both absolute and relative paths 50 | base_path: "stacks/workflows" 51 | 52 | logs: 53 | verbose: false 54 | colors: true 55 | -------------------------------------------------------------------------------- /03-first-aws-environment/bin/random-pet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Since we're creating an S3 bucket as part of this tutorial, we need the names of those 4 | # buckets to be unique, otherwise we'll run into issues with AWS S3 API errors blocking 5 | # our terraform applies. This scripts generates random pet names to append to attributes 6 | # which ensures all bucket names in the project are unique. 7 | 8 | # Build random value 9 | # We install golang-petname as part of our tutorials toolbox. 10 | RANDOM_PET=$(golang-petname) 11 | 12 | # Updates our tfstate-backend's attributes to be unique 13 | yq eval --inplace \ 14 | ".components.terraform.tfstate-backend.vars.attributes.[0] = \"$RANDOM_PET\"" \ 15 | stacks/ue2-root.yaml 16 | 17 | # Updates our static backend config with unique names 18 | yq eval --inplace \ 19 | ".terraform.backend.s3.bucket = \"acme-ue2-tfstate-$RANDOM_PET\"" \ 20 | stacks/catalog/globals.yaml 21 | 22 | yq eval --inplace \ 23 | ".terraform.backend.s3.dynamodb_table = \"acme-ue2-tfstate-lock-$RANDOM_PET\"" \ 24 | stacks/catalog/globals.yaml 25 | 26 | # Updates our dev + prod static-site component vars to ensure we're creating unique names 27 | yq eval --inplace \ 28 | ".components.terraform.static-site.vars.attributes.[0] = \"$RANDOM_PET\"" \ 29 | stacks/uw2-dev.yaml 30 | 31 | yq eval --inplace \ 32 | ".components.terraform.static-site.vars.attributes.[0] = \"$RANDOM_PET\"" \ 33 | stacks/uw2-prod.yaml 34 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/context.tf: -------------------------------------------------------------------------------- 1 | # 2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label 3 | # All other instances of this file should be a copy of that one 4 | # 5 | # 6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf 7 | # and then place it in your Terraform module to automatically get 8 | # Cloud Posse's standard configuration inputs suitable for passing 9 | # to Cloud Posse modules. 10 | # 11 | # Modules should access the whole context as `module.this.context` 12 | # to get the input variables with nulls for defaults, 13 | # for example `context = module.this.context`, 14 | # and access individual variables as `module.this.`, 15 | # with final values filled in. 16 | # 17 | # For example, when using defaults, `module.this.context.delimiter` 18 | # will be null, and `module.this.delimiter` will be `-` (hyphen). 19 | # 20 | 21 | module "this" { 22 | source = "cloudposse/label/null" 23 | version = "0.24.1" # requires Terraform >= 0.13.0 24 | 25 | enabled = var.enabled 26 | namespace = var.namespace 27 | environment = var.environment 28 | stage = var.stage 29 | name = var.name 30 | delimiter = var.delimiter 31 | attributes = var.attributes 32 | tags = var.tags 33 | additional_tag_map = var.additional_tag_map 34 | label_order = var.label_order 35 | regex_replace_chars = var.regex_replace_chars 36 | id_length_limit = var.id_length_limit 37 | label_key_case = var.label_key_case 38 | label_value_case = var.label_value_case 39 | 40 | context = var.context 41 | } 42 | 43 | # Copy contents of cloudposse/terraform-null-label/variables.tf here 44 | 45 | variable "context" { 46 | type = any 47 | default = { 48 | enabled = true 49 | namespace = null 50 | environment = null 51 | stage = null 52 | name = null 53 | delimiter = null 54 | attributes = [] 55 | tags = {} 56 | additional_tag_map = {} 57 | regex_replace_chars = null 58 | label_order = [] 59 | id_length_limit = null 60 | label_key_case = null 61 | label_value_case = null 62 | } 63 | description = <<-EOT 64 | Single object for setting entire context at once. 65 | See description of individual variables for details. 66 | Leave string and numeric variables as `null` to use default value. 67 | Individual variable settings (non-null) override settings in context object, 68 | except for attributes, tags, and additional_tag_map, which are merged. 69 | EOT 70 | 71 | validation { 72 | condition = lookup(var.context, "label_key_case", null) == null ? true : contains(["lower", "title", "upper"], var.context["label_key_case"]) 73 | error_message = "Allowed values: `lower`, `title`, `upper`." 74 | } 75 | 76 | validation { 77 | condition = lookup(var.context, "label_value_case", null) == null ? true : contains(["lower", "title", "upper", "none"], var.context["label_value_case"]) 78 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`." 79 | } 80 | } 81 | 82 | variable "enabled" { 83 | type = bool 84 | default = null 85 | description = "Set to false to prevent the module from creating any resources" 86 | } 87 | 88 | variable "namespace" { 89 | type = string 90 | default = null 91 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 92 | } 93 | 94 | variable "environment" { 95 | type = string 96 | default = null 97 | description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'" 98 | } 99 | 100 | variable "stage" { 101 | type = string 102 | default = null 103 | description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'" 104 | } 105 | 106 | variable "name" { 107 | type = string 108 | default = null 109 | description = "Solution name, e.g. 'app' or 'jenkins'" 110 | } 111 | 112 | variable "delimiter" { 113 | type = string 114 | default = null 115 | description = <<-EOT 116 | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`. 117 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. 118 | EOT 119 | } 120 | 121 | variable "attributes" { 122 | type = list(string) 123 | default = [] 124 | description = "Additional attributes (e.g. `1`)" 125 | } 126 | 127 | variable "tags" { 128 | type = map(string) 129 | default = {} 130 | description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`" 131 | } 132 | 133 | variable "additional_tag_map" { 134 | type = map(string) 135 | default = {} 136 | description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`." 137 | } 138 | 139 | variable "label_order" { 140 | type = list(string) 141 | default = null 142 | description = <<-EOT 143 | The naming order of the id output and Name tag. 144 | Defaults to ["namespace", "environment", "stage", "name", "attributes"]. 145 | You can omit any of the 5 elements, but at least one must be present. 146 | EOT 147 | } 148 | 149 | variable "regex_replace_chars" { 150 | type = string 151 | default = null 152 | description = <<-EOT 153 | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. 154 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. 155 | EOT 156 | } 157 | 158 | variable "id_length_limit" { 159 | type = number 160 | default = null 161 | description = <<-EOT 162 | Limit `id` to this many characters (minimum 6). 163 | Set to `0` for unlimited length. 164 | Set to `null` for default, which is `0`. 165 | Does not affect `id_full`. 166 | EOT 167 | validation { 168 | condition = var.id_length_limit == null ? true : var.id_length_limit >= 6 || var.id_length_limit == 0 169 | error_message = "The id_length_limit must be >= 6 if supplied (not null), or 0 for unlimited length." 170 | } 171 | } 172 | 173 | variable "label_key_case" { 174 | type = string 175 | default = null 176 | description = <<-EOT 177 | The letter case of label keys (`tag` names) (i.e. `name`, `namespace`, `environment`, `stage`, `attributes`) to use in `tags`. 178 | Possible values: `lower`, `title`, `upper`. 179 | Default value: `title`. 180 | EOT 181 | 182 | validation { 183 | condition = var.label_key_case == null ? true : contains(["lower", "title", "upper"], var.label_key_case) 184 | error_message = "Allowed values: `lower`, `title`, `upper`." 185 | } 186 | } 187 | 188 | variable "label_value_case" { 189 | type = string 190 | default = null 191 | description = <<-EOT 192 | The letter case of output label values (also used in `tags` and `id`). 193 | Possible values: `lower`, `title`, `upper` and `none` (no transformation). 194 | Default value: `lower`. 195 | EOT 196 | 197 | validation { 198 | condition = var.label_value_case == null ? true : contains(["lower", "title", "upper", "none"], var.label_value_case) 199 | error_message = "Allowed values: `lower`, `title`, `upper`, `none`." 200 | } 201 | } 202 | #### End of copy of cloudposse/terraform-null-label/variables.tf 203 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/main.tf: -------------------------------------------------------------------------------- 1 | module "cdn" { 2 | source = "cloudposse/cloudfront-s3-cdn/aws" 3 | version = "0.90.0" 4 | 5 | name = "static-site" 6 | website_enabled = true 7 | cloudfront_access_logging_enabled = false 8 | 9 | context = module.this.context 10 | } 11 | 12 | resource "aws_s3_bucket_object" "site_index" { 13 | bucket = module.cdn.s3_bucket 14 | key = "index.html" 15 | content_type = "text/html" 16 | 17 | content = <<-EOT 18 |

Hello World!

19 |

This is ${var.stage}!

20 | EOT 21 | } 22 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/outputs.tf: -------------------------------------------------------------------------------- 1 | output "domain_name" { 2 | value = module.cdn.cf_domain_name 3 | description = "The FQDN of the created static site." 4 | } 5 | 6 | output "s3_bucket_name" { 7 | value = module.cdn.s3_bucket 8 | description = "The FQDN of the created static site." 9 | } 10 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "AWS Region" 4 | } 5 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/static-site/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | # Note, we are not upgrading to v5 until this issue is resolved: 8 | # https://github.com/cloudposse/terraform-aws-cloudfront-s3-cdn/issues/279 9 | version = "< 5" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/README.md: -------------------------------------------------------------------------------- 1 | # Component: `tfstate-backend` 2 | 3 | This component is responsible for provisioning an S3 Bucket and DynamoDB table that follow security best practices for usage as a Terraform backend. 4 | 5 | ## Usage 6 | 7 | **Stack Level**: Regional 8 | 9 | Here's an example snippet for how to use this component. 10 | 11 | ```yaml 12 | components: 13 | terraform: 14 | tfstate-backend: 15 | vars: 16 | name: tfstate 17 | force_destroy: false 18 | prevent_unencrypted_uploads: true 19 | enable_server_side_encryption: true 20 | ``` 21 | 22 | 23 | ## Requirements 24 | 25 | | Name | Version | 26 | |------|---------| 27 | | [terraform](#requirement\_terraform) | ~> 0.14.0 | 28 | | [aws](#requirement\_aws) | ~> 3.32 | 29 | | [local](#requirement\_local) | ~> 2.1 | 30 | | [template](#requirement\_template) | ~> 2.2 | 31 | 32 | ## Providers 33 | 34 | No providers. 35 | 36 | ## Modules 37 | 38 | | Name | Source | Version | 39 | |------|--------|---------| 40 | | [tfstate\_backend](#module\_tfstate\_backend) | cloudposse/tfstate-backend/aws | 0.32.0 | 41 | | [this](#module\_this) | cloudposse/label/null | 0.24.1 | 42 | 43 | ## Resources 44 | 45 | No resources. 46 | 47 | ## Inputs 48 | 49 | | Name | Description | Type | Default | Required | 50 | |------|-------------|------|---------|:--------:| 51 | | [additional\_tag\_map](#input\_additional\_tag\_map) | Additional tags for appending to tags\_as\_list\_of\_maps. Not added to `tags`. | `map(string)` | `{}` | no | 52 | | [attributes](#input\_attributes) | Additional attributes (e.g. `1`) | `list(string)` | `[]` | no | 53 | | [context](#input\_context) | Single object for setting entire context at once.
See description of individual variables for details.
Leave string and numeric variables as `null` to use default value.
Individual variable settings (non-null) override settings in context object,
except for attributes, tags, and additional\_tag\_map, which are merged. |
object({
enabled = bool
namespace = string
environment = string
stage = string
name = string
delimiter = string
attributes = list(string)
tags = map(string)
additional_tag_map = map(string)
regex_replace_chars = string
label_order = list(string)
id_length_limit = number
})
|
{
"additional_tag_map": {},
"attributes": [],
"delimiter": null,
"enabled": true,
"environment": null,
"id_length_limit": null,
"label_order": [],
"name": null,
"namespace": null,
"regex_replace_chars": null,
"stage": null,
"tags": {}
}
| no | 54 | | [delimiter](#input\_delimiter) | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | 55 | | [enable\_server\_side\_encryption](#input\_enable\_server\_side\_encryption) | Enable DynamoDB and S3 server-side encryption | `bool` | `true` | no | 56 | | [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | 57 | | [environment](#input\_environment) | Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | 58 | | [force\_destroy](#input\_force\_destroy) | A boolean that indicates the terraform state S3 bucket can be destroyed even if it contains objects. These objects are not recoverable. | `bool` | `false` | no | 59 | | [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters.
Set to `0` for unlimited length.
Set to `null` for default, which is `0`.
Does not affect `id_full`. | `number` | `null` | no | 60 | | [label\_order](#input\_label\_order) | The naming order of the id output and Name tag.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 5 elements, but at least one must be present. | `list(string)` | `null` | no | 61 | | [name](#input\_name) | Solution name, e.g. 'app' or 'jenkins' | `string` | `null` | no | 62 | | [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `null` | no | 63 | | [prevent\_unencrypted\_uploads](#input\_prevent\_unencrypted\_uploads) | Prevent uploads of unencrypted objects to S3 | `bool` | `false` | no | 64 | | [regex\_replace\_chars](#input\_regex\_replace\_chars) | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | 65 | | [region](#input\_region) | AWS Region | `string` | n/a | yes | 66 | | [stage](#input\_stage) | Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | 67 | | [tags](#input\_tags) | Additional tags (e.g. `map('BusinessUnit','XYZ')` | `map(string)` | `{}` | no | 68 | | [tfstate\_account\_id](#input\_tfstate\_account\_id) | The ID of the account where the Terraform remote state backend is provisioned | `string` | `""` | no | 69 | | [tfstate\_assume\_role](#input\_tfstate\_assume\_role) | Set to false to use the caller's role to access the Terraform remote state | `bool` | `true` | no | 70 | | [tfstate\_bucket\_environment\_name](#input\_tfstate\_bucket\_environment\_name) | The name of the environment for Terraform state bucket | `string` | `""` | no | 71 | | [tfstate\_bucket\_stage\_name](#input\_tfstate\_bucket\_stage\_name) | The name of the stage for Terraform state bucket | `string` | `"root"` | no | 72 | | [tfstate\_existing\_role\_arn](#input\_tfstate\_existing\_role\_arn) | The ARN of the existing IAM Role to access the Terraform remote state. If not provided and `remote_state_assume_role` is `true`, a role will be constructed from `remote_state_role_arn_template` | `string` | `""` | no | 73 | | [tfstate\_role\_arn\_template](#input\_tfstate\_role\_arn\_template) | IAM Role ARN template for accessing the Terraform remote state | `string` | `"arn:aws:iam::%s:role/%s-%s-%s-%s"` | no | 74 | | [tfstate\_role\_environment\_name](#input\_tfstate\_role\_environment\_name) | The name of the environment for Terraform state IAM role | `string` | `"gbl"` | no | 75 | | [tfstate\_role\_name](#input\_tfstate\_role\_name) | IAM Role name for accessing the Terraform remote state | `string` | `"terraform"` | no | 76 | | [tfstate\_role\_stage\_name](#input\_tfstate\_role\_stage\_name) | The name of the stage for Terraform state IAM role | `string` | `"root"` | no | 77 | 78 | ## Outputs 79 | 80 | | Name | Description | 81 | |------|-------------| 82 | | [tfstate\_backend\_dynamodb\_table\_arn](#output\_tfstate\_backend\_dynamodb\_table\_arn) | Terraform state DynamoDB table ARN | 83 | | [tfstate\_backend\_dynamodb\_table\_id](#output\_tfstate\_backend\_dynamodb\_table\_id) | Terraform state DynamoDB table ID | 84 | | [tfstate\_backend\_dynamodb\_table\_name](#output\_tfstate\_backend\_dynamodb\_table\_name) | Terraform state DynamoDB table name | 85 | | [tfstate\_backend\_s3\_bucket\_arn](#output\_tfstate\_backend\_s3\_bucket\_arn) | Terraform state S3 bucket ARN | 86 | | [tfstate\_backend\_s3\_bucket\_domain\_name](#output\_tfstate\_backend\_s3\_bucket\_domain\_name) | Terraform state S3 bucket domain name | 87 | | [tfstate\_backend\_s3\_bucket\_id](#output\_tfstate\_backend\_s3\_bucket\_id) | Terraform state S3 bucket ID | 88 | 89 | 90 | ## References 91 | 92 | - [cloudposse/terraform-aws-components](https://github.com/cloudposse/terraform-aws-components/tree/master/modules/tfstate-backend) - Cloud Posse's upstream component 93 | 94 | [](https://cpco.io/component) 95 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/context.tf: -------------------------------------------------------------------------------- 1 | # 2 | # ONLY EDIT THIS FILE IN github.com/cloudposse/terraform-null-label 3 | # All other instances of this file should be a copy of that one 4 | # 5 | # 6 | # Copy this file from https://github.com/cloudposse/terraform-null-label/blob/master/exports/context.tf 7 | # and then place it in your Terraform module to automatically get 8 | # Cloud Posse's standard configuration inputs suitable for passing 9 | # to Cloud Posse modules. 10 | # 11 | # Modules should access the whole context as `module.this.context` 12 | # to get the input variables with nulls for defaults, 13 | # for example `context = module.this.context`, 14 | # and access individual variables as `module.this.`, 15 | # with final values filled in. 16 | # 17 | # For example, when using defaults, `module.this.context.delimiter` 18 | # will be null, and `module.this.delimiter` will be `-` (hyphen). 19 | # 20 | 21 | module "this" { 22 | source = "cloudposse/label/null" 23 | version = "0.24.1" # requires Terraform >= 0.13.0 24 | 25 | enabled = var.enabled 26 | namespace = var.namespace 27 | environment = var.environment 28 | stage = var.stage 29 | name = var.name 30 | delimiter = var.delimiter 31 | attributes = var.attributes 32 | tags = var.tags 33 | additional_tag_map = var.additional_tag_map 34 | label_order = var.label_order 35 | regex_replace_chars = var.regex_replace_chars 36 | id_length_limit = var.id_length_limit 37 | 38 | context = var.context 39 | } 40 | 41 | # Copy contents of cloudposse/terraform-null-label/variables.tf here 42 | 43 | variable "context" { 44 | type = object({ 45 | enabled = bool 46 | namespace = string 47 | environment = string 48 | stage = string 49 | name = string 50 | delimiter = string 51 | attributes = list(string) 52 | tags = map(string) 53 | additional_tag_map = map(string) 54 | regex_replace_chars = string 55 | label_order = list(string) 56 | id_length_limit = number 57 | }) 58 | default = { 59 | enabled = true 60 | namespace = null 61 | environment = null 62 | stage = null 63 | name = null 64 | delimiter = null 65 | attributes = [] 66 | tags = {} 67 | additional_tag_map = {} 68 | regex_replace_chars = null 69 | label_order = [] 70 | id_length_limit = null 71 | } 72 | description = <<-EOT 73 | Single object for setting entire context at once. 74 | See description of individual variables for details. 75 | Leave string and numeric variables as `null` to use default value. 76 | Individual variable settings (non-null) override settings in context object, 77 | except for attributes, tags, and additional_tag_map, which are merged. 78 | EOT 79 | } 80 | 81 | variable "enabled" { 82 | type = bool 83 | default = null 84 | description = "Set to false to prevent the module from creating any resources" 85 | } 86 | 87 | variable "namespace" { 88 | type = string 89 | default = null 90 | description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" 91 | } 92 | 93 | variable "environment" { 94 | type = string 95 | default = null 96 | description = "Environment, e.g. 'uw2', 'us-west-2', OR 'prod', 'staging', 'dev', 'UAT'" 97 | } 98 | 99 | variable "stage" { 100 | type = string 101 | default = null 102 | description = "Stage, e.g. 'prod', 'staging', 'dev', OR 'source', 'build', 'test', 'deploy', 'release'" 103 | } 104 | 105 | variable "name" { 106 | type = string 107 | default = null 108 | description = "Solution name, e.g. 'app' or 'jenkins'" 109 | } 110 | 111 | variable "delimiter" { 112 | type = string 113 | default = null 114 | description = <<-EOT 115 | Delimiter to be used between `namespace`, `environment`, `stage`, `name` and `attributes`. 116 | Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. 117 | EOT 118 | } 119 | 120 | variable "attributes" { 121 | type = list(string) 122 | default = [] 123 | description = "Additional attributes (e.g. `1`)" 124 | } 125 | 126 | variable "tags" { 127 | type = map(string) 128 | default = {} 129 | description = "Additional tags (e.g. `map('BusinessUnit','XYZ')`" 130 | } 131 | 132 | variable "additional_tag_map" { 133 | type = map(string) 134 | default = {} 135 | description = "Additional tags for appending to tags_as_list_of_maps. Not added to `tags`." 136 | } 137 | 138 | variable "label_order" { 139 | type = list(string) 140 | default = null 141 | description = <<-EOT 142 | The naming order of the id output and Name tag. 143 | Defaults to ["namespace", "environment", "stage", "name", "attributes"]. 144 | You can omit any of the 5 elements, but at least one must be present. 145 | EOT 146 | } 147 | 148 | variable "regex_replace_chars" { 149 | type = string 150 | default = null 151 | description = <<-EOT 152 | Regex to replace chars with empty string in `namespace`, `environment`, `stage` and `name`. 153 | If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. 154 | EOT 155 | } 156 | 157 | variable "id_length_limit" { 158 | type = number 159 | default = null 160 | description = <<-EOT 161 | Limit `id` to this many characters. 162 | Set to `0` for unlimited length. 163 | Set to `null` for default, which is `0`. 164 | Does not affect `id_full`. 165 | EOT 166 | } 167 | 168 | #### End of copy of cloudposse/terraform-null-label/variables.tf 169 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/default.auto.tfvars: -------------------------------------------------------------------------------- 1 | # This file is included by default in terraform plans; see Makefile.parent plan commands. 2 | 3 | enabled = true 4 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/main.tf: -------------------------------------------------------------------------------- 1 | module "tfstate_backend" { 2 | source = "cloudposse/tfstate-backend/aws" 3 | version = "0.32.0" # requires Terraform >= 0.13.0 4 | 5 | force_destroy = var.force_destroy 6 | prevent_unencrypted_uploads = var.prevent_unencrypted_uploads 7 | enable_server_side_encryption = var.enable_server_side_encryption 8 | 9 | context = module.this.context 10 | } 11 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/outputs.tf: -------------------------------------------------------------------------------- 1 | output "tfstate_backend_s3_bucket_domain_name" { 2 | value = module.tfstate_backend.s3_bucket_domain_name 3 | description = "Terraform state S3 bucket domain name" 4 | } 5 | 6 | output "tfstate_backend_s3_bucket_id" { 7 | value = module.tfstate_backend.s3_bucket_id 8 | description = "Terraform state S3 bucket ID" 9 | } 10 | 11 | output "tfstate_backend_s3_bucket_arn" { 12 | value = module.tfstate_backend.s3_bucket_arn 13 | description = "Terraform state S3 bucket ARN" 14 | } 15 | 16 | output "tfstate_backend_dynamodb_table_name" { 17 | value = module.tfstate_backend.dynamodb_table_name 18 | description = "Terraform state DynamoDB table name" 19 | } 20 | 21 | output "tfstate_backend_dynamodb_table_id" { 22 | value = module.tfstate_backend.dynamodb_table_id 23 | description = "Terraform state DynamoDB table ID" 24 | } 25 | 26 | output "tfstate_backend_dynamodb_table_arn" { 27 | value = module.tfstate_backend.dynamodb_table_arn 28 | description = "Terraform state DynamoDB table ARN" 29 | } 30 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | } 4 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/tfstate-context.tf: -------------------------------------------------------------------------------- 1 | variable "tfstate_assume_role" { 2 | type = bool 3 | description = "Set to false to use the caller's role to access the Terraform remote state" 4 | default = true 5 | } 6 | 7 | variable "tfstate_existing_role_arn" { 8 | type = string 9 | description = "The ARN of the existing IAM Role to access the Terraform remote state. If not provided and `remote_state_assume_role` is `true`, a role will be constructed from `remote_state_role_arn_template`" 10 | default = "" 11 | } 12 | 13 | variable "tfstate_account_id" { 14 | type = string 15 | default = "" 16 | description = "The ID of the account where the Terraform remote state backend is provisioned" 17 | } 18 | 19 | variable "tfstate_role_arn_template" { 20 | type = string 21 | default = "arn:aws:iam::%s:role/%s-%s-%s-%s" 22 | description = "IAM Role ARN template for accessing the Terraform remote state" 23 | } 24 | 25 | variable "tfstate_role_environment_name" { 26 | type = string 27 | default = "gbl" 28 | description = "The name of the environment for Terraform state IAM role" 29 | } 30 | 31 | variable "tfstate_role_stage_name" { 32 | type = string 33 | default = "root" 34 | description = "The name of the stage for Terraform state IAM role" 35 | } 36 | 37 | variable "tfstate_bucket_environment_name" { 38 | type = string 39 | default = "" 40 | description = "The name of the environment for Terraform state bucket" 41 | } 42 | 43 | variable "tfstate_bucket_stage_name" { 44 | type = string 45 | default = "root" 46 | description = "The name of the stage for Terraform state bucket" 47 | } 48 | 49 | variable "tfstate_role_name" { 50 | type = string 51 | default = "terraform" 52 | description = "IAM Role name for accessing the Terraform remote state" 53 | } 54 | 55 | locals { 56 | tfstate_access_role_arn = var.tfstate_assume_role ? ( 57 | (var.tfstate_existing_role_arn != null && var.tfstate_existing_role_arn != "") ? var.tfstate_existing_role_arn : ( 58 | format(var.tfstate_role_arn_template, 59 | var.tfstate_account_id, 60 | module.this.namespace, 61 | var.tfstate_role_environment_name, 62 | var.tfstate_role_stage_name, 63 | var.tfstate_role_name 64 | ) 65 | ) 66 | ) : null 67 | 68 | tfstate_bucket = "${module.this.namespace}-${var.tfstate_bucket_environment_name}-${var.tfstate_bucket_stage_name}-tfstate" 69 | tfstate_dynamodb_table = "${module.this.namespace}-${var.tfstate_bucket_environment_name}-${var.tfstate_bucket_stage_name}-tfstate-lock" 70 | } 71 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | type = string 3 | description = "AWS Region" 4 | } 5 | 6 | variable "force_destroy" { 7 | type = bool 8 | default = false 9 | description = "A boolean that indicates the terraform state S3 bucket can be destroyed even if it contains objects. These objects are not recoverable." 10 | } 11 | 12 | variable "prevent_unencrypted_uploads" { 13 | type = bool 14 | default = false 15 | description = "Prevent uploads of unencrypted objects to S3" 16 | } 17 | 18 | variable "enable_server_side_encryption" { 19 | type = bool 20 | default = true 21 | description = "Enable DynamoDB and S3 server-side encryption" 22 | } 23 | -------------------------------------------------------------------------------- /03-first-aws-environment/components/terraform/tfstate-backend/versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 3.32" 8 | } 9 | local = { 10 | source = "hashicorp/local" 11 | version = "~> 2.1" 12 | } 13 | template = { 14 | source = "cloudposse/template" 15 | version = "~> 2.2" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /03-first-aws-environment/stacks/catalog/globals.yaml: -------------------------------------------------------------------------------- 1 | vars: 2 | namespace: acme 3 | terraform: 4 | backend_type: s3 5 | backend: 6 | s3: 7 | encrypt: true 8 | bucket: "acme-ue2-root-tfstate-TODO" 9 | key: "terraform.tfstate" 10 | dynamodb_table: "acme-ue2-root-tfstate-lock-TODO" 11 | acl: "bucket-owner-full-control" 12 | region: "us-east-2" 13 | -------------------------------------------------------------------------------- /03-first-aws-environment/stacks/catalog/uw2-globals.yaml: -------------------------------------------------------------------------------- 1 | vars: 2 | region: us-west-2 3 | environment: uw2 4 | -------------------------------------------------------------------------------- /03-first-aws-environment/stacks/ue2-root.yaml: -------------------------------------------------------------------------------- 1 | import: 2 | - catalog/globals 3 | vars: 4 | region: us-east-2 5 | environment: ue2 6 | stage: root 7 | components: 8 | terraform: 9 | tfstate-backend: 10 | backend: 11 | s3: 12 | workspace_key_prefix: "tfstate-backend" 13 | vars: 14 | name: tfstate 15 | attributes: [TODO] 16 | force_destroy: false 17 | prevent_unencrypted_uploads: true 18 | enable_server_side_encryption: true 19 | -------------------------------------------------------------------------------- /03-first-aws-environment/stacks/uw2-dev.yaml: -------------------------------------------------------------------------------- 1 | import: 2 | - catalog/globals 3 | - catalog/uw2-globals 4 | vars: 5 | stage: dev 6 | components: 7 | terraform: 8 | static-site: 9 | vars: 10 | attributes: [TODO] 11 | -------------------------------------------------------------------------------- /03-first-aws-environment/stacks/uw2-prod.yaml: -------------------------------------------------------------------------------- 1 | import: 2 | - catalog/globals 3 | - catalog/uw2-globals 4 | vars: 5 | stage: prod 6 | components: 7 | terraform: 8 | static-site: 9 | vars: 10 | attributes: [TODO] 11 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG VERSION=latest 2 | ARG OS=debian 3 | ARG CLI_NAME=tutorials 4 | ARG TF_1_VERSION=1.3.10 5 | ARG ATMOS_VERSION=1.50.0 6 | 7 | FROM cloudposse/geodesic:$VERSION-$OS 8 | 9 | ARG TF_1_VERSION 10 | # Install terraform. 11 | RUN apt-get update && apt-get install -y -u --allow-downgrades \ 12 | terraform-1="${TF_1_VERSION}-*" && \ 13 | update-alternatives --set terraform /usr/share/terraform/1/bin/terraform 14 | 15 | # Install Atmos 16 | ARG ATMOS_VERSION 17 | RUN apt-get install -y --allow-downgrades \ 18 | atmos="${ATMOS_VERSION}-*" \ 19 | vendir 20 | 21 | # Geodesic message of the Day 22 | ENV MOTD_URL="" 23 | 24 | ENV AWS_VAULT_BACKEND=file 25 | 26 | ENV DOCKER_IMAGE="cloudposse/tutorials" 27 | ENV DOCKER_TAG="latest" 28 | 29 | # Geodesic banner 30 | ENV BANNER="Tutorials" 31 | 32 | COPY rootfs/ / 33 | COPY ./ /tutorials 34 | 35 | WORKDIR / 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | export DOCKER_ORG ?= cloudposse 4 | export DOCKER_IMAGE ?= $(DOCKER_ORG)/tutorials 5 | export DOCKER_TAG ?= latest 6 | export DOCKER_IMAGE_NAME ?= $(DOCKER_IMAGE):$(DOCKER_TAG) 7 | export CONTAINER_NAME ?= tuts 8 | 9 | # List of targets the `readme` target should call before generating the readme 10 | export README_DEPS ?= docs/targets.md 11 | 12 | -include $(shell curl -sSL -o .build-harness "https://cloudposse.tools/build-harness"; echo .build-harness) 13 | 14 | ## Initialize build-harness, install deps, build docker container, install wrapper script and run shell 15 | all: init deps build run 16 | @exit 0 17 | 18 | ## Install dependencies (if any) 19 | deps: 20 | @exit 0 21 | 22 | ## Build docker image 23 | build: 24 | @make --no-print-directory docker/build 25 | 26 | run: 27 | @docker run -it \ 28 | --rm \ 29 | --volume "$$HOME":/localhost \ 30 | --volume "$$PWD":/tutorials \ 31 | --name $(CONTAINER_NAME) \ 32 | cloudposse/tutorials:latest; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tutorials [![Latest Release](https://img.shields.io/github/release/cloudposse/tutorials.svg)](https://github.com/cloudposse/tutorials/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) 3 | 4 | 5 | [![README Header][readme_header_img]][readme_header_link] 6 | 7 | [![Cloud Posse][logo]](https://cpco.io/homepage) 8 | 9 | 29 | 30 | The tutorial code examples for usage with [docs.cloudposse.com/category/tutorials](https://docs.cloudposse.com/category/tutorials/). 31 | 32 | 33 | --- 34 | 35 | This project is part of our comprehensive ["SweetOps"](https://cpco.io/sweetops) approach towards DevOps. 36 | [][share_email] 37 | [][share_googleplus] 38 | [][share_facebook] 39 | [][share_reddit] 40 | [][share_linkedin] 41 | [][share_twitter] 42 | 43 | 44 | 45 | 46 | It's 100% Open Source and licensed under the [APACHE2](LICENSE). 47 | 48 | ## Introduction 49 | 50 | This tutorials repo is expected to accompany walking through our various getting started tutorials. It provides the hands on code that you can interact with to jumpstart your journey into understanding and utilizing the SweetOps methodology. 51 | 52 | ## Share the Love 53 | 54 | Like this project? Please give it a ★ on [our GitHub](https://github.com/cloudposse/atmos)! (it helps us **a lot**) 55 | 56 | Are you using this project or any of our other projects? Consider [leaving a testimonial][testimonial]. =) 57 | 58 | 59 | ## Related Projects 60 | 61 | Check out these related projects. 62 | 63 | - [docs.cloudposse.com](https://docs.cloudposse.com) - The documentation site for SweetOps 64 | - [docs](https://github.com/cloudposse/docs) - The source for our documentation site 65 | - [atmos](https://github.com/cloudposse/atmos) - Universal Tool for DevOps and Cloud Automation (works with terraform, helm, helmfile, istioctl, etc) 66 | - [Geodesic](https://github.com/cloudposse/geodesic) - 🚀 Geodesic is a DevOps Linux Distro. We use it as a cloud automation shell. It's the fastest way to get up and running with a rock solid Open Source toolchain. 67 | - [terraform-aws-components](https://github.com/cloudposse/terraform-aws-components) - Opinionated, self-contained Terraform root modules that each solve one, specific problem 68 | 69 | ## Help 70 | 71 | **Got a question?** We got answers. 72 | 73 | File a GitHub [issue](https://github.com/cloudposse/atmos/issues), send us an [email][email] or join our [Slack Community][slack]. 74 | 75 | [![README Commercial Support][readme_commercial_support_img]][readme_commercial_support_link] 76 | 77 | ## DevOps Accelerator for Startups 78 | 79 | 80 | We are a [**DevOps Accelerator**][commercial_support]. We'll help you build your cloud infrastructure from the ground up so you can own it. Then we'll show you how to operate it and stick around for as long as you need us. 81 | 82 | [![Learn More](https://img.shields.io/badge/learn%20more-success.svg?style=for-the-badge)][commercial_support] 83 | 84 | Work directly with our team of DevOps experts via email, slack, and video conferencing. 85 | 86 | We deliver 10x the value for a fraction of the cost of a full-time engineer. Our track record is not even funny. If you want things done right and you need it done FAST, then we're your best bet. 87 | 88 | - **Reference Architecture.** You'll get everything you need from the ground up built using 100% infrastructure as code. 89 | - **Release Engineering.** You'll have end-to-end CI/CD with unlimited staging environments. 90 | - **Site Reliability Engineering.** You'll have total visibility into your apps and microservices. 91 | - **Security Baseline.** You'll have built-in governance with accountability and audit logs for all changes. 92 | - **GitOps.** You'll be able to operate your infrastructure via Pull Requests. 93 | - **Training.** You'll receive hands-on training so your team can operate what we build. 94 | - **Questions.** You'll have a direct line of communication between our teams via a Shared Slack channel. 95 | - **Troubleshooting.** You'll get help to triage when things aren't working. 96 | - **Code Reviews.** You'll receive constructive feedback on Pull Requests. 97 | - **Bug Fixes.** We'll rapidly work with you to fix any bugs in our projects. 98 | 99 | ## Slack Community 100 | 101 | Join our [Open Source Community][slack] on Slack. It's **FREE** for everyone! Our "SweetOps" community is where you get to talk with others who share a similar vision for how to rollout and manage infrastructure. This is the best place to talk shop, ask questions, solicit feedback, and work together as a community to build totally *sweet* infrastructure. 102 | 103 | ## Discourse Forums 104 | 105 | Participate in our [Discourse Forums][discourse]. Here you'll find answers to commonly asked questions. Most questions will be related to the enormous number of projects we support on our GitHub. Come here to collaborate on answers, find solutions, and get ideas about the products and services we value. It only takes a minute to get started! Just sign in with SSO using your GitHub account. 106 | 107 | ## Newsletter 108 | 109 | Sign up for [our newsletter][newsletter] that covers everything on our technology radar. Receive updates on what we're up to on GitHub as well as awesome new projects we discover. 110 | 111 | ## Office Hours 112 | 113 | [Join us every Wednesday via Zoom][office_hours] for our weekly "Lunch & Learn" sessions. It's **FREE** for everyone! 114 | 115 | [![zoom](https://img.cloudposse.com/fit-in/200x200/https://cloudposse.com/wp-content/uploads/2019/08/Powered-by-Zoom.png")][office_hours] 116 | 117 | ## Contributing 118 | 119 | ### Bug Reports & Feature Requests 120 | 121 | Please use the [issue tracker](https://github.com/cloudposse/atmos/issues) to report any bugs or file feature requests. 122 | 123 | ### Developing 124 | 125 | If you are interested in being a contributor and want to get involved in developing this project or [help out](https://cpco.io/help-out) with our other projects, we would love to hear from you! Shoot us an [email][email]. 126 | 127 | In general, PRs are welcome. We follow the typical "fork-and-pull" Git workflow. 128 | 129 | 1. **Fork** the repo on GitHub 130 | 2. **Clone** the project to your own machine 131 | 3. **Commit** changes to your own branch 132 | 4. **Push** your work back up to your fork 133 | 5. Submit a **Pull Request** so that we can review your changes 134 | 135 | **NOTE:** Be sure to merge the latest changes from "upstream" before making a pull request! 136 | 137 | 138 | ## Copyright 139 | 140 | Copyright © 2017-2021 [Cloud Posse, LLC](https://cpco.io/copyright) 141 | 142 | 143 | 144 | ## License 145 | 146 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 147 | 148 | See [LICENSE](LICENSE) for full details. 149 | 150 | ```text 151 | Licensed to the Apache Software Foundation (ASF) under one 152 | or more contributor license agreements. See the NOTICE file 153 | distributed with this work for additional information 154 | regarding copyright ownership. The ASF licenses this file 155 | to you under the Apache License, Version 2.0 (the 156 | "License"); you may not use this file except in compliance 157 | with the License. You may obtain a copy of the License at 158 | 159 | https://www.apache.org/licenses/LICENSE-2.0 160 | 161 | Unless required by applicable law or agreed to in writing, 162 | software distributed under the License is distributed on an 163 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 164 | KIND, either express or implied. See the License for the 165 | specific language governing permissions and limitations 166 | under the License. 167 | ``` 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | ## Trademarks 178 | 179 | All other trademarks referenced herein are the property of their respective owners. 180 | 181 | ## About 182 | 183 | This project is maintained and funded by [Cloud Posse, LLC][website]. Like it? Please let us know by [leaving a testimonial][testimonial]! 184 | 185 | [![Cloud Posse][logo]][website] 186 | 187 | We're a [DevOps Professional Services][hire] company based in Los Angeles, CA. We ❤️ [Open Source Software][we_love_open_source]. 188 | 189 | We offer [paid support][commercial_support] on all of our projects. 190 | 191 | Check out [our other projects][github], [follow us on twitter][twitter], [apply for a job][jobs], or [hire us][hire] to help with your cloud strategy and implementation. 192 | 193 | 194 | 195 | ### Contributors 196 | 197 | 198 | | [![Erik Osterman][osterman_avatar]][osterman_homepage]
[Erik Osterman][osterman_homepage] | [![Andriy Knysh][aknysh_avatar]][aknysh_homepage]
[Andriy Knysh][aknysh_homepage] | 199 | |---|---| 200 | 201 | 202 | [osterman_homepage]: https://github.com/osterman 203 | [osterman_avatar]: https://img.cloudposse.com/150x150/https://github.com/osterman.png 204 | [aknysh_homepage]: https://github.com/aknysh 205 | [aknysh_avatar]: https://img.cloudposse.com/150x150/https://github.com/aknysh.png 206 | 207 | [![README Footer][readme_footer_img]][readme_footer_link] 208 | [![Beacon][beacon]][website] 209 | 210 | [logo]: https://cloudposse.com/logo-300x69.svg 211 | [docs]: https://cpco.io/docs?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=docs 212 | [website]: https://cpco.io/homepage?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=website 213 | [github]: https://cpco.io/github?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=github 214 | [jobs]: https://cpco.io/jobs?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=jobs 215 | [hire]: https://cpco.io/hire?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=hire 216 | [slack]: https://cpco.io/slack?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=slack 217 | [linkedin]: https://cpco.io/linkedin?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=linkedin 218 | [twitter]: https://cpco.io/twitter?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=twitter 219 | [testimonial]: https://cpco.io/leave-testimonial?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=testimonial 220 | [office_hours]: https://cloudposse.com/office-hours?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=office_hours 221 | [newsletter]: https://cpco.io/newsletter?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=newsletter 222 | [discourse]: https://ask.sweetops.com/?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=discourse 223 | [email]: https://cpco.io/email?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=email 224 | [commercial_support]: https://cpco.io/commercial-support?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=commercial_support 225 | [we_love_open_source]: https://cpco.io/we-love-open-source?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=we_love_open_source 226 | [terraform_modules]: https://cpco.io/terraform-modules?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=terraform_modules 227 | [readme_header_img]: https://cloudposse.com/readme/header/img 228 | [readme_header_link]: https://cloudposse.com/readme/header/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=readme_header_link 229 | [readme_footer_img]: https://cloudposse.com/readme/footer/img 230 | [readme_footer_link]: https://cloudposse.com/readme/footer/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=readme_footer_link 231 | [readme_commercial_support_img]: https://cloudposse.com/readme/commercial-support/img 232 | [readme_commercial_support_link]: https://cloudposse.com/readme/commercial-support/link?utm_source=github&utm_medium=readme&utm_campaign=cloudposse/atmos&utm_content=readme_commercial_support_link 233 | [share_twitter]: https://twitter.com/intent/tweet/?text=atmos&url=https://github.com/cloudposse/atmos 234 | [share_linkedin]: https://www.linkedin.com/shareArticle?mini=true&title=atmos&url=https://github.com/cloudposse/atmos 235 | [share_reddit]: https://reddit.com/submit/?url=https://github.com/cloudposse/atmos 236 | [share_facebook]: https://facebook.com/sharer/sharer.php?u=https://github.com/cloudposse/atmos 237 | [share_googleplus]: https://plus.google.com/share?url=https://github.com/cloudposse/atmos 238 | [share_email]: mailto:?subject=atmos&body=https://github.com/cloudposse/atmos 239 | [beacon]: https://ga-beacon.cloudposse.com/UA-76589703-4/cloudposse/atmos?pixel&cs=github&cm=readme&an=atmos 240 | -------------------------------------------------------------------------------- /docs/schema/stack-config-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/schema#", 3 | "type": "object", 4 | "properties": { 5 | "import": { 6 | "type": "array", 7 | "items": { 8 | "type": "string" 9 | } 10 | }, 11 | "vars": { 12 | "type": "object" 13 | }, 14 | "terraform": { 15 | "type": "object", 16 | "properties": { 17 | "vars": { 18 | "type": "object" 19 | } 20 | } 21 | }, 22 | "helmfile": { 23 | "type": "object", 24 | "properties": { 25 | "vars": { 26 | "type": "object" 27 | } 28 | } 29 | }, 30 | "components": { 31 | "type": "object", 32 | "properties": { 33 | "terraform": { 34 | "type": "object", 35 | "patternProperties": { 36 | "^.*$": { 37 | "type": "object", 38 | "properties": { 39 | "vars": { 40 | "type": "object" 41 | }, 42 | "backend": { 43 | "type": "object" 44 | } 45 | } 46 | } 47 | }, 48 | "additionalProperties": false 49 | }, 50 | "helmfile": { 51 | "type": "object", 52 | "patternProperties": { 53 | "^.*$": { 54 | "type": "object", 55 | "properties": { 56 | "vars": { 57 | "type": "object" 58 | }, 59 | "backend": false 60 | } 61 | } 62 | }, 63 | "additionalProperties": false 64 | } 65 | }, 66 | "additionalProperties": false 67 | }, 68 | "workflows": { 69 | "type": "object", 70 | "patternProperties": { 71 | "^.*$": { 72 | "type": "object" 73 | } 74 | }, 75 | "additionalProperties": false 76 | } 77 | }, 78 | "additionalProperties": false 79 | } 80 | -------------------------------------------------------------------------------- /docs/targets.md: -------------------------------------------------------------------------------- 1 | 2 | ## Makefile Targets 3 | ```text 4 | Available targets: 5 | 6 | help Help screen 7 | help/all Display help for all targets 8 | help/short This help short screen 9 | lint Lint terraform code 10 | 11 | ``` 12 | 13 | -------------------------------------------------------------------------------- /rootfs/etc/motd: -------------------------------------------------------------------------------- 1 | 2 | 3 | ================================================= 4 | Welcome to the SweetOps tutorials toolbox! 5 | 6 | This Geodesic toolbox is an interactive way to work through the various tutorials from the SweetOps documentation available at https://docs.cloudposse.com/tutorials. 7 | 8 | It includes all of the code from our tutorials repository mounted to `/tutorials` which you can view at https://github.com/cloudposse/tutorials. 9 | 10 | Please open a GitHub issue if you run into any problems! 11 | ================================================= 12 | --------------------------------------------------------------------------------