├── .gitignore ├── LICENSE.txt ├── README.md └── code ├── ansible └── 01-why-terraform │ ├── hosts │ ├── playbook.yml │ ├── procedural-examples.yml │ └── webserver.yml ├── bash ├── 01-why-terraform │ ├── README.md │ └── setup-webserver.sh ├── 02-intro-to-terraform-syntax │ ├── README.md │ └── run-webserver.sh └── 03-terraform-state │ ├── README.md │ ├── bash-unit-test-example.sh │ └── user-data.sh ├── json └── 02-intro-to-terraform-syntax │ └── iam-policy.json ├── opa └── 09-testing-terraform-code │ ├── README.md │ └── enforce_tagging.rego ├── packer └── 01-why-terraform │ ├── README.md │ └── webserver.json ├── ruby ├── 04-terraform-modules │ ├── function-example.rb │ ├── function-with-input-and-output-params-example.rb │ └── function-with-input-params-example.rb ├── 09-testing-terraform-code │ ├── web-server-basic-test.rb │ ├── web-server-basic.rb │ ├── web-server-full-di-test.rb │ ├── web-server-full-di.rb │ ├── web-server-full-test.rb │ └── web-server-full.rb └── 10-terraform-team │ ├── web-server-test.rb │ └── web-server.rb └── terraform ├── .terraform-version ├── 00-preface └── hello-world │ ├── .terraform.lock.hcl │ ├── README.md │ └── main.tf ├── 01-why-terraform └── web-server │ ├── .terraform.lock.hcl │ ├── README.md │ └── main.tf ├── 02-intro-to-terraform-syntax ├── one-server │ ├── .terraform.lock.hcl │ ├── README.md │ └── main.tf ├── one-webserver-with-vars │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── one-webserver │ ├── .terraform.lock.hcl │ ├── README.md │ └── main.tf └── webserver-cluster │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── 03-terraform-state ├── file-layout-example │ ├── global │ │ └── s3 │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ └── webserver-cluster │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── user-data.sh │ │ └── variables.tf └── workspaces-example │ └── one-instance │ ├── .terraform.lock.hcl │ ├── README.md │ └── main.tf ├── 04-terraform-module ├── module-example │ ├── modules │ │ └── services │ │ │ └── webserver-cluster │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ ├── user-data.sh │ │ │ └── variables.tf │ ├── prod │ │ ├── data-stores │ │ │ └── mysql │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── services │ │ │ └── webserver-cluster │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ └── webserver-cluster │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf └── multi-repo-example │ └── live │ ├── prod │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── services │ │ └── webserver-cluster │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── stage │ ├── data-stores │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── services │ └── webserver-cluster │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── 05-tips-and-tricks ├── loops-and-if-statements │ ├── live │ │ ├── global │ │ │ ├── existing-iam-user │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ └── main.tf │ │ │ ├── for-expressions │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ └── main.tf │ │ │ ├── one-iam-user │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ └── variables.tf │ │ │ ├── string-directives │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ └── main.tf │ │ │ ├── three-iam-users-for-each │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ ├── three-iam-users-increment-name │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ └── variables.tf │ │ │ ├── three-iam-users-module-count │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ ├── three-iam-users-module-for-each │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── three-iam-users-unique-names │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ ├── prod │ │ │ ├── data-stores │ │ │ │ └── mysql │ │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ │ ├── README.md │ │ │ │ │ ├── main.tf │ │ │ │ │ ├── outputs.tf │ │ │ │ │ └── variables.tf │ │ │ └── services │ │ │ │ └── webserver-cluster │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── stage │ │ │ ├── data-stores │ │ │ └── mysql │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ │ └── services │ │ │ ├── multiple-ec2-instances │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ └── main.tf │ │ │ └── webserver-cluster │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── modules │ │ ├── landing-zone │ │ └── iam-user │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ └── webserver-cluster │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── user-data-new.sh │ │ ├── user-data.sh │ │ └── variables.tf └── zero-downtime-deployment │ ├── live │ ├── global │ │ └── moved-example │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ └── variables.tf │ ├── prod │ │ ├── data-stores │ │ │ └── mysql │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── services │ │ │ ├── webserver-cluster-instance-refresh │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ │ └── webserver-cluster │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ ├── webserver-cluster-instance-refresh │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ │ └── webserver-cluster │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── modules │ └── services │ ├── webserver-cluster-instance-refresh │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── user-data.sh │ └── variables.tf │ └── webserver-cluster │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── user-data.sh │ └── variables.tf ├── 06-managing-secrets-with-terraform ├── ec2-iam-role │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── github-actions-oidc │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── kms-cmk │ ├── .terraform.lock.hcl │ ├── README.md │ ├── db-creds.yml │ ├── encrypt.sh │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── mysql-aws-secrets-mgr │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── mysql-kms │ ├── .terraform.lock.hcl │ ├── README.md │ ├── db-creds.yml.encrypted │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── mysql-vars │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── 07-working-with-multiple-providers ├── examples │ ├── kubernetes-eks │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── kubernetes-local │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ └── outputs.tf │ ├── multi-account-module │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── multi-account-root │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── multi-region │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ └── outputs.tf │ └── single-account │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ └── main.tf ├── live │ ├── prod │ │ └── data-stores │ │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ └── data-stores │ │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf └── modules │ ├── data-stores │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── multi-account │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ └── outputs.tf │ └── services │ ├── eks-cluster │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf │ └── k8s-app │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── 08-production-grade-infrastructure ├── more-than-terraform │ ├── external-data-source │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ ├── local-exec-provisioner │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ ├── null-resource │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ └── remote-exec-provisioner │ │ ├── .terraform.lock.hcl │ │ └── main.tf └── small-modules │ ├── examples │ ├── alb │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── asg │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── live │ ├── prod │ │ ├── data-stores │ │ │ └── mysql │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── services │ │ │ └── hello-world-app │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ └── hello-world-app │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── modules │ ├── cluster │ └── asg-rolling-deploy │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── data-stores │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── networking │ └── alb │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── services │ └── hello-world-app │ ├── .terraform.lock.hcl │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── user-data.sh │ └── variables.tf ├── 09-testing-terraform-code ├── examples │ ├── alb │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── dependencies.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── asg │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── dependencies.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── hello-world-app │ │ ├── standalone │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── with-mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── opa │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ └── variables.tf ├── live │ ├── prod │ │ ├── data-stores │ │ │ └── mysql │ │ │ │ ├── .terraform.lock.hcl │ │ │ │ ├── README.md │ │ │ │ ├── main.tf │ │ │ │ ├── outputs.tf │ │ │ │ └── variables.tf │ │ └── services │ │ │ └── hello-world-app │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── stage │ │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ │ └── services │ │ └── hello-world-app │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── modules │ ├── cluster │ │ └── asg-rolling-deploy │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ ├── networking │ │ └── alb │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ ├── main.tf │ │ │ ├── outputs.tf │ │ │ └── variables.tf │ └── services │ │ └── hello-world-app │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── dependencies.tf │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── user-data.sh │ │ └── variables.tf └── test │ ├── README.md │ ├── alb_example_test.go │ ├── asg_example_test.go │ ├── go.mod │ ├── go.sum │ ├── go_sanity_test.go │ ├── hello_world_app_example_test.go │ ├── hello_world_integration_test.go │ ├── mysql_example_test.go │ ├── opa_test.go │ └── test_helpers.go └── 10-terraform-team ├── examples ├── alb │ ├── .terraform.lock.hcl │ ├── README.md │ ├── dependencies.tf │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── asg │ ├── .terraform.lock.hcl │ ├── README.md │ ├── dependencies.tf │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── live ├── prod │ ├── .terraform.lock.hcl │ ├── data-stores │ │ └── mysql │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ └── terragrunt.hcl │ ├── services │ │ └── hello-world-app │ │ │ ├── .terraform.lock.hcl │ │ │ ├── README.md │ │ │ └── terragrunt.hcl │ └── terragrunt.hcl └── stage │ ├── .terraform.lock.hcl │ ├── data-stores │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ └── terragrunt.hcl │ ├── services │ ├── conflict-anna │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ ├── conflict-bill │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ ├── conflict-original │ │ ├── .terraform.lock.hcl │ │ └── main.tf │ └── hello-world-app │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ └── terragrunt.hcl │ └── terragrunt.hcl ├── modules ├── cluster │ └── asg-rolling-deploy │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── data-stores │ └── mysql │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── networking │ └── alb │ │ ├── .terraform.lock.hcl │ │ ├── README.md │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf └── services │ └── hello-world-app │ ├── .terraform.lock.hcl │ ├── README.md │ ├── dependencies.tf │ ├── main.tf │ ├── outputs.tf │ ├── user-data.sh │ └── variables.tf └── test ├── README.md ├── alb_example_test.go ├── asg_example_test.go ├── go.mod ├── go.sum ├── go_sanity_test.go ├── hello_world_integration_test.go ├── mysql_example_test.go └── test_helpers.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.tfstate 2 | *.tfstate.backup 3 | .terraform 4 | *.iml -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yevgeniy Brikman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /code/ansible/01-why-terraform/hosts: -------------------------------------------------------------------------------- 1 | [webservers] 2 | 11.11.11.11 3 | 11.11.11.12 4 | 11.11.11.13 5 | 11.11.11.14 6 | 11.11.11.15 7 | -------------------------------------------------------------------------------- /code/ansible/01-why-terraform/playbook.yml: -------------------------------------------------------------------------------- 1 | - hosts: webservers 2 | roles: 3 | - webserver -------------------------------------------------------------------------------- /code/ansible/01-why-terraform/procedural-examples.yml: -------------------------------------------------------------------------------- 1 | - ec2: 2 | count: 10 3 | image: ami-0fb653ca2d3203ac1 4 | instance_type: t2.micro 5 | 6 | - ec2: 7 | count: 5 8 | image: ami-0fb653ca2d3203ac1 9 | instance_type: t2.micro 10 | -------------------------------------------------------------------------------- /code/ansible/01-why-terraform/webserver.yml: -------------------------------------------------------------------------------- 1 | - name: Update the apt-get cache 2 | apt: 3 | update_cache: yes 4 | 5 | - name: Install PHP 6 | apt: 7 | name: php 8 | 9 | - name: Install Apache 10 | apt: 11 | name: apache2 12 | 13 | - name: Copy the code from the repository 14 | git: repo=https://github.com/brikis98/php-app.git dest=/var/www/html/app 15 | 16 | - name: Start Apache 17 | service: name=apache2 state=started enabled=yes -------------------------------------------------------------------------------- /code/bash/01-why-terraform/README.md: -------------------------------------------------------------------------------- 1 | # setup-webserver.sh 2 | 3 | This folder contains an example of a Bash script that can be used to install and Apache, PHP, and a sample PHP app 4 | on an Ubuntu server. 5 | 6 | For more info, please see Chapter 1, "Why Terraform", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Pre-requisites 10 | 11 | This script should be run on an Ubuntu server. 12 | 13 | ## Quick start 14 | 15 | ``` 16 | ./setup-webserver.sh 17 | ``` -------------------------------------------------------------------------------- /code/bash/01-why-terraform/setup-webserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # A script that configures a web server 3 | 4 | set -e 5 | 6 | # Update the apt-get cache 7 | sudo apt-get update 8 | 9 | # Install PHP and Apache 10 | sudo apt-get install -y php apache2 11 | 12 | # Copy the code from the repository 13 | sudo git clone https://github.com/brikis98/php-app.git /var/www/html/app 14 | 15 | # Start Apache 16 | sudo service apache2 start 17 | -------------------------------------------------------------------------------- /code/bash/02-intro-to-terraform-syntax/README.md: -------------------------------------------------------------------------------- 1 | # run-webserver.sh 2 | 3 | This folder contains an example of a Bash script that can be used to start a web server that listens on port 8080 4 | and returns the text "Hello, World" for the URL `/`. 5 | 6 | For more info, please see Chapter 2, "Getting started with Terraform", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Pre-requisites 10 | 11 | You must have [busybox](https://busybox.net/) installed on your computer. 12 | 13 | ## Quick start 14 | 15 | ``` 16 | ./run-webserver.sh 17 | curl http://localhost:8080 18 | ``` -------------------------------------------------------------------------------- /code/bash/02-intro-to-terraform-syntax/run-webserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Hello, World" > index.html 3 | nohup busybox httpd -f -p 8080 & 4 | -------------------------------------------------------------------------------- /code/bash/03-terraform-state/README.md: -------------------------------------------------------------------------------- 1 | # bash-unit-test-example.sh 2 | 3 | This folder shows how extracting your [User Data 4 | script](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) into a separate file allows you to write 5 | unit tests for that file. The folder contains a sample `user-data.sh` Bash script, which fires up a web server on port 6 | 8080, plus a simple unit test for it, `bash-unit-test-example.sh`, that runs that server and checks that it works. 7 | 8 | For more info, please see Chapter 3, "How to Manage Terraform State", of 9 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 10 | 11 | ## Pre-requisites 12 | 13 | You must have [busybox](https://busybox.net/) installed on your computer. 14 | 15 | ## Quick start 16 | 17 | ``` 18 | ./bash-unit-test-example.sh 19 | ``` -------------------------------------------------------------------------------- /code/bash/03-terraform-state/bash-unit-test-example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export db_address=12.34.56.78 4 | export db_port=5555 5 | export server_port=8888 6 | 7 | ./user-data.sh 8 | 9 | output=$(curl "http://localhost:$server_port") 10 | 11 | if [[ $output == *"Hello, World"* ]]; then 12 | echo "Success! Got expected text from server." 13 | else 14 | echo "Error. Did not get back expected text 'Hello, World'." 15 | fi 16 | -------------------------------------------------------------------------------- /code/bash/03-terraform-state/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <Hello, World 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/opa/09-testing-terraform-code/README.md: -------------------------------------------------------------------------------- 1 | # OPA example 2 | 3 | This folder contains example [Open Policy Agent (OPA)](https://www.openpolicyagent.org/) policy that enforces all 4 | resources include a specific tag. 5 | 6 | For more info, please see Chapter 9, "How to test Terraform code", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Quick start 10 | 11 | Please see the README in the companion Terraform module at 12 | [09-testing-terraform-code/examples/opa](../../terraform/09-testing-terraform-code/examples/opa) for instructions on 13 | how to use this policy to test that module. 14 | 15 | -------------------------------------------------------------------------------- /code/opa/09-testing-terraform-code/enforce_tagging.rego: -------------------------------------------------------------------------------- 1 | package terraform 2 | 3 | allow { 4 | resource_change := input.resource_changes[_] 5 | resource_change.change.after.tags["ManagedBy"] 6 | } -------------------------------------------------------------------------------- /code/packer/01-why-terraform/README.md: -------------------------------------------------------------------------------- 1 | # Packer example 2 | 3 | This folder shows an example [Packer](https://www.packer.io/) template that can be used to create an [Amazon Machine 4 | Image (AMI)](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html) of an Ubuntu server with Apache, PHP, and 5 | a sample PHP app installed. 6 | 7 | For more info, please see Chapter 1, "Why Terraform", of 8 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 9 | 10 | ## Pre-requisites 11 | 12 | * You must have [Packer](https://www.packer.io/) installed on your computer. 13 | * You must have an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 14 | 15 | ## Quick start 16 | 17 | **Please note that this example will deploy real resources into your AWS account. We have made every effort to ensure 18 | all the resources qualify for the [AWS Free Tier](https://aws.amazon.com/free/), but we are not responsible for any 19 | charges you may incur.** 20 | 21 | Configure your [AWS access 22 | keys](http://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys) as 23 | environment variables: 24 | 25 | ``` 26 | export AWS_ACCESS_KEY_ID=(your access key id) 27 | export AWS_SECRET_ACCESS_KEY=(your secret access key) 28 | ``` 29 | 30 | To build the AMI: 31 | 32 | ``` 33 | packer build webserver.json 34 | ``` -------------------------------------------------------------------------------- /code/packer/01-why-terraform/webserver.json: -------------------------------------------------------------------------------- 1 | { 2 | "builders": [{ 3 | "ami_name": "packer-example-{{isotime | clean_resource_name}}", 4 | "instance_type": "t2.micro", 5 | "region": "us-east-2", 6 | "type": "amazon-ebs", 7 | "source_ami": "ami-0fb653ca2d3203ac1", 8 | "ssh_username": "ubuntu" 9 | }], 10 | "provisioners": [{ 11 | "type": "shell", 12 | "inline": [ 13 | "sudo apt-get update", 14 | "sudo apt-get install -y php apache2", 15 | "sudo git clone https://github.com/brikis98/php-app.git /var/www/html/app" 16 | ], 17 | "environment_vars": [ 18 | "DEBIAN_FRONTEND=noninteractive" 19 | ], 20 | "pause_before": "60s" 21 | }] 22 | } -------------------------------------------------------------------------------- /code/ruby/04-terraform-modules/function-example.rb: -------------------------------------------------------------------------------- 1 | # Define the function in one place 2 | def example_function() 3 | puts "Hello, World" 4 | end 5 | 6 | # Use the function in multiple other places 7 | example_function() 8 | -------------------------------------------------------------------------------- /code/ruby/04-terraform-modules/function-with-input-and-output-params-example.rb: -------------------------------------------------------------------------------- 1 | # A function that returns a value 2 | def example_function(param1, param2) 3 | return "Hello, #{param1} #{param2}" 4 | end 5 | 6 | # Call the function and get the return value 7 | return_value = example_function("foo", "bar") 8 | -------------------------------------------------------------------------------- /code/ruby/04-terraform-modules/function-with-input-params-example.rb: -------------------------------------------------------------------------------- 1 | # A function with two input parameters 2 | def example_function(param1, param2) 3 | puts "Hello, #{param1} #{param2}" 4 | end 5 | 6 | # Pass two input parameters to the function 7 | example_function("foo", "bar") 8 | -------------------------------------------------------------------------------- /code/ruby/09-testing-terraform-code/web-server-basic.rb: -------------------------------------------------------------------------------- 1 | require 'webrick' 2 | 3 | class WebServer < WEBrick::HTTPServlet::AbstractServlet 4 | def do_GET(request, response) 5 | case request.path 6 | when "/" 7 | response.status = 200 8 | response['Content-Type'] = 'text/plain' 9 | response.body = 'Hello, World' 10 | when "/api" 11 | response.status = 201 12 | response['Content-Type'] = 'application/json' 13 | response.body = '{"foo":"bar"}' 14 | else 15 | response.status = 404 16 | response['Content-Type'] = 'text/plain' 17 | response.body = 'Not Found' 18 | end 19 | end 20 | end 21 | 22 | # This will only run if this script was called directly from the CLI, but 23 | # not if it was required from another file 24 | if __FILE__ == $0 25 | # Run the server on localhost at port 8000 26 | server = WEBrick::HTTPServer.new :Port => 8000 27 | server.mount '/', WebServer 28 | 29 | # Shut down the server on CTRL+C 30 | trap 'INT' do server.shutdown end 31 | 32 | # Start the server 33 | server.start 34 | end 35 | -------------------------------------------------------------------------------- /code/ruby/09-testing-terraform-code/web-server-full.rb: -------------------------------------------------------------------------------- 1 | require 'webrick' 2 | 3 | class WebServer < WEBrick::HTTPServlet::AbstractServlet 4 | def do_GET(request, response) 5 | handlers = Handlers.new 6 | status_code, content_type, body = handlers.handle(request.path) 7 | 8 | response.status = status_code 9 | response['Content-Type'] = content_type 10 | response.body = body 11 | end 12 | end 13 | 14 | class Handlers 15 | def handle(path) 16 | case path 17 | when "/" 18 | [200, 'text/plain', 'Hello, World'] 19 | when "/api" 20 | [201, 'application/json', '{"foo":"bar"}'] 21 | else 22 | [404, 'text/plain', 'Not Found'] 23 | end 24 | end 25 | end 26 | 27 | # This will only run if this script was called directly from the CLI, but 28 | # not if it was required from another file 29 | if __FILE__ == $0 30 | # Run the server on localhost at port 8000 31 | server = WEBrick::HTTPServer.new :Port => 8000 32 | server.mount '/', WebServer 33 | 34 | # Shut down the server on CTRL+C 35 | trap 'INT' do server.shutdown end 36 | 37 | # Start the server 38 | server.start 39 | end 40 | -------------------------------------------------------------------------------- /code/terraform/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.2.3 -------------------------------------------------------------------------------- /code/terraform/00-preface/hello-world/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "example" { 17 | ami = "ami-0fb653ca2d3203ac1" 18 | instance_type = "t2.micro" 19 | } 20 | 21 | -------------------------------------------------------------------------------- /code/terraform/01-why-terraform/web-server/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "app" { 17 | instance_type = "t2.micro" 18 | availability_zone = "us-east-2a" 19 | ami = "ami-0fb653ca2d3203ac1" 20 | 21 | user_data = <<-EOF 22 | #!/bin/bash 23 | sudo service apache2 start 24 | EOF 25 | } 26 | 27 | -------------------------------------------------------------------------------- /code/terraform/02-intro-to-terraform-syntax/one-server/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "example" { 17 | ami = "ami-0fb653ca2d3203ac1" 18 | instance_type = "t2.micro" 19 | 20 | tags = { 21 | Name = "terraform-example" 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /code/terraform/02-intro-to-terraform-syntax/one-webserver-with-vars/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "example" { 17 | ami = "ami-0fb653ca2d3203ac1" 18 | instance_type = "t2.micro" 19 | vpc_security_group_ids = [aws_security_group.instance.id] 20 | 21 | user_data = <<-EOF 22 | #!/bin/bash 23 | echo "Hello, World" > index.html 24 | nohup busybox httpd -f -p ${var.server_port} & 25 | EOF 26 | 27 | user_data_replace_on_change = true 28 | 29 | tags = { 30 | Name = "terraform-example" 31 | } 32 | } 33 | 34 | resource "aws_security_group" "instance" { 35 | 36 | name = var.security_group_name 37 | 38 | ingress { 39 | from_port = var.server_port 40 | to_port = var.server_port 41 | protocol = "tcp" 42 | cidr_blocks = ["0.0.0.0/0"] 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /code/terraform/02-intro-to-terraform-syntax/one-webserver-with-vars/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_ip" { 2 | value = aws_instance.example.public_ip 3 | description = "The public IP address of the web server" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/02-intro-to-terraform-syntax/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/02-intro-to-terraform-syntax/webserver-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | variable "server_port" { 2 | description = "The port the server will use for HTTP requests" 3 | type = number 4 | default = 8080 5 | } 6 | 7 | variable "alb_name" { 8 | description = "The name of the ALB" 9 | type = string 10 | default = "terraform-asg-example" 11 | } 12 | 13 | variable "instance_security_group_name" { 14 | description = "The name of the security group for the EC2 Instances" 15 | type = string 16 | default = "terraform-example-instance" 17 | } 18 | 19 | variable "alb_security_group_name" { 20 | description = "The name of the security group for the ALB" 21 | type = string 22 | default = "terraform-example-alb" 23 | } 24 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/global/s3/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_arn" { 2 | value = aws_s3_bucket.terraform_state.arn 3 | description = "The ARN of the S3 bucket" 4 | } 5 | 6 | output "dynamodb_table_name" { 7 | value = aws_dynamodb_table.terraform_locks.name 8 | description = "The name of the DynamoDB table" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/global/s3/variables.tf: -------------------------------------------------------------------------------- 1 | variable "bucket_name" { 2 | description = "The name of the S3 bucket. Must be globally unique." 3 | type = string 4 | } 5 | 6 | variable "table_name" { 7 | description = "The name of the DynamoDB table. Must be unique in this AWS account." 8 | type = string 9 | } -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | 13 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 14 | # manually, uncomment and fill in the config below. 15 | 16 | # bucket = "" 17 | # key = "/terraform.tfstate" 18 | # region = "us-east-2" 19 | # dynamodb_table = "" 20 | # encrypt = true 21 | 22 | } 23 | } 24 | 25 | provider "aws" { 26 | region = "us-east-2" 27 | } 28 | 29 | resource "aws_db_instance" "example" { 30 | identifier_prefix = "terraform-up-and-running" 31 | engine = "mysql" 32 | allocated_storage = 10 33 | instance_class = "db.t2.micro" 34 | skip_final_snapshot = true 35 | 36 | db_name = var.db_name 37 | 38 | username = var.db_username 39 | password = var.db_password 40 | } 41 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/stage/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/file-layout-example/stage/services/webserver-cluster/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <Hello, World 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/03-terraform-state/workspaces-example/one-instance/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | 13 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 14 | # manually, uncomment and fill in the config below. 15 | 16 | # bucket = "" 17 | # key = "/terraform.tfstate" 18 | # region = "us-east-2" 19 | # dynamodb_table = "" 20 | # encrypt = true 21 | 22 | } 23 | } 24 | 25 | provider "aws" { 26 | region = "us-east-2" 27 | } 28 | 29 | resource "aws_instance" "example" { 30 | ami = "ami-0fb653ca2d3203ac1" 31 | 32 | instance_type = terraform.workspace == "default" ? "t2.medium" : "t2.micro" 33 | 34 | } 35 | 36 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/modules/services/webserver-cluster/README.md: -------------------------------------------------------------------------------- 1 | # Web server cluster module example 2 | 3 | This folder contains example [Terraform](https://www.terraform.io/) configuration that define a module for deploying a 4 | cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) and [Auto 5 | Scaling](https://aws.amazon.com/autoscaling/)) and a load balancer (using 6 | [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 7 | The load balancer listens on port 80 and returns the text "Hello, World" for the `/` URL. 8 | 9 | For more info, please see Chapter 4, "How to Create Reusable Infrastructure with Terraform Modules", of 10 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 11 | 12 | ## Quick start 13 | 14 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 15 | configurations. See [stage/services/webserver-cluster](../../../stage/services/webserver-cluster) and 16 | [prod/services/webserver-cluster](../../../prod/services/webserver-cluster) for examples. -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/modules/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = aws_autoscaling_group.example.name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ID of the Security Group attached to the load balancer" 14 | } 15 | 16 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/modules/services/webserver-cluster/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <Hello, World 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/prod/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/prod/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/prod/services/webserver-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_remote_state_bucket" { 7 | description = "The name of the S3 bucket used for the database's remote state storage" 8 | type = string 9 | } 10 | 11 | variable "db_remote_state_key" { 12 | description = "The name of the key in the S3 bucket used for the database's remote state storage" 13 | type = string 14 | } 15 | 16 | # --------------------------------------------------------------------------------------------------------------------- 17 | # OPTIONAL PARAMETERS 18 | # These parameters have reasonable defaults. 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | 21 | variable "cluster_name" { 22 | description = "The name to use to namespace all the resources in the cluster" 23 | type = string 24 | default = "webservers-prod" 25 | } 26 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/services/webserver-cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | source = "../../../modules/services/webserver-cluster" 18 | 19 | # (parameters hidden for clarity) 20 | 21 | cluster_name = var.cluster_name 22 | db_remote_state_bucket = var.db_remote_state_bucket 23 | db_remote_state_key = var.db_remote_state_key 24 | 25 | instance_type = "t2.micro" 26 | min_size = 2 27 | max_size = 2 28 | } 29 | 30 | resource "aws_security_group_rule" "allow_testing_inbound" { 31 | type = "ingress" 32 | security_group_id = module.webserver_cluster.alb_security_group_id 33 | 34 | from_port = 12345 35 | to_port = 12345 36 | protocol = "tcp" 37 | cidr_blocks = ["0.0.0.0/0"] 38 | } 39 | 40 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/module-example/stage/services/webserver-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_remote_state_bucket" { 7 | description = "The name of the S3 bucket used for the database's remote state storage" 8 | type = string 9 | } 10 | 11 | variable "db_remote_state_key" { 12 | description = "The name of the key in the S3 bucket used for the database's remote state storage" 13 | type = string 14 | } 15 | 16 | # --------------------------------------------------------------------------------------------------------------------- 17 | # OPTIONAL PARAMETERS 18 | # These parameters have reasonable defaults. 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | 21 | variable "cluster_name" { 22 | description = "The name to use to namespace all the resources in the cluster" 23 | type = string 24 | default = "webservers-stage" 25 | } 26 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/prod/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/prod/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/prod/services/webserver-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_remote_state_bucket" { 7 | description = "The name of the S3 bucket used for the database's remote state storage" 8 | type = string 9 | } 10 | 11 | variable "db_remote_state_key" { 12 | description = "The name of the key in the S3 bucket used for the database's remote state storage" 13 | type = string 14 | } 15 | 16 | # --------------------------------------------------------------------------------------------------------------------- 17 | # OPTIONAL PARAMETERS 18 | # These parameters have reasonable defaults. 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | 21 | variable "cluster_name" { 22 | description = "The name to use to namespace all the resources in the cluster" 23 | type = string 24 | default = "webservers-prod" 25 | } 26 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/services/webserver-cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | 18 | # Since the terraform-up-and-running-code repo is open source, we're using an HTTPS URL here. If it was a private 19 | # repo, we'd instead use an SSH URL (git@github.com:brikis98/terraform-up-and-running-code.git) to leverage SSH auth 20 | source = "github.com/brikis98/terraform-up-and-running-code//code/terraform/04-terraform-module/module-example/modules/services/webserver-cluster?ref=v0.3.0" 21 | 22 | cluster_name = var.cluster_name 23 | db_remote_state_bucket = var.db_remote_state_bucket 24 | db_remote_state_key = var.db_remote_state_key 25 | 26 | instance_type = "t2.micro" 27 | min_size = 2 28 | max_size = 2 29 | } 30 | 31 | resource "aws_security_group_rule" "allow_testing_inbound" { 32 | type = "ingress" 33 | security_group_id = module.webserver_cluster.alb_security_group_id 34 | 35 | from_port = 12345 36 | to_port = 12345 37 | protocol = "tcp" 38 | cidr_blocks = ["0.0.0.0/0"] 39 | } 40 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/04-terraform-module/multi-repo-example/live/stage/services/webserver-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_remote_state_bucket" { 7 | description = "The name of the S3 bucket used for the database's remote state storage" 8 | type = string 9 | } 10 | 11 | variable "db_remote_state_key" { 12 | description = "The name of the key in the S3 bucket used for the database's remote state storage" 13 | type = string 14 | } 15 | 16 | # --------------------------------------------------------------------------------------------------------------------- 17 | # OPTIONAL PARAMETERS 18 | # These parameters have reasonable defaults. 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | 21 | variable "cluster_name" { 22 | description = "The name to use to namespace all the resources in the cluster" 23 | type = string 24 | default = "webservers-stage" 25 | } 26 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/existing-iam-user/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_iam_user" "existing_user" { 17 | # Make sure to update this to your own user name! 18 | name = "yevgeniy.brikman" 19 | } 20 | 21 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/for-expressions/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/for-expressions/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | } 4 | 5 | variable "names" { 6 | description = "A list of names" 7 | type = list(string) 8 | default = ["neo", "trinity", "morpheus"] 9 | } 10 | 11 | output "upper_names" { 12 | value = [for name in var.names : upper(name)] 13 | } 14 | 15 | output "short_upper_names" { 16 | value = [for name in var.names : upper(name) if length(name) < 5] 17 | } 18 | 19 | variable "hero_thousand_faces" { 20 | description = "map" 21 | type = map(string) 22 | default = { 23 | neo = "hero" 24 | trinity = "love interest" 25 | morpheus = "mentor" 26 | } 27 | } 28 | 29 | output "bios" { 30 | value = [for name, role in var.hero_thousand_faces : "${name} is the ${role}"] 31 | } 32 | 33 | output "upper_roles" { 34 | value = {for name, role in var.hero_thousand_faces : upper(name) => upper(role)} 35 | } 36 | 37 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/one-iam-user/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_iam_user" "example" { 17 | 18 | name = var.user_name 19 | 20 | } 21 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/one-iam-user/variables.tf: -------------------------------------------------------------------------------- 1 | variable "user_name" { 2 | description = "The user name to use" 3 | type = string 4 | default = "neo" 5 | } -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/string-directives/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/live/global/string-directives/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | } 4 | 5 | variable "names" { 6 | description = "Names to render" 7 | type = list(string) 8 | default = ["neo", "trinity", "morpheus"] 9 | } 10 | 11 | output "for_directive" { 12 | value = "%{ for name in var.names }${name}, %{ endfor }" 13 | } 14 | 15 | output "for_directive_index" { 16 | value = "%{ for i, name in var.names }(${i}) ${name}, %{ endfor }" 17 | } 18 | 19 | output "for_directive_index_if" { 20 | value = < index.html 4 | nohup busybox httpd -f -p ${server_port} & 5 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/loops-and-if-statements/modules/services/webserver-cluster/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <Hello, World 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/global/moved-example/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | # This was the old identifier for the security group. Below is the same security group, but with the new identifier. 17 | # resource "aws_security_group" "instance" { 18 | # name = var.security_group_name 19 | # } 20 | 21 | resource "aws_security_group" "cluster_instance" { 22 | name = var.security_group_name 23 | } 24 | 25 | # Automatically update state to handle the security group's identifier being changed 26 | moved { 27 | from = aws_security_group.instance 28 | to = aws_security_group.cluster_instance 29 | } 30 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/global/moved-example/variables.tf: -------------------------------------------------------------------------------- 1 | variable "security_group_name" { 2 | description = "The name to use for the Security Group" 3 | type = string 4 | default = "moved-example-security-group" 5 | } -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/services/webserver-cluster-instance-refresh/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | source = "../../../../modules/services/webserver-cluster-instance-refresh" 18 | 19 | ami = "ami-0fb653ca2d3203ac1" 20 | 21 | server_text = var.server_text 22 | 23 | cluster_name = var.cluster_name 24 | db_remote_state_bucket = var.db_remote_state_bucket 25 | db_remote_state_key = var.db_remote_state_key 26 | 27 | instance_type = "m4.large" 28 | min_size = 2 29 | max_size = 10 30 | enable_autoscaling = true 31 | } 32 | 33 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/services/webserver-cluster-instance-refresh/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/services/webserver-cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | source = "../../../../modules/services/webserver-cluster" 18 | 19 | ami = "ami-0fb653ca2d3203ac1" 20 | 21 | server_text = var.server_text 22 | 23 | cluster_name = var.cluster_name 24 | db_remote_state_bucket = var.db_remote_state_bucket 25 | db_remote_state_key = var.db_remote_state_key 26 | 27 | instance_type = "m4.large" 28 | min_size = 2 29 | max_size = 10 30 | enable_autoscaling = true 31 | } 32 | 33 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/prod/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | db_name = var.db_name 33 | username = var.db_username 34 | password = var.db_password 35 | skip_final_snapshot = true 36 | } 37 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/services/webserver-cluster-instance-refresh/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | source = "../../../../modules/services/webserver-cluster-instance-refresh" 18 | 19 | ami = "ami-0fb653ca2d3203ac1" 20 | 21 | server_text = var.server_text 22 | 23 | cluster_name = var.cluster_name 24 | db_remote_state_bucket = var.db_remote_state_bucket 25 | db_remote_state_key = var.db_remote_state_key 26 | 27 | instance_type = "t2.micro" 28 | min_size = 2 29 | max_size = 2 30 | enable_autoscaling = false 31 | } 32 | 33 | resource "aws_security_group_rule" "allow_testing_inbound" { 34 | type = "ingress" 35 | security_group_id = module.webserver_cluster.alb_security_group_id 36 | 37 | from_port = 12345 38 | to_port = 12345 39 | protocol = "tcp" 40 | cidr_blocks = ["0.0.0.0/0"] 41 | } 42 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/services/webserver-cluster-instance-refresh/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/services/webserver-cluster/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "webserver_cluster" { 17 | source = "../../../../modules/services/webserver-cluster" 18 | 19 | ami = "ami-0fb653ca2d3203ac1" 20 | 21 | server_text = var.server_text 22 | 23 | cluster_name = var.cluster_name 24 | db_remote_state_bucket = var.db_remote_state_bucket 25 | db_remote_state_key = var.db_remote_state_key 26 | 27 | instance_type = "t2.micro" 28 | min_size = 2 29 | max_size = 2 30 | enable_autoscaling = false 31 | } 32 | 33 | resource "aws_security_group_rule" "allow_testing_inbound" { 34 | type = "ingress" 35 | security_group_id = module.webserver_cluster.alb_security_group_id 36 | 37 | from_port = 12345 38 | to_port = 12345 39 | protocol = "tcp" 40 | cidr_blocks = ["0.0.0.0/0"] 41 | } 42 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/live/stage/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.webserver_cluster.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster-instance-refresh/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = aws_autoscaling_group.example.name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ID of the Security Group attached to the load balancer" 14 | } 15 | 16 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster-instance-refresh/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <${server_text} 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster/README.md: -------------------------------------------------------------------------------- 1 | # Web server cluster module example 2 | 3 | This folder contains example [Terraform](https://www.terraform.io/) configuration that define a module for deploying a 4 | cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) and [Auto 5 | Scaling](https://aws.amazon.com/autoscaling/)) and a load balancer (using 6 | [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 7 | The load balancer listens on port 80 and returns the text "Hello, World" for the `/` URL. The Auto Scaling Group is 8 | able to do a zero-downtime deployment when you update any of it's properties using `create_before_destroy`. 9 | 10 | For more info, please see Chapter 5, "Terraform Tips & Tricks: Loops, If-Statements, Deployment, and Gotchas", of 11 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 12 | 13 | ## Quick start 14 | 15 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 16 | configurations. See [live/stage/services/webserver-cluster](../../../live/stage/services/webserver-cluster) and 17 | [live/prod/services/webserver-cluster](../../../live/prod/services/webserver-cluster) for examples. -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = aws_autoscaling_group.example.name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ID of the Security Group attached to the load balancer" 14 | } 15 | 16 | -------------------------------------------------------------------------------- /code/terraform/05-tips-and-tricks/zero-downtime-deployment/modules/services/webserver-cluster/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <${server_text} 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/ec2-iam-role/outputs.tf: -------------------------------------------------------------------------------- 1 | output "instance_id" { 2 | value = aws_instance.example.id 3 | description = "The ID of the EC2 instance" 4 | } 5 | 6 | output "instance_ip" { 7 | value = aws_instance.example.public_ip 8 | description = "The public IP of the EC2 instance" 9 | } 10 | 11 | output "iam_role_arn" { 12 | value = aws_iam_role.instance.arn 13 | description = "The ARN of the IAM role" 14 | } 15 | 16 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/ec2-iam-role/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "name" { 7 | description = "The name used to namespace all the resources created by this module" 8 | type = string 9 | default = "ec2-iam-role-example" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/github-actions-oidc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "aws_iam_github_actions_oidc_arn" { 2 | value = aws_iam_openid_connect_provider.github_actions.arn 3 | description = "The ARN of the IAM OIDC identity provider for GitHub Actions" 4 | } 5 | 6 | output "aws_iam_github_actions_oidc_url" { 7 | value = aws_iam_openid_connect_provider.github_actions.url 8 | description = "The URL of the IAM OIDC identity provider for GitHub Actions" 9 | } 10 | 11 | output "iam_role_arn" { 12 | value = aws_iam_role.instance.arn 13 | description = "The ARN of the IAM role" 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/kms-cmk/db-creds.yml: -------------------------------------------------------------------------------- 1 | # This file is used as an example of a plain text input to encryption. Since this is in plain text, it should 2 | # normally NOT be checked into version control; we're only doing it here as this file is necessary for demonstration 3 | # and testing purposes. 4 | username: admin 5 | password: password 6 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/kms-cmk/encrypt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [[ "$#" -ne 4 ]]; then 6 | echo "Usage: encrypt.sh " 7 | exit 8 | fi 9 | 10 | CMK_ID="$1" 11 | AWS_REGION="$2" 12 | INPUT_FILE="$3" 13 | OUTPUT_FILE="$4" 14 | 15 | echo "Encrypting contents of $INPUT_FILE using CMK $CMK_ID..." 16 | ciphertext=$(aws kms encrypt \ 17 | --key-id "$CMK_ID" \ 18 | --region "$AWS_REGION" \ 19 | --plaintext "fileb://$INPUT_FILE" \ 20 | --output text \ 21 | --query CiphertextBlob) 22 | 23 | echo "Writing result to $OUTPUT_FILE..." 24 | echo "$ciphertext" > "$OUTPUT_FILE" 25 | 26 | echo "Done!" 27 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/kms-cmk/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | # Create a KMS Customer Managed Key (CMK) 17 | resource "aws_kms_key" "cmk" { 18 | policy = data.aws_iam_policy_document.cmk_admin_policy.json 19 | 20 | # We set a short deletion window, as these keys are only used 21 | # for testing/learning, and we want to minimize the AWS charges 22 | deletion_window_in_days = 7 23 | } 24 | 25 | # A simple policy that makes the current IAM user the admin 26 | # of the CMK 27 | data "aws_iam_policy_document" "cmk_admin_policy" { 28 | statement { 29 | effect = "Allow" 30 | resources = ["*"] 31 | actions = ["kms:*"] 32 | principals { 33 | type = "AWS" 34 | identifiers = [data.aws_caller_identity.self.arn] 35 | } 36 | } 37 | } 38 | 39 | # Look up the details of the current user 40 | data "aws_caller_identity" "self" {} 41 | 42 | # Create an alias for the CMK 43 | resource "aws_kms_alias" "cmk" { 44 | name = "alias/${var.alias}" 45 | target_key_id = aws_kms_key.cmk.id 46 | } 47 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/kms-cmk/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cmk_arn" { 2 | value = aws_kms_key.cmk.arn 3 | description = "The ARN of the CMK" 4 | } 5 | 6 | output "cmk_alias" { 7 | value = aws_kms_alias.cmk.name 8 | description = "The alias of the CMK" 9 | } -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/kms-cmk/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alias" { 7 | description = "The alias to use for the CMK" 8 | type = string 9 | default = "kms-cmk-example" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-aws-secrets-mgr/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | data "aws_secretsmanager_secret_version" "creds" { 17 | secret_id = "db-creds" 18 | } 19 | 20 | locals { 21 | db_creds = jsondecode( 22 | data.aws_secretsmanager_secret_version.creds.secret_string 23 | ) 24 | } 25 | 26 | resource "aws_db_instance" "example" { 27 | identifier_prefix = "terraform-up-and-running" 28 | engine = "mysql" 29 | allocated_storage = 10 30 | instance_class = "db.t2.micro" 31 | skip_final_snapshot = true 32 | db_name = var.db_name 33 | 34 | # Pass the secrets to the resource 35 | username = local.db_creds.username 36 | password = local.db_creds.password 37 | } 38 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-aws-secrets-mgr/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-aws-secrets-mgr/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "The name to use for the database" 8 | type = string 9 | default = "example" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-kms/db-creds.yml.encrypted: -------------------------------------------------------------------------------- 1 | AQICAHgJ0F9KVwxlpnApoGMu/war5RjX6fgTpmn9kuca5bRjTwEZtg0ctwZ9oLN0SF5FWpb7AAABpDCCAaAGCSqGSIb3DQEHBqCCAZEwggGNAgEAMIIBhgYJKoZIhvcNAQcBMB4GCWCGSAFlAwQBLjARBAwZ4eI89+nKQsxOoWQCARCAggFXbRD+xC18jjW2J6yMtBHskZsseKRNeu1Bz/xS8FjM9XASYcGlFlmlyEIjiYG4yv5tzbqO2gHJeBg3Q0nUX+4KIZzio+FhMSbvxiP/qPqNo47Bwth9HH4HvVte84dXHWcNf3krAWyy9m8YIQ9daBHe+H8KGkWYxoxfR/zMl3Gmfyq9Hv5knAJYFY6qhETC1ZIu759m5D6lmcTd+fIuwhkdZ3IdCDBD0Cuw3VWoHETUMgy1LMfmb7q3rC4+QFnY/R3xXg8BSxjLWggtBGZguYXgBVXWkJ1X9W9eZPWmKZmwjE6etaZ9GrZPXCUzD/hUrZdE9MqPIGbKppXIkkZg7I3ifKEt53SDCOhxkDpluZiqOf9Vmucv6tXOwFmPAjojNYy8iOtwWmxAf6uCaXPvEtWdplqV/2X7a8UeuHhhML2+DGf7fc3bq9kb26gvawGK7A3en8UvjAAoZg== 2 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-kms/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | data "aws_kms_secrets" "creds" { 17 | secret { 18 | name = "db" 19 | payload = file("${path.module}/db-creds.yml.encrypted") 20 | } 21 | } 22 | 23 | locals { 24 | db_creds = yamldecode(data.aws_kms_secrets.creds.plaintext["db"]) 25 | } 26 | 27 | resource "aws_db_instance" "example" { 28 | identifier_prefix = "terraform-up-and-running" 29 | engine = "mysql" 30 | allocated_storage = 10 31 | instance_class = "db.t2.micro" 32 | skip_final_snapshot = true 33 | db_name = var.db_name 34 | 35 | # Pass the secrets to the resource 36 | username = local.db_creds.username 37 | password = local.db_creds.password 38 | } 39 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-kms/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-kms/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "The name to use for the database" 8 | type = string 9 | default = "example" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-vars/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | 15 | } 16 | 17 | resource "aws_db_instance" "example" { 18 | identifier_prefix = "terraform-up-and-running" 19 | engine = "mysql" 20 | allocated_storage = 10 21 | instance_class = "db.t2.micro" 22 | skip_final_snapshot = true 23 | db_name = var.db_name 24 | 25 | # Pass the secrets to the resource 26 | username = var.db_username 27 | password = var.db_password 28 | } 29 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-vars/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/06-managing-secrets-with-terraform/mysql-vars/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/kubernetes-eks/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_arn" { 2 | value = module.eks_cluster.cluster_arn 3 | description = "ARN of the EKS cluster" 4 | } 5 | 6 | output "cluster_endpoint" { 7 | value = module.eks_cluster.cluster_endpoint 8 | description = "Endpoint of the EKS cluster" 9 | } 10 | 11 | output "service_endpoint" { 12 | value = module.simple_webapp.service_endpoint 13 | description = "The K8S Service endpoint" 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/kubernetes-eks/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "cluster_name" { 7 | description = "The name to use for the EKS cluster and all its associated resources" 8 | type = string 9 | default = "kubernetes-example" 10 | } 11 | 12 | variable "app_name" { 13 | description = "The name to use for the app deployed into the EKS cluster" 14 | type = string 15 | default = "simple-webapp" 16 | } 17 | 18 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/kubernetes-local/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ann-felix/terraform-up-and-running-code/39a90d2af83d42891424e1f52774cc6fca09163b/code/terraform/07-working-with-multiple-providers/examples/kubernetes-local/README.md -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/kubernetes-local/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | kubernetes = { 6 | source = "hashicorp/kubernetes" 7 | version = "~> 2.0" 8 | } 9 | } 10 | } 11 | 12 | # Authenticate to Kubernetes running locally in Docker Desktop 13 | # using your local kubeconfig file and docker-desktop config 14 | provider "kubernetes" { 15 | config_path = "~/.kube/config" 16 | config_context = "docker-desktop" 17 | } 18 | 19 | # Deploy a simple web app into the EKS cluster 20 | module "simple_webapp" { 21 | source = "../../modules/services/k8s-app" 22 | 23 | name = "simple-webapp" 24 | image = "training/webapp" 25 | replicas = 2 26 | container_port = 5000 27 | 28 | environment_variables = { 29 | PROVIDER = "Terraform" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/kubernetes-local/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service_endpoint" { 2 | value = module.simple_webapp.service_endpoint 3 | description = "The K8S Service endpoint" 4 | } -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-module/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | alias = "parent" 15 | } 16 | 17 | provider "aws" { 18 | region = "us-east-2" 19 | alias = "child" 20 | 21 | assume_role { 22 | role_arn = var.child_iam_role_arn 23 | } 24 | } 25 | 26 | module "multi_account_example" { 27 | source = "../../modules/multi-account" 28 | 29 | providers = { 30 | aws.parent = aws.parent 31 | aws.child = aws.child 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-module/outputs.tf: -------------------------------------------------------------------------------- 1 | output "parent_account_id" { 2 | value = module.multi_account_example.parent_account_id 3 | description = "The ID of the parent AWS account" 4 | } 5 | 6 | output "child_account_id" { 7 | value = module.multi_account_example.child_account_id 8 | description = "The ID of the child AWS account" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-module/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "child_iam_role_arn" { 7 | description = "The ARN of an IAM role to assume in the child AWS account" 8 | type = string 9 | } -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-root/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | alias = "parent" 15 | } 16 | 17 | provider "aws" { 18 | region = "us-east-2" 19 | alias = "child" 20 | 21 | assume_role { 22 | role_arn = var.child_iam_role_arn 23 | } 24 | } 25 | 26 | data "aws_caller_identity" "parent" { 27 | provider = aws.parent 28 | } 29 | 30 | data "aws_caller_identity" "child" { 31 | provider = aws.child 32 | } 33 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-root/outputs.tf: -------------------------------------------------------------------------------- 1 | output "parent_account_id" { 2 | value = data.aws_caller_identity.parent.account_id 3 | description = "The ID of the parent AWS account" 4 | } 5 | 6 | output "child_account_id" { 7 | value = data.aws_caller_identity.child.account_id 8 | description = "The ID of the child AWS account" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-account-root/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "child_iam_role_arn" { 7 | description = "The ARN of an IAM role to assume in the child AWS account" 8 | type = string 9 | } -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/multi-region/outputs.tf: -------------------------------------------------------------------------------- 1 | output "region_1" { 2 | value = data.aws_region.region_1.name 3 | description = "The name of the first region" 4 | } 5 | 6 | output "region_2" { 7 | value = data.aws_region.region_2.name 8 | description = "The name of the second region" 9 | } 10 | 11 | output "instance_region_1_az" { 12 | value = aws_instance.region_1.availability_zone 13 | description = "The AZ where the instance in the first region deployed" 14 | } 15 | 16 | output "instance_region_2_az" { 17 | value = aws_instance.region_2.availability_zone 18 | description = "The AZ where the instance in the second region deployed" 19 | } 20 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/examples/single-account/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/live/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "primary_address" { 2 | value = module.mysql_primary.address 3 | description = "Connect to the primary database at this endpoint" 4 | } 5 | 6 | output "primary_port" { 7 | value = module.mysql_primary.port 8 | description = "The port the primary database is listening on" 9 | } 10 | 11 | output "primary_arn" { 12 | value = module.mysql_primary.arn 13 | description = "The ARN of the primary database" 14 | } 15 | 16 | output "replica_address" { 17 | value = module.mysql_replica.address 18 | description = "Connect to the replica database at this endpoint" 19 | } 20 | 21 | output "replica_port" { 22 | value = module.mysql_replica.port 23 | description = "The port the replica database is listening on" 24 | } 25 | 26 | output "replica_arn" { 27 | value = module.mysql_replica.arn 28 | description = "The ARN of the replica database" 29 | } 30 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/live/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/live/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | module "mysql" { 28 | source = "../../../../modules/data-stores/mysql" 29 | 30 | db_name = var.db_name 31 | db_username = var.db_username 32 | db_password = var.db_password 33 | } 34 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/live/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | value = module.mysql.arn 3 | description = "The ARN of the database" 4 | } 5 | 6 | output "address" { 7 | value = module.mysql.address 8 | description = "Connect to the database at this endpoint" 9 | } 10 | 11 | output "port" { 12 | value = module.mysql.port 13 | description = "The port the database is listening on" 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/live/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/data-stores/mysql/README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys a MySQL database 4 | (using [RDS](https://aws.amazon.com/rds/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 5 | 6 | For more info, please see Chapter 7, "Working with Multiple Providers", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Quick start 10 | 11 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 12 | configurations. See [live/stage/data-stores/mysql](../../../live/stage/data-stores/mysql) and 13 | [live/prod/data-stores/mysql](../../../live/prod/data-stores/mysql) for examples. -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | resource "aws_db_instance" "example" { 13 | identifier_prefix = "terraform-up-and-running" 14 | allocated_storage = 10 15 | instance_class = "db.t2.micro" 16 | skip_final_snapshot = true 17 | 18 | # Enable backups 19 | backup_retention_period = var.backup_retention_period 20 | 21 | # If specified, this DB will be a replica 22 | replicate_source_db = var.replicate_source_db 23 | 24 | # Only set these params if replicate_source_db is not set 25 | engine = var.replicate_source_db == null ? "mysql" : null 26 | db_name = var.replicate_source_db == null ? var.db_name : null 27 | username = var.replicate_source_db == null ? var.db_username : null 28 | password = var.replicate_source_db == null ? var.db_password : null 29 | } 30 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | 11 | output "arn" { 12 | value = aws_db_instance.example.arn 13 | description = "The ARN of the database" 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "Name for the DB." 8 | type = string 9 | default = null 10 | } 11 | 12 | variable "db_username" { 13 | description = "Username for the DB." 14 | type = string 15 | sensitive = true 16 | default = null 17 | } 18 | 19 | variable "db_password" { 20 | description = "Password for the DB." 21 | type = string 22 | sensitive = true 23 | default = null 24 | } 25 | 26 | variable "backup_retention_period" { 27 | description = "Days to retain backups. Must be > 0 to enable replication." 28 | type = number 29 | default = null 30 | } 31 | 32 | variable "replicate_source_db" { 33 | description = "If specified, replicate the RDS database at the given ARN." 34 | type = string 35 | default = null 36 | } 37 | 38 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/multi-account/README.md: -------------------------------------------------------------------------------- 1 | # Multi AWS account module 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that shows how to have a reusable 4 | Terraform module work with multiple providers, which gives it the ability to work with multiple [Amazon Web Services 5 | (AWS) accounts](http://aws.amazon.com/). This module defines `configuration_aliases`, so users of the module can pass 6 | in providers that have authenticated to different AWS accounts (e.g., via IAM roles). 7 | 8 | For more info, please see Chapter 7, "Working with Multiple Providers", of 9 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 10 | 11 | ## Quick start 12 | 13 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 14 | configurations. See [examples/multi-account-module](../../examples/multi-account-module) for a working example. 15 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/multi-account/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | configuration_aliases = [aws.parent, aws.child] 9 | } 10 | } 11 | } 12 | 13 | data "aws_caller_identity" "parent" { 14 | provider = aws.parent 15 | } 16 | 17 | data "aws_caller_identity" "child" { 18 | provider = aws.child 19 | } 20 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/multi-account/outputs.tf: -------------------------------------------------------------------------------- 1 | output "parent_account_id" { 2 | value = data.aws_caller_identity.parent.account_id 3 | description = "The ID of the parent AWS account" 4 | } 5 | 6 | output "child_account_id" { 7 | value = data.aws_caller_identity.child.account_id 8 | description = "The ID of the child AWS account" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/services/eks-cluster/README.md: -------------------------------------------------------------------------------- 1 | # EKS Cluster 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys a Kubernetes cluster 4 | using [EKS](https://aws.amazon.com/eks/) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). This is 5 | about the simplest, most minimal EKS cluster possible: useful for learning, but NOT any sort of production usage. 6 | 7 | For more info, please see Chapter 7, "Working with Multiple Providers", of 8 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 9 | 10 | ## Quick start 11 | 12 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 13 | configurations. See [examples/kubernetes](../../../examples/kubernetes-eks) for a working example. -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/services/eks-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | output "cluster_name" { 2 | value = aws_eks_cluster.cluster.name 3 | description = "Name of the EKS cluster" 4 | } 5 | 6 | output "cluster_arn" { 7 | value = aws_eks_cluster.cluster.arn 8 | description = "ARN of the EKS cluster" 9 | } 10 | 11 | output "cluster_endpoint" { 12 | value = aws_eks_cluster.cluster.endpoint 13 | description = "Endpoint of the EKS cluster" 14 | } 15 | 16 | output "cluster_certificate_authority" { 17 | value = aws_eks_cluster.cluster.certificate_authority 18 | description = "Certificate authority of the EKS cluster" 19 | } 20 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/services/eks-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "name" { 7 | description = "The name to use for the EKS cluster" 8 | type = string 9 | } 10 | 11 | variable "min_size" { 12 | description = "Minimum number of nodes to have in the EKS cluster" 13 | type = number 14 | } 15 | 16 | variable "max_size" { 17 | description = "Maximum number of nodes to have in the EKS cluster" 18 | type = number 19 | } 20 | 21 | variable "desired_size" { 22 | description = "Desired number of nodes to have in the EKS cluster" 23 | type = number 24 | } 25 | 26 | variable "instance_types" { 27 | description = "The types of EC2 instances to run in the node group" 28 | type = list(string) 29 | } 30 | -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/services/k8s-app/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes app 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys an app onto a 4 | Kubernetes cluster using a [Kubernetes 5 | Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) and configures a load balancer for 6 | the app using a [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/). This is 7 | about the simplest, most minimal way to run a K8S app possible: useful for learning, but NOT any sort of production 8 | usage. 9 | 10 | For more info, please see Chapter 7, "Working with Multiple Providers", of 11 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 12 | 13 | ## Quick start 14 | 15 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 16 | configurations. See [examples/kubernetes](../../../examples/kubernetes-eks) for a working example. -------------------------------------------------------------------------------- /code/terraform/07-working-with-multiple-providers/modules/services/k8s-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "service_status" { 2 | value = kubernetes_service.app.status 3 | description = "The K8S Service status" 4 | } 5 | 6 | locals { 7 | status = kubernetes_service.app.status 8 | } 9 | 10 | output "service_endpoint" { 11 | value = try( 12 | "http://${local.status[0]["load_balancer"][0]["ingress"][0]["hostname"]}", 13 | "(error parsing hostname from status)" 14 | ) 15 | description = "The K8S Service endpoint" 16 | } 17 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/more-than-terraform/external-data-source/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | external = { 6 | source = "hashicorp/external" 7 | version = "~> 2.0" 8 | } 9 | } 10 | } 11 | 12 | data "external" "echo" { 13 | program = ["bash", "-c", "cat /dev/stdin"] 14 | 15 | query = { 16 | foo = "bar" 17 | } 18 | } 19 | 20 | output "echo" { 21 | value = data.external.echo.result 22 | } 23 | 24 | output "echo_foo" { 25 | value = data.external.echo.result.foo 26 | } 27 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/more-than-terraform/local-exec-provisioner/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "example" { 17 | ami = data.aws_ami.ubuntu.id 18 | instance_type = "t2.micro" 19 | 20 | provisioner "local-exec" { 21 | command = "echo \"Hello, World from $(uname -smp)\"" 22 | } 23 | } 24 | 25 | data "aws_ami" "ubuntu" { 26 | most_recent = true 27 | owners = ["099720109477"] # Canonical 28 | 29 | filter { 30 | name = "name" 31 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 32 | } 33 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/more-than-terraform/null-resource/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | null = { 6 | source = "hashicorp/null" 7 | version = "~> 3.0" 8 | } 9 | } 10 | } 11 | 12 | resource "null_resource" "example" { 13 | # Use UUID to force this null_resource to be recreated on every 14 | # call to 'terraform apply' 15 | triggers = { 16 | uuid = uuid() 17 | } 18 | 19 | provisioner "local-exec" { 20 | command = "echo \"Hello, World from $(uname -smp)\"" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/alb/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "alb" { 17 | source = "../../modules/networking/alb" 18 | 19 | alb_name = var.alb_name 20 | subnet_ids = data.aws_subnets.default.ids 21 | } 22 | 23 | data "aws_vpc" "default" { 24 | default = true 25 | } 26 | 27 | data "aws_subnets" "default" { 28 | filter { 29 | name = "vpc-id" 30 | values = [data.aws_vpc.default.id] 31 | } 32 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name of the ALB and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/asg/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "asg" { 17 | source = "../../modules/cluster/asg-rolling-deploy" 18 | 19 | cluster_name = var.cluster_name 20 | 21 | ami = data.aws_ami.ubuntu.id 22 | instance_type = "t2.micro" 23 | 24 | min_size = 1 25 | max_size = 1 26 | enable_autoscaling = false 27 | 28 | subnet_ids = data.aws_subnets.default.ids 29 | } 30 | 31 | data "aws_vpc" "default" { 32 | default = true 33 | } 34 | 35 | data "aws_subnets" "default" { 36 | filter { 37 | name = "vpc-id" 38 | values = [data.aws_vpc.default.id] 39 | } 40 | } 41 | 42 | data "aws_ami" "ubuntu" { 43 | most_recent = true 44 | owners = ["099720109477"] # Canonical 45 | 46 | filter { 47 | name = "name" 48 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/asg/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = module.asg.asg_name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/asg/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "cluster_name" { 7 | description = "The name of the ASG and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "mysql" { 17 | source = "../../modules/data-stores/mysql" 18 | 19 | db_name = var.db_name 20 | db_username = var.db_username 21 | db_password = var.db_password 22 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/examples/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/prod/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | module "mysql" { 28 | source = "../../../../modules/data-stores/mysql" 29 | 30 | db_name = var.db_name 31 | db_username = var.db_username 32 | db_password = var.db_password 33 | } 34 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/prod/services/hello-world-app/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "hello_world_app" { 17 | 18 | source = "../../../../modules/services/hello-world-app" 19 | 20 | server_text = var.server_text 21 | 22 | environment = var.environment 23 | db_remote_state_bucket = var.db_remote_state_bucket 24 | db_remote_state_key = var.db_remote_state_key 25 | 26 | instance_type = "t2.micro" 27 | min_size = 2 28 | max_size = 2 29 | enable_autoscaling = false 30 | ami = data.aws_ami.ubuntu.id 31 | } 32 | 33 | data "aws_ami" "ubuntu" { 34 | most_recent = true 35 | owners = ["099720109477"] # Canonical 36 | 37 | filter { 38 | name = "name" 39 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/prod/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | module "mysql" { 28 | source = "../../../../modules/data-stores/mysql" 29 | 30 | db_name = var.db_name 31 | db_username = var.db_username 32 | db_password = var.db_password 33 | } 34 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/stage/services/hello-world-app/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "hello_world_app" { 17 | 18 | source = "../../../../modules/services/hello-world-app" 19 | 20 | server_text = var.server_text 21 | 22 | environment = var.environment 23 | db_remote_state_bucket = var.db_remote_state_bucket 24 | db_remote_state_key = var.db_remote_state_key 25 | 26 | instance_type = "t2.micro" 27 | min_size = 2 28 | max_size = 2 29 | enable_autoscaling = false 30 | ami = data.aws_ami.ubuntu.id 31 | } 32 | 33 | data "aws_ami" "ubuntu" { 34 | most_recent = true 35 | owners = ["099720109477"] # Canonical 36 | 37 | filter { 38 | name = "name" 39 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/live/stage/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/cluster/asg-rolling-deploy/README.md: -------------------------------------------------------------------------------- 1 | # Auto Scaling Group with Rolling Deploy 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) and [Auto 5 | Scaling](https://aws.amazon.com/autoscaling/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | The Auto Scaling Group is able to do a zero-downtime deployment (using [instance 7 | refresh](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html)) when you update any of its 8 | properties. 9 | 10 | For more info, please see Chapter 8, "Production-grade Terraform code", of 11 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 12 | 13 | ## Quick start 14 | 15 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 16 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 17 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/cluster/asg-rolling-deploy/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = aws_autoscaling_group.example.name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | 6 | output "instance_security_group_id" { 7 | value = aws_security_group.instance.id 8 | description = "The ID of the EC2 Instance Security Group" 9 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/data-stores/mysql/README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys a MySQL database 4 | (using [RDS](https://aws.amazon.com/rds/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 5 | 6 | For more info, please see Chapter 8, "Production-grade Terraform code", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Quick start 10 | 11 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 12 | configurations. See [live/stage/data-stores/mysql](../../../live/stage/data-stores/mysql) and 13 | [live/prod/data-stores/mysql](../../../live/prod/data-stores/mysql) for examples. -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | resource "aws_db_instance" "example" { 13 | identifier_prefix = "terraform-up-and-running" 14 | engine = "mysql" 15 | allocated_storage = 10 16 | instance_class = "db.t2.micro" 17 | db_name = var.db_name 18 | username = var.db_username 19 | password = var.db_password 20 | skip_final_snapshot = true 21 | } 22 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "The name to use for the database" 8 | type = string 9 | } 10 | 11 | variable "db_username" { 12 | description = "The username for the database" 13 | type = string 14 | sensitive = true 15 | } 16 | 17 | variable "db_password" { 18 | description = "The password for the database" 19 | type = string 20 | sensitive = true 21 | } 22 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/networking/alb/README.md: -------------------------------------------------------------------------------- 1 | # Application Load Balancer 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a load balancer (using [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an 5 | [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | 7 | For more info, please see Chapter 8, "Production-grade Terraform code", of 8 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 9 | 10 | ## Quick start 11 | 12 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 13 | configurations. See [live/stage/networking/alb](../../../live/stage/networking/alb) and 14 | [live/prod/networking/alb](../../../live/prod/networking/alb) for examples. -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/networking/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "alb_http_listener_arn" { 7 | value = aws_lb_listener.http.arn 8 | description = "The ARN of the HTTP listener" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ALB Security Group ID" 14 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/networking/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name to use for this ALB" 8 | type = string 9 | } 10 | 11 | variable "subnet_ids" { 12 | description = "The subnet IDs to deploy to" 13 | type = list(string) 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/services/hello-world-app/README.md: -------------------------------------------------------------------------------- 1 | # Hello World App 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a simple "Hello, World app" across a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) 5 | and [Auto Scaling](https://aws.amazon.com/autoscaling/)) with a load balancer (using 6 | [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an [Amazon Web Services (AWS) 7 | account](http://aws.amazon.com/). 8 | 9 | For more info, please see Chapter 8, "Production-grade Terraform code", of 10 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 11 | 12 | ## Quick start 13 | 14 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 15 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 16 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = module.asg.asg_name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "instance_security_group_id" { 12 | value = module.asg.instance_security_group_id 13 | description = "The ID of the EC2 Instance Security Group" 14 | } -------------------------------------------------------------------------------- /code/terraform/08-production-grade-infrastructure/small-modules/modules/services/hello-world-app/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <${server_text} 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/alb/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc" "default" { 2 | default = true 3 | } 4 | 5 | data "aws_subnets" "default" { 6 | filter { 7 | name = "vpc-id" 8 | values = [data.aws_vpc.default.id] 9 | } 10 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/alb/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "alb" { 17 | source = "../../modules/networking/alb" 18 | 19 | alb_name = var.alb_name 20 | 21 | subnet_ids = data.aws_subnets.default.ids 22 | } 23 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name of the ALB and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/asg/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc" "default" { 2 | default = true 3 | } 4 | 5 | data "aws_subnets" "default" { 6 | filter { 7 | name = "vpc-id" 8 | values = [data.aws_vpc.default.id] 9 | } 10 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/asg/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "asg" { 17 | source = "../../modules/cluster/asg-rolling-deploy" 18 | 19 | cluster_name = var.cluster_name 20 | 21 | ami = data.aws_ami.ubuntu.id 22 | instance_type = "t2.micro" 23 | 24 | min_size = 1 25 | max_size = 1 26 | enable_autoscaling = false 27 | 28 | subnet_ids = data.aws_subnets.default.ids 29 | } 30 | 31 | data "aws_ami" "ubuntu" { 32 | most_recent = true 33 | owners = ["099720109477"] # Canonical 34 | 35 | filter { 36 | name = "name" 37 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/asg/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = module.asg.asg_name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/asg/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "cluster_name" { 7 | description = "The name of the ASG and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/hello-world-app/standalone/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "hello_world_app" { 17 | source = "../../../modules/services/hello-world-app" 18 | 19 | server_text = var.server_text 20 | 21 | environment = var.environment 22 | 23 | mysql_config = var.mysql_config 24 | 25 | instance_type = "t2.micro" 26 | min_size = 2 27 | max_size = 2 28 | enable_autoscaling = false 29 | ami = data.aws_ami.ubuntu.id 30 | } 31 | 32 | data "aws_ami" "ubuntu" { 33 | most_recent = true 34 | owners = ["099720109477"] # Canonical 35 | 36 | filter { 37 | name = "name" 38 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/hello-world-app/standalone/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/hello-world-app/standalone/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "mysql_config" { 7 | description = "The config for the MySQL DB" 8 | 9 | type = object({ 10 | address = string 11 | port = number 12 | }) 13 | 14 | default = { 15 | address = "mock-mysql-address" 16 | port = 12345 17 | } 18 | } 19 | 20 | # --------------------------------------------------------------------------------------------------------------------- 21 | # OPTIONAL PARAMETERS 22 | # These parameters have reasonable defaults. 23 | # --------------------------------------------------------------------------------------------------------------------- 24 | 25 | variable "server_text" { 26 | description = "The text the web server should return" 27 | default = "Hello, World" 28 | type = string 29 | } 30 | 31 | variable "environment" { 32 | description = "The name of the environment we're deploying to" 33 | type = string 34 | default = "example" 35 | } 36 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/hello-world-app/with-mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "hello_world_app" { 17 | source = "../../../modules/services/hello-world-app" 18 | 19 | server_text = var.server_text 20 | 21 | environment = var.environment 22 | 23 | # Pass all the outputs from the mysql module straight through! 24 | mysql_config = module.mysql 25 | 26 | instance_type = "t2.micro" 27 | min_size = 2 28 | max_size = 2 29 | enable_autoscaling = false 30 | ami = data.aws_ami.ubuntu.id 31 | } 32 | 33 | module "mysql" { 34 | source = "../../../modules/data-stores/mysql" 35 | 36 | db_name = var.db_name 37 | db_username = var.db_username 38 | db_password = var.db_password 39 | } 40 | 41 | data "aws_ami" "ubuntu" { 42 | most_recent = true 43 | owners = ["099720109477"] # Canonical 44 | 45 | filter { 46 | name = "name" 47 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 48 | } 49 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/hello-world-app/with-mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "mysql_config" { 7 | value = module.mysql 8 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "mysql" { 17 | source = "../../modules/data-stores/mysql" 18 | 19 | db_name = var.db_name 20 | db_username = var.db_username 21 | db_password = var.db_password 22 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The password for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database" 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/opa/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "example" { 17 | ami = data.aws_ami.ubuntu.id 18 | instance_type = "t2.micro" 19 | 20 | tags = var.tags 21 | } 22 | 23 | data "aws_ami" "ubuntu" { 24 | most_recent = true 25 | owners = ["099720109477"] # Canonical 26 | 27 | filter { 28 | name = "name" 29 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 30 | } 31 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/examples/opa/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "tags" { 7 | description = "The tags to add to the EC2 instance" 8 | type = map(string) 9 | default = { 10 | ManagedBy = "Terraform" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 13 | # manually, uncomment and fill in the config below. 14 | 15 | # bucket = "" 16 | # key = "/terraform.tfstate" 17 | # region = "us-east-2" 18 | # dynamodb_table = "" 19 | # encrypt = true 20 | } 21 | } 22 | 23 | provider "aws" { 24 | region = "us-east-2" 25 | } 26 | 27 | module "mysql" { 28 | source = "../../../../modules/data-stores/mysql" 29 | 30 | db_name = var.db_name 31 | db_username = var.db_username 32 | db_password = var.db_password 33 | } 34 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_prod" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/services/hello-world-app/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | } 12 | 13 | provider "aws" { 14 | region = "us-east-2" 15 | } 16 | 17 | module "hello_world_app" { 18 | 19 | source = "../../../../modules/services/hello-world-app" 20 | 21 | server_text = var.server_text 22 | 23 | environment = var.environment 24 | db_remote_state_bucket = var.db_remote_state_bucket 25 | db_remote_state_key = var.db_remote_state_key 26 | 27 | instance_type = "t2.micro" 28 | min_size = 2 29 | max_size = 2 30 | enable_autoscaling = false 31 | ami = data.aws_ami.ubuntu.id 32 | } 33 | 34 | data "aws_ami" "ubuntu" { 35 | most_recent = true 36 | owners = ["099720109477"] # Canonical 37 | 38 | filter { 39 | name = "name" 40 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 41 | } 42 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/prod/services/hello-world-app/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_remote_state_bucket" { 7 | description = "The name of the S3 bucket for the database's remote state" 8 | type = string 9 | } 10 | 11 | variable "db_remote_state_key" { 12 | description = "The path for the database's remote state in S3" 13 | type = string 14 | } 15 | 16 | # --------------------------------------------------------------------------------------------------------------------- 17 | # OPTIONAL PARAMETERS 18 | # These parameters have reasonable defaults. 19 | # --------------------------------------------------------------------------------------------------------------------- 20 | 21 | variable "server_text" { 22 | description = "The text the web server should return" 23 | default = "Hello, World" 24 | type = string 25 | } 26 | 27 | variable "environment" { 28 | description = "The name of the environment we're deploying to" 29 | type = string 30 | default = "prod" 31 | } 32 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/stage/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | 11 | backend "s3" { 12 | 13 | # This backend configuration is filled in automatically at test time by Terratest. If you wish to run this example 14 | # manually, uncomment and fill in the config below. 15 | 16 | # bucket = "" 17 | # key = "/terraform.tfstate" 18 | # region = "us-east-2" 19 | # dynamodb_table = "" 20 | # encrypt = true 21 | 22 | } 23 | } 24 | 25 | provider "aws" { 26 | region = "us-east-2" 27 | } 28 | 29 | module "mysql" { 30 | source = "../../../../modules/data-stores/mysql" 31 | 32 | db_name = var.db_name 33 | db_username = var.db_username 34 | db_password = var.db_password 35 | } 36 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/stage/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = module.mysql.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = module.mysql.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/stage/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_username" { 7 | description = "The username for the database" 8 | type = string 9 | sensitive = true 10 | } 11 | 12 | variable "db_password" { 13 | description = "The password for the database" 14 | type = string 15 | sensitive = true 16 | } 17 | 18 | # --------------------------------------------------------------------------------------------------------------------- 19 | # OPTIONAL PARAMETERS 20 | # These parameters have reasonable defaults. 21 | # --------------------------------------------------------------------------------------------------------------------- 22 | 23 | variable "db_name" { 24 | description = "The name to use for the database" 25 | type = string 26 | default = "example_database_stage" 27 | } 28 | 29 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/stage/services/hello-world-app/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "hello_world_app" { 17 | source = "../../../../modules/services/hello-world-app" 18 | 19 | server_text = var.server_text 20 | 21 | environment = var.environment 22 | db_remote_state_bucket = var.db_remote_state_bucket 23 | db_remote_state_key = var.db_remote_state_key 24 | 25 | instance_type = "t2.micro" 26 | min_size = 2 27 | max_size = 2 28 | enable_autoscaling = false 29 | ami = data.aws_ami.ubuntu.id 30 | } 31 | 32 | data "aws_ami" "ubuntu" { 33 | most_recent = true 34 | owners = ["099720109477"] # Canonical 35 | 36 | filter { 37 | name = "name" 38 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/live/stage/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.hello_world_app.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/cluster/asg-rolling-deploy/README.md: -------------------------------------------------------------------------------- 1 | # Auto Scaling Group with Rolling Deploy 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) and [Auto 5 | Scaling](https://aws.amazon.com/autoscaling/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | The Auto Scaling Group is able to do a zero-downtime deployment (using [instance 7 | refresh](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html)) when you update any of its 8 | properties. 9 | 10 | For more info, please see Chapter 9, "How to test Terraform code", of 11 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 12 | 13 | ## Quick start 14 | 15 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 16 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 17 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/cluster/asg-rolling-deploy/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = aws_autoscaling_group.example.name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | 6 | output "instance_security_group_id" { 7 | value = aws_security_group.instance.id 8 | description = "The ID of the EC2 Instance Security Group" 9 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/data-stores/mysql/README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys a MySQL database 4 | (using [RDS](https://aws.amazon.com/rds/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 5 | 6 | For more info, please see Chapter 9, "How to test Terraform code", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Quick start 10 | 11 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 12 | configurations. See [live/stage/data-stores/mysql](../../../live/stage/data-stores/mysql) and 13 | [live/prod/data-stores/mysql](../../../live/prod/data-stores/mysql) for examples. -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | resource "aws_db_instance" "example" { 13 | identifier_prefix = "terraform-up-and-running" 14 | engine = "mysql" 15 | allocated_storage = 10 16 | instance_class = "db.t2.micro" 17 | db_name = var.db_name 18 | username = var.db_username 19 | password = var.db_password 20 | skip_final_snapshot = true 21 | } 22 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "The name to use for the database" 8 | type = string 9 | } 10 | 11 | variable "db_username" { 12 | description = "The username for the database" 13 | type = string 14 | sensitive = true 15 | } 16 | 17 | variable "db_password" { 18 | description = "The password for the database" 19 | type = string 20 | sensitive = true 21 | } 22 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/networking/alb/README.md: -------------------------------------------------------------------------------- 1 | # Application Load Balancer 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a load balancer (using [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an 5 | [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | 7 | For more info, please see Chapter 9, "How to test Terraform code", of 8 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 9 | 10 | ## Quick start 11 | 12 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 13 | configurations. See [live/stage/networking/alb](../../../live/stage/networking/alb) and 14 | [live/prod/networking/alb](../../../live/prod/networking/alb) for examples. -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/networking/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "alb_http_listener_arn" { 7 | value = aws_lb_listener.http.arn 8 | description = "The ARN of the HTTP listener" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ALB Security Group ID" 14 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/networking/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name to use for this ALB" 8 | type = string 9 | } 10 | 11 | variable "subnet_ids" { 12 | description = "The subnet IDs to deploy to" 13 | type = list(string) 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/services/hello-world-app/README.md: -------------------------------------------------------------------------------- 1 | # Hello World App 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a simple "Hello, World app" across a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) 5 | and [Auto Scaling](https://aws.amazon.com/autoscaling/)) with a load balancer (using 6 | [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an [Amazon Web Services (AWS) 7 | account](http://aws.amazon.com/). 8 | 9 | For more info, please see Chapter 9, "How to test Terraform code", of 10 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 11 | 12 | ## Quick start 13 | 14 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 15 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 16 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/services/hello-world-app/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "db" { 2 | count = var.mysql_config == null ? 1 : 0 3 | 4 | backend = "s3" 5 | 6 | config = { 7 | bucket = var.db_remote_state_bucket 8 | key = var.db_remote_state_key 9 | region = "us-east-2" 10 | } 11 | } 12 | 13 | data "aws_vpc" "default" { 14 | count = var.vpc_id == null ? 1 : 0 15 | default = true 16 | } 17 | 18 | data "aws_subnets" "default" { 19 | count = var.subnet_ids == null ? 1 : 0 20 | 21 | filter { 22 | name = "vpc-id" 23 | values = [local.vpc_id] 24 | } 25 | 26 | } 27 | 28 | locals { 29 | mysql_config = ( 30 | var.mysql_config == null 31 | ? data.terraform_remote_state.db[0].outputs 32 | : var.mysql_config 33 | ) 34 | 35 | vpc_id = ( 36 | var.vpc_id == null 37 | ? data.aws_vpc.default[0].id 38 | : var.vpc_id 39 | ) 40 | 41 | subnet_ids = ( 42 | var.subnet_ids == null 43 | ? data.aws_subnets.default[0].ids 44 | : var.subnet_ids 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = module.asg.asg_name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "instance_security_group_id" { 12 | value = module.asg.instance_security_group_id 13 | description = "The ID of the EC2 Instance Security Group" 14 | } -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/modules/services/hello-world-app/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <${server_text} 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/test/asg_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gruntwork-io/terratest/modules/random" 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "testing" 8 | ) 9 | 10 | func TestAsgExample(t *testing.T) { 11 | t.Parallel() 12 | 13 | terraformOptions := &terraform.Options{ 14 | // You should update this relative path to point at your alb 15 | // example directory! 16 | TerraformDir: "../examples/asg", 17 | Vars: map[string]interface{}{ 18 | "cluster_name": fmt.Sprintf("test-%s", random.UniqueId()), 19 | }, 20 | } 21 | 22 | defer terraform.Destroy(t, terraformOptions) 23 | terraform.InitAndApply(t, terraformOptions) 24 | } 25 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/test/go_sanity_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGoIsWorking(t *testing.T) { 9 | fmt.Println() 10 | fmt.Println("If you see this text, it's working!") 11 | fmt.Println() 12 | } 13 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/test/mysql_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gruntwork-io/terratest/modules/random" 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "testing" 8 | ) 9 | 10 | func TestMySqlExample(t *testing.T) { 11 | t.Parallel() 12 | 13 | terraformOptions := &terraform.Options{ 14 | // You should update this relative path to point at your mysql 15 | // example directory! 16 | TerraformDir: "../examples/mysql", 17 | Vars: map[string]interface{}{ 18 | "db_name": fmt.Sprintf("test_%s", random.UniqueId()), 19 | "db_username": "admin", 20 | "db_password": "password", 21 | }, 22 | } 23 | 24 | defer terraform.Destroy(t, terraformOptions) 25 | terraform.InitAndApply(t, terraformOptions) 26 | } 27 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/test/opa_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/gruntwork-io/terratest/modules/opa" 5 | "github.com/gruntwork-io/terratest/modules/terraform" 6 | "testing" 7 | ) 8 | 9 | func TestOPA(t *testing.T) { 10 | t.Parallel() 11 | 12 | // Test the Terraform code in the examples/opa folder 13 | tfOpts := &terraform.Options{ 14 | TerraformDir: "../examples/opa", 15 | } 16 | 17 | // Run the Terraform code against the OPA policy in enforce_source.rego 18 | opaOpts := &opa.EvalOptions{ 19 | RulePath: "../../../opa/09-testing-terraform-code/enforce_source.rego", 20 | FailMode: opa.FailUndefined, 21 | } 22 | 23 | // Fail the test if the OPA policy fails 24 | terraform.OPAEval(t, tfOpts, opaOpts, "data.enforce_source.allow") 25 | } 26 | -------------------------------------------------------------------------------- /code/terraform/09-testing-terraform-code/test/test_helpers.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | // To avoid wasting lots of time constantly creating and deleting S3 buckets for the tests that need to store state in 9 | // an S3 backend, some of the tests require that you create the S3 bucket ahead of time and set the bucket name and 10 | // region as environment variables 11 | const TerraformStateBucketForTestEnvVarName = "TEST_STATE_S3_BUCKET" 12 | const TerraformStateRegionForTestEnvVarName = "TEST_STATE_REGION" 13 | 14 | // Get the value of the environment variable with the given name. If that environment variable is not set, fail the 15 | // test. 16 | func GetRequiredEnvVar(t *testing.T, envVarName string) string { 17 | envVarValue := os.Getenv(envVarName) 18 | if envVarValue == "" { 19 | t.Fatalf("Required environment variable '%s' is not set", envVarName) 20 | } 21 | return envVarValue 22 | } 23 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/alb/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc" "default" { 2 | default = true 3 | } 4 | 5 | data "aws_subnets" "default" { 6 | filter { 7 | name = "vpc-id" 8 | values = [data.aws_vpc.default.id] 9 | } 10 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/alb/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "alb" { 17 | source = "../../modules/networking/alb" 18 | 19 | alb_name = var.alb_name 20 | 21 | subnet_ids = data.aws_subnets.default.ids 22 | } 23 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name of the ALB and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/asg/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "aws_vpc" "default" { 2 | default = true 3 | } 4 | 5 | data "aws_subnets" "default" { 6 | filter { 7 | name = "vpc-id" 8 | values = [data.aws_vpc.default.id] 9 | } 10 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/asg/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | module "asg" { 17 | source = "../../modules/cluster/asg-rolling-deploy" 18 | 19 | cluster_name = var.cluster_name 20 | 21 | ami = data.aws_ami.ubuntu.id 22 | instance_type = "t2.micro" 23 | 24 | min_size = 1 25 | max_size = 1 26 | enable_autoscaling = false 27 | 28 | subnet_ids = data.aws_subnets.default.ids 29 | } 30 | 31 | data "aws_ami" "ubuntu" { 32 | most_recent = true 33 | owners = ["099720109477"] # Canonical 34 | 35 | filter { 36 | name = "name" 37 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 38 | } 39 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/asg/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = module.asg.asg_name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/examples/asg/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # OPTIONAL PARAMETERS 3 | # These parameters have reasonable defaults. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "cluster_name" { 7 | description = "The name of the ASG and all its resources" 8 | type = string 9 | default = "terraform-up-and-running" 10 | } 11 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/data-stores/mysql/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/data-stores/mysql/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | source = "../../../../modules//data-stores/mysql" 3 | } 4 | 5 | include { 6 | path = find_in_parent_folders() 7 | } 8 | 9 | inputs = { 10 | db_name = "example_prod" 11 | 12 | # Set the username using the TF_VAR_db_username environment variable 13 | # Set the password using the TF_VAR_db_password environment variable 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/services/hello-world-app/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/services/hello-world-app/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | source = "../../../../modules//services/hello-world-app" 3 | } 4 | 5 | include { 6 | path = find_in_parent_folders() 7 | } 8 | 9 | dependency "mysql" { 10 | config_path = "../../data-stores/mysql" 11 | } 12 | 13 | inputs = { 14 | environment = "prod" 15 | ami = "ami-0fb653ca2d3203ac1" 16 | 17 | min_size = 2 18 | max_size = 2 19 | 20 | enable_autoscaling = false 21 | 22 | mysql_config = dependency.mysql.outputs 23 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/prod/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terragrunt_version_constraint = ">= v0.36.0" 2 | 3 | remote_state { 4 | backend = "s3" 5 | 6 | generate = { 7 | path = "backend.tf" 8 | if_exists = "overwrite" 9 | } 10 | 11 | config = { 12 | bucket = get_env("TEST_STATE_S3_BUCKET", "") 13 | key = "${path_relative_to_include()}/terraform.tfstate" 14 | region = get_env("TEST_STATE_REGION", "") 15 | encrypt = true 16 | dynamodb_table = get_env("TEST_STATE_DYNAMODB_TABLE", "") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/data-stores/mysql/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/data-stores/mysql/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | source = "../../../../modules//data-stores/mysql" 3 | } 4 | 5 | include { 6 | path = find_in_parent_folders() 7 | } 8 | 9 | inputs = { 10 | db_name = "example_stage" 11 | 12 | # Set the username using the TF_VAR_db_username environment variable 13 | # Set the password using the TF_VAR_db_password environment variable 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/services/conflict-anna/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "foo" { 17 | ami = data.aws_ami.ubuntu.id 18 | instance_type = "t2.medium" 19 | } 20 | 21 | data "aws_ami" "ubuntu" { 22 | most_recent = true 23 | owners = ["099720109477"] # Canonical 24 | 25 | filter { 26 | name = "name" 27 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 28 | } 29 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/services/conflict-bill/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "foo" { 17 | ami = data.aws_ami.ubuntu.id 18 | instance_type = "t2.micro" 19 | 20 | tags = { 21 | Name = "foo" 22 | } 23 | } 24 | 25 | data "aws_ami" "ubuntu" { 26 | most_recent = true 27 | owners = ["099720109477"] # Canonical 28 | 29 | filter { 30 | name = "name" 31 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 32 | } 33 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/services/conflict-original/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 1.0.0, < 2.0.0" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-2" 14 | } 15 | 16 | resource "aws_instance" "foo" { 17 | ami = data.aws_ami.ubuntu.id 18 | instance_type = "t2.micro" 19 | } 20 | 21 | data "aws_ami" "ubuntu" { 22 | most_recent = true 23 | owners = ["099720109477"] # Canonical 24 | 25 | filter { 26 | name = "name" 27 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"] 28 | } 29 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/services/hello-world-app/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/services/hello-world-app/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terraform { 2 | source = "../../../../modules//services/hello-world-app" 3 | } 4 | 5 | include { 6 | path = find_in_parent_folders() 7 | } 8 | 9 | dependency "mysql" { 10 | config_path = "../../data-stores/mysql" 11 | } 12 | 13 | inputs = { 14 | environment = "stage" 15 | ami = "ami-0fb653ca2d3203ac1" 16 | 17 | min_size = 2 18 | max_size = 2 19 | 20 | enable_autoscaling = false 21 | 22 | mysql_config = dependency.mysql.outputs 23 | } 24 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/live/stage/terragrunt.hcl: -------------------------------------------------------------------------------- 1 | terragrunt_version_constraint = ">= v0.36.0" 2 | 3 | remote_state { 4 | backend = "s3" 5 | 6 | generate = { 7 | path = "backend.tf" 8 | if_exists = "overwrite" 9 | } 10 | 11 | config = { 12 | 13 | bucket = get_env("TEST_STATE_S3_BUCKET", "") 14 | 15 | key = "${path_relative_to_include()}/terraform.tfstate" 16 | 17 | region = get_env("TEST_STATE_REGION", "") 18 | 19 | encrypt = true 20 | 21 | dynamodb_table = get_env("TEST_STATE_DYNAMODB_TABLE", "") 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/cluster/asg-rolling-deploy/README.md: -------------------------------------------------------------------------------- 1 | # Auto Scaling Group with Rolling Deploy 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) and [Auto 5 | Scaling](https://aws.amazon.com/autoscaling/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | The Auto Scaling Group is able to do a zero-downtime deployment (using [instance 7 | refresh](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html)) when you update any of its 8 | properties. 9 | 10 | For more info, please see Chapter 10, "How to use Terraform as a Team", of 11 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 12 | 13 | ## Quick start 14 | 15 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 16 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 17 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/cluster/asg-rolling-deploy/outputs.tf: -------------------------------------------------------------------------------- 1 | output "asg_name" { 2 | value = aws_autoscaling_group.example.name 3 | description = "The name of the Auto Scaling Group" 4 | } 5 | 6 | output "instance_security_group_id" { 7 | value = aws_security_group.instance.id 8 | description = "The ID of the EC2 Instance Security Group" 9 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/data-stores/mysql/README.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that deploys a MySQL database 4 | (using [RDS](https://aws.amazon.com/rds/)) in an [Amazon Web Services (AWS) account](http://aws.amazon.com/). 5 | 6 | For more info, please see Chapter 10, "How to use Terraform as a Team", of 7 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 8 | 9 | ## Quick start 10 | 11 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 12 | configurations. See [live/stage/data-stores/mysql](../../../live/stage/data-stores/mysql) and 13 | [live/prod/data-stores/mysql](../../../live/prod/data-stores/mysql) for examples. -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/data-stores/mysql/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "us-east-2" 3 | } 4 | 5 | terraform { 6 | # Require any 1.x version of Terraform 7 | required_version = ">= 1.0.0, < 2.0.0" 8 | 9 | # Require an 3.x version of the AWS provider 10 | required_providers { 11 | aws = { 12 | source = "hashicorp/aws" 13 | version = "~> 4.0" 14 | } 15 | } 16 | } 17 | 18 | resource "aws_db_instance" "example" { 19 | identifier_prefix = "terraform-up-and-running" 20 | engine = "mysql" 21 | allocated_storage = 10 22 | instance_class = "db.t2.micro" 23 | db_name = var.db_name 24 | username = var.db_username 25 | password = var.db_password 26 | skip_final_snapshot = true 27 | } 28 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/data-stores/mysql/outputs.tf: -------------------------------------------------------------------------------- 1 | output "address" { 2 | value = aws_db_instance.example.address 3 | description = "Connect to the database at this endpoint" 4 | } 5 | 6 | output "port" { 7 | value = aws_db_instance.example.port 8 | description = "The port the database is listening on" 9 | } 10 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/data-stores/mysql/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "db_name" { 7 | description = "The name to use for the database" 8 | type = string 9 | } 10 | 11 | variable "db_username" { 12 | description = "The username for the database" 13 | type = string 14 | sensitive = true 15 | } 16 | 17 | variable "db_password" { 18 | description = "The password for the database" 19 | type = string 20 | sensitive = true 21 | } 22 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/networking/alb/README.md: -------------------------------------------------------------------------------- 1 | # Application Load Balancer 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a load balancer (using [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an 5 | [Amazon Web Services (AWS) account](http://aws.amazon.com/). 6 | 7 | For more info, please see Chapter 10, "How to use Terraform as a Team", of 8 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 9 | 10 | ## Quick start 11 | 12 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 13 | configurations. See [live/stage/networking/alb](../../../live/stage/networking/alb) and 14 | [live/prod/networking/alb](../../../live/prod/networking/alb) for examples. -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/networking/alb/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = aws_lb.example.dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "alb_http_listener_arn" { 7 | value = aws_lb_listener.http.arn 8 | description = "The ARN of the HTTP listener" 9 | } 10 | 11 | output "alb_security_group_id" { 12 | value = aws_security_group.alb.id 13 | description = "The ALB Security Group ID" 14 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/networking/alb/variables.tf: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------------------------------------- 2 | # REQUIRED PARAMETERS 3 | # You must provide a value for each of these parameters. 4 | # --------------------------------------------------------------------------------------------------------------------- 5 | 6 | variable "alb_name" { 7 | description = "The name to use for this ALB" 8 | type = string 9 | } 10 | 11 | variable "subnet_ids" { 12 | description = "The subnet IDs to deploy to" 13 | type = list(string) 14 | } 15 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/services/hello-world-app/README.md: -------------------------------------------------------------------------------- 1 | # Hello World App 2 | 3 | This folder contains an example [Terraform](https://www.terraform.io/) configuration that defines a module for 4 | deploying a simple "Hello, World app" across a cluster of web servers (using [EC2](https://aws.amazon.com/ec2/) 5 | and [Auto Scaling](https://aws.amazon.com/autoscaling/)) with a load balancer (using 6 | [ELB](https://aws.amazon.com/elasticloadbalancing/)) in an [Amazon Web Services (AWS) 7 | account](http://aws.amazon.com/). 8 | 9 | For more info, please see Chapter 10, "How to use Terraform as a Team", of 10 | *[Terraform: Up and Running](http://www.terraformupandrunning.com)*. 11 | 12 | ## Quick start 13 | 14 | Terraform modules are not meant to be deployed directly. Instead, you should be including them in other Terraform 15 | configurations. See [live/stage/services/hello-world-app](../../../live/stage/services/hello-world-app) and 16 | [live/prod/services/hello-world-app](../../../live/prod/services/hello-world-app) for examples. -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/services/hello-world-app/dependencies.tf: -------------------------------------------------------------------------------- 1 | data "terraform_remote_state" "db" { 2 | count = var.mysql_config == null ? 1 : 0 3 | 4 | backend = "s3" 5 | 6 | config = { 7 | bucket = var.db_remote_state_bucket 8 | key = var.db_remote_state_key 9 | region = "us-east-2" 10 | } 11 | } 12 | 13 | data "aws_vpc" "default" { 14 | count = var.vpc_id == null ? 1 : 0 15 | default = true 16 | } 17 | 18 | data "aws_subnets" "default" { 19 | count = var.subnet_ids == null ? 1 : 0 20 | 21 | filter { 22 | name = "vpc-id" 23 | values = [local.vpc_id] 24 | } 25 | 26 | } 27 | 28 | locals { 29 | mysql_config = ( 30 | var.mysql_config == null 31 | ? data.terraform_remote_state.db[0].outputs 32 | : var.mysql_config 33 | ) 34 | 35 | vpc_id = ( 36 | var.vpc_id == null 37 | ? data.aws_vpc.default[0].id 38 | : var.vpc_id 39 | ) 40 | 41 | subnet_ids = ( 42 | var.subnet_ids == null 43 | ? data.aws_subnets.default[0].ids 44 | : var.subnet_ids 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/services/hello-world-app/outputs.tf: -------------------------------------------------------------------------------- 1 | output "alb_dns_name" { 2 | value = module.alb.alb_dns_name 3 | description = "The domain name of the load balancer" 4 | } 5 | 6 | output "asg_name" { 7 | value = module.asg.asg_name 8 | description = "The name of the Auto Scaling Group" 9 | } 10 | 11 | output "instance_security_group_id" { 12 | value = module.asg.instance_security_group_id 13 | description = "The ID of the EC2 Instance Security Group" 14 | } -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/modules/services/hello-world-app/user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cat > index.html <${server_text} 5 |

DB address: ${db_address}

6 |

DB port: ${db_port}

7 | EOF 8 | 9 | nohup busybox httpd -f -p ${server_port} & 10 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/test/asg_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gruntwork-io/terratest/modules/random" 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "testing" 8 | ) 9 | 10 | func TestAsgExample(t *testing.T) { 11 | t.Parallel() 12 | 13 | terraformOptions := &terraform.Options{ 14 | // You should update this relative path to point at your asg 15 | // example directory! 16 | TerraformDir: "../examples/asg", 17 | Vars: map[string]interface{}{ 18 | "cluster_name": fmt.Sprintf("test-%s", random.UniqueId()), 19 | }, 20 | } 21 | 22 | defer terraform.Destroy(t, terraformOptions) 23 | terraform.InitAndApply(t, terraformOptions) 24 | } 25 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/test/go_sanity_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestGoIsWorking(t *testing.T) { 9 | fmt.Println() 10 | fmt.Println("If you see this text, it's working!") 11 | fmt.Println() 12 | } 13 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/test/mysql_example_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gruntwork-io/terratest/modules/random" 6 | "github.com/gruntwork-io/terratest/modules/terraform" 7 | "testing" 8 | ) 9 | 10 | func TestMySqlExample(t *testing.T) { 11 | t.Parallel() 12 | 13 | terraformOptions := &terraform.Options{ 14 | // You should update this relative path to point at your mysql 15 | // example directory! 16 | TerraformDir: "../examples/mysql", 17 | Vars: map[string]interface{}{ 18 | "db_name": fmt.Sprintf("test_%s", random.UniqueId()), 19 | "db_username": "admin", 20 | "db_password": "password", 21 | }, 22 | } 23 | 24 | defer terraform.Destroy(t, terraformOptions) 25 | terraform.InitAndApply(t, terraformOptions) 26 | } 27 | -------------------------------------------------------------------------------- /code/terraform/10-terraform-team/test/test_helpers.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | // To avoid wasting lots of time constantly creating and deleting S3 buckets for the tests that need to store state in 9 | // an S3 backend, some of the tests require that you create the S3 bucket ahead of time and set the bucket name and 10 | // region as environment variables. Note that these env vars are also read automatically by terragrunt.hcl files. 11 | const TerraformStateBucketForTestEnvVarName = "TEST_STATE_S3_BUCKET" 12 | const TerraformStateRegionForTestEnvVarName = "TEST_STATE_REGION" 13 | 14 | // Get the value of the environment variable with the given name. If that environment variable is not set, fail the 15 | // test. 16 | func GetRequiredEnvVar(t *testing.T, envVarName string) string { 17 | envVarValue := os.Getenv(envVarName) 18 | if envVarValue == "" { 19 | t.Fatalf("Required environment variable '%s' is not set", envVarName) 20 | } 21 | return envVarValue 22 | } 23 | --------------------------------------------------------------------------------