{"id":20112817,"url":"https://github.com/cloudacademy/terraform-aws","last_synced_at":"2025-10-09T00:24:04.306Z","repository":{"id":37537790,"uuid":"390161385","full_name":"cloudacademy/terraform-aws","owner":"cloudacademy","description":"Terraform 1.x AWS Course","archived":false,"fork":false,"pushed_at":"2024-07-24T09:22:22.000Z","size":1798,"stargazers_count":125,"open_issues_count":1,"forks_count":297,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-05-25T13:06:40.598Z","etag":null,"topics":["aws","cloudacademy","devops","iac","terraform"],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudacademy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-28T00:08:08.000Z","updated_at":"2025-05-13T17:50:53.000Z","dependencies_parsed_at":"2024-07-24T11:00:06.679Z","dependency_job_id":null,"html_url":"https://github.com/cloudacademy/terraform-aws","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/cloudacademy/terraform-aws","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudacademy%2Fterraform-aws","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudacademy%2Fterraform-aws/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudacademy%2Fterraform-aws/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudacademy%2Fterraform-aws/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudacademy","download_url":"https://codeload.github.com/cloudacademy/terraform-aws/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudacademy%2Fterraform-aws/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279000718,"owners_count":26082879,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","cloudacademy","devops","iac","terraform"],"created_at":"2024-11-13T18:22:32.330Z","updated_at":"2025-10-09T00:24:04.268Z","avatar_url":"https://github.com/cloudacademy.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/cloudacademy/terraform-aws)\n\n# CloudAcademy Terraform 1.x AWS Course\nThis repo contains example Terraform configurations for building AWS infrastructure.\n\n## AWS Exercises\nThe exercises directory contains a set of different AWS infrastructure provisioning exercises.\n\n### Exercise 1\nCreate a simple AWS VPC spanning 2 AZs. Public subnets will be created, together with an internet gateway, and single route table. A t3.micro instance will be deployed and installed with Nginx for web serving. Security groups will be created and deployed to secure all network traffic between the various components.\n\nhttps://github.com/cloudacademy/terraform-aws/tree/main/exercises/exercise1\n\n![AWS Architecture](./doc/AWS-VPC-Nginx.png)\n\n#### Project Structure\n\n```\n├── main.tf\n├── outputs.tf\n├── terraform.tfvars\n└── variables.tf\n```\n\n#### TF Variable Notes\n\n- `workstation_ip`: The Terraform variable `workstation_ip` represents your workstation's external perimeter public IP address, and needs to be represented using CIDR notation. This IP address is used later on within the Terraform infrastructure provisioning process to lock down SSH access on the instance(s) (provisioned by Terraform) - this is a security safety measure to prevent anyone else from attempting SSH access. The public IP address will be different and unique for each user - the easiest way to get this address is to type \"what is my ip address\" in a google search. As an example response, lets say Google responded with `202.10.23.16` - then the value assigned to the Terraform `workstation_ip` variable would be `202.10.23.16/32` (note the `/32` is this case indicates that it is a single IP address).\n\n- `key_name`: The Terraform variable `key_name` represents the AWS SSH Keypair name that will be used to allow SSH access to the Bastion Host that gets created at provisioning time. If you intend to use the Bastion Host - then you will need to create your own SSH Keypair (typically done within the AWS EC2 console) ahead of time.\n\n  - The required Terraform `workstation_ip` and `key_name` variables can be established multiple ways, one of which is to prefix the variable name with `TF_VAR_` and have it then set as an environment variable within your shell, something like:\n\n  - **Linux**: `export TF_VAR_workstation_ip=202.10.23.16/32` and `export TF_VAR_key_name=your_ssh_key_name`\n\n  - **Windows**: `set TF_VAR_workstation_ip=202.10.23.16/32` and `set TF_VAR_key_name=your_ssh_key_name`\n\n- Terraform environment variables are documented here:\n[https://www.terraform.io/cli/config/environment-variables](https://www.terraform.io/cli/config/environment-variables)\n\n### Exercise 2\nCreate an advanced AWS VPC spanning 2 AZs with both public and private subnets. An internet gateway and NAT gateway will be deployed into it. Public and private route tables will be established. An application load balancer (ALB) will be installed which will load balance traffic across an auto scaling group (ASG) of Nginx web servers. Security groups will be created and deployed to secure all network traffic between the various components.\n\nhttps://github.com/cloudacademy/terraform-aws/tree/main/exercises/exercise2\n\n![AWS Architecture](./doc/AWS-VPC-ASG-Nginx.png)\n\n#### Project Structure\n\n```\n├── ec2.userdata\n├── main.tf\n├── outputs.tf\n├── terraform.tfvars\n└── variables.tf\n```\n\n#### TF Variable Notes\n\n- `workstation_ip`: The Terraform variable `workstation_ip` represents your workstation's external perimeter public IP address, and needs to be represented using CIDR notation. This IP address is used later on within the Terraform infrastructure provisioning process to lock down SSH access on the instance(s) (provisioned by Terraform) - this is a security safety measure to prevent anyone else from attempting SSH access. The public IP address will be different and unique for each user - the easiest way to get this address is to type \"what is my ip address\" in a google search. As an example response, lets say Google responded with `202.10.23.16` - then the value assigned to the Terraform `workstation_ip` variable would be `202.10.23.16/32` (note the `/32` is this case indicates that it is a single IP address).\n\n- `key_name`: The Terraform variable `key_name` represents the AWS SSH Keypair name that will be used to allow SSH access to the Bastion Host that gets created at provisioning time. If you intend to use the Bastion Host - then you will need to create your own SSH Keypair (typically done within the AWS EC2 console) ahead of time.\n\n  - The required Terraform `workstation_ip` and `key_name` variables can be established multiple ways, one of which is to prefix the variable name with `TF_VAR_` and have it then set as an environment variable within your shell, something like:\n\n  - **Linux**: `export TF_VAR_workstation_ip=202.10.23.16/32` and `export TF_VAR_key_name=your_ssh_key_name`\n\n  - **Windows**: `set TF_VAR_workstation_ip=202.10.23.16/32` and `set TF_VAR_key_name=your_ssh_key_name`\n\n- Terraform environment variables are documented here:\n[https://www.terraform.io/cli/config/environment-variables](https://www.terraform.io/cli/config/environment-variables)\n\n### Exercise 3\nSame AWS architecture as used in Exercise 2. This exercise demonstrates a different Terraform technique, using the Terraform \"count\" meta argument, for configuring the public and private subnets as well as their respective route tables.\n\nhttps://github.com/cloudacademy/terraform-aws/tree/main/exercises/exercise3\n\n![AWS Architecture](./doc/AWS-VPC-ASG-Nginx.png)\n\n#### Project Structure\n\n```\n├── ec2.userdata\n├── main.tf\n├── outputs.tf\n├── terraform.tfvars\n└── variables.tf\n```\n\n#### TF Variable Notes\n\n- `workstation_ip`: The Terraform variable `workstation_ip` represents your workstation's external perimeter public IP address, and needs to be represented using CIDR notation. This IP address is used later on within the Terraform infrastructure provisioning process to lock down SSH access on the instance(s) (provisioned by Terraform) - this is a security safety measure to prevent anyone else from attempting SSH access. The public IP address will be different and unique for each user - the easiest way to get this address is to type \"what is my ip address\" in a google search. As an example response, lets say Google responded with `202.10.23.16` - then the value assigned to the Terraform `workstation_ip` variable would be `202.10.23.16/32` (note the `/32` is this case indicates that it is a single IP address).\n\n- `key_name`: The Terraform variable `key_name` represents the AWS SSH Keypair name that will be used to allow SSH access to the Bastion Host that gets created at provisioning time. If you intend to use the Bastion Host - then you will need to create your own SSH Keypair (typically done within the AWS EC2 console) ahead of time.\n\n  - The required Terraform `workstation_ip` and `key_name` variables can be established multiple ways, one of which is to prefix the variable name with `TF_VAR_` and have it then set as an environment variable within your shell, something like:\n\n  - **Linux**: `export TF_VAR_workstation_ip=202.10.23.16/32` and `export TF_VAR_key_name=your_ssh_key_name`\n\n  - **Windows**: `set TF_VAR_workstation_ip=202.10.23.16/32` and `set TF_VAR_key_name=your_ssh_key_name`\n\n- Terraform environment variables are documented here:\n[https://www.terraform.io/cli/config/environment-variables](https://www.terraform.io/cli/config/environment-variables)\n\n### Exercise 4\nCreate an advanced AWS VPC to host a fully functioning cloud native application.\n\n![Cloud Native Application](/doc/voteapp.png)\n\nThe VPC will span 2 AZs, and have both public and private subnets. An internet gateway and NAT gateway will be deployed into it. Public and private route tables will be established. An application load balancer (ALB) will be installed which will load balance traffic across an auto scaling group (ASG) of Nginx web servers installed with the cloud native application frontend and API. A database instance running MongoDB will be installed in the private zone. Security groups will be created and deployed to secure all network traffic between the various components.\n\nFor demonstration purposes only - both the frontend and the API will be deployed to the same set of ASG instances - to reduce running costs.\n\nhttps://github.com/cloudacademy/terraform-aws/tree/main/exercises/exercise4\n\n![AWS Architecture](/doc/AWS-VPC-FullApp.png)\n\nThe auto scaling web application layer bootstraps itself with both the [Frontend](https://github.com/cloudacademy/voteapp-frontend-react-2020) and [API](https://github.com/cloudacademy/voteapp-api-go) components by pulling down their **latest** respective releases from the following repos:\n\n* Frontend: https://github.com/cloudacademy/voteapp-frontend-react-2020/releases/latest\n\n* API: https://github.com/cloudacademy/voteapp-api-go/releases/latest\n\nThe bootstrapping process for the [Frontend](https://github.com/cloudacademy/voteapp-frontend-react-2020) and [API](https://github.com/cloudacademy/voteapp-api-go) components is codified within a ```template_cloudinit_config``` block located in the application module's [main.tf](./modules/application/main.tf) file:\n\n```terraform\ndata \"template_cloudinit_config\" \"config\" {\n  gzip          = false\n  base64_encode = false\n\n  #userdata\n  part {\n    content_type = \"text/x-shellscript\"\n    content      = \u003c\u003c-EOF\n    #! /bin/bash\n    apt-get -y update\n    apt-get -y install nginx\n    apt-get -y install jq\n\n    ALB_DNS=${aws_lb.alb1.dns_name}\n    MONGODB_PRIVATEIP=${var.mongodb_ip}\n    \n    mkdir -p /tmp/cloudacademy-app\n    cd /tmp/cloudacademy-app\n\n    echo ===========================\n    echo FRONTEND - download latest release and install...\n    mkdir -p ./voteapp-frontend-react-2020\n    pushd ./voteapp-frontend-react-2020\n    curl -sL https://api.github.com/repos/cloudacademy/voteapp-frontend-react-2020/releases/latest | jq -r '.assets[0].browser_download_url' | xargs curl -OL\n    INSTALL_FILENAME=$(curl -sL https://api.github.com/repos/cloudacademy/voteapp-frontend-react-2020/releases/latest | jq -r '.assets[0].name')\n    tar -xvzf $INSTALL_FILENAME\n    rm -rf /var/www/html\n    cp -R build /var/www/html\n    cat \u003e /var/www/html/env-config.js \u003c\u003c EOFF\n    window._env_ = {REACT_APP_APIHOSTPORT: \"$ALB_DNS\"}\n    EOFF\n    popd\n\n    echo ===========================\n    echo API - download latest release, install, and start...\n    mkdir -p ./voteapp-api-go\n    pushd ./voteapp-api-go\n    curl -sL https://api.github.com/repos/cloudacademy/voteapp-api-go/releases/latest | jq -r '.assets[] | select(.name | contains(\"linux-amd64\")) | .browser_download_url' | xargs curl -OL\n    INSTALL_FILENAME=$(curl -sL https://api.github.com/repos/cloudacademy/voteapp-api-go/releases/latest | jq -r '.assets[] | select(.name | contains(\"linux-amd64\")) | .name')\n    tar -xvzf $INSTALL_FILENAME\n    #start the API up...\n    MONGO_CONN_STR=mongodb://$MONGODB_PRIVATEIP:27017/langdb ./api \u0026\n    popd\n\n    systemctl restart nginx\n    systemctl status nginx\n    echo fin v1.00!\n\n    EOF    \n  }\n}\n```\n\n#### ALB Target Group Configuration\n\nThe ALB will configured with a single listener (port 80). 2 target groups will be established. The frontend target group points to the Nginx web server (port 80). The API target group points to the custom API service (port 8080). \n\n![AWS Architecture](/doc/AWS-VPC-FullApp-TargetGrps.png)\n\n#### Project Structure\n\n```\n├── main.tf\n├── modules\n│   ├── application\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── bastion\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── network\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── security\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   └── storage\n│       ├── install.sh\n│       ├── main.tf\n│       ├── outputs.tf\n│       └── vars.tf\n├── outputs.tf\n├── terraform.tfvars\n└── variables.tf\n```\n\n#### TF Variable Notes\n\n- `workstation_ip`: The Terraform variable `workstation_ip` represents your workstation's external perimeter public IP address, and needs to be represented using CIDR notation. This IP address is used later on within the Terraform infrastructure provisioning process to lock down SSH access on the instance(s) (provisioned by Terraform) - this is a security safety measure to prevent anyone else from attempting SSH access. The public IP address will be different and unique for each user - the easiest way to get this address is to type \"what is my ip address\" in a google search. As an example response, lets say Google responded with `202.10.23.16` - then the value assigned to the Terraform `workstation_ip` variable would be `202.10.23.16/32` (note the `/32` is this case indicates that it is a single IP address).\n\n- `key_name`: The Terraform variable `key_name` represents the AWS SSH Keypair name that will be used to allow SSH access to the Bastion Host that gets created at provisioning time. If you intend to use the Bastion Host - then you will need to create your own SSH Keypair (typically done within the AWS EC2 console) ahead of time.\n\n  - The required Terraform `workstation_ip` and `key_name` variables can be established multiple ways, one of which is to prefix the variable name with `TF_VAR_` and have it then set as an environment variable within your shell, something like:\n\n  - **Linux**: `export TF_VAR_workstation_ip=202.10.23.16/32` and `export TF_VAR_key_name=your_ssh_key_name`\n\n  - **Windows**: `set TF_VAR_workstation_ip=202.10.23.16/32` and `set TF_VAR_key_name=your_ssh_key_name`\n\n- Terraform environment variables are documented here:\n[https://www.terraform.io/cli/config/environment-variables](https://www.terraform.io/cli/config/environment-variables)\n\n### Exercise 5\nRefactoring of the Cloud Native Application (excercise 4) to use [Ansible](https://www.ansible.com/) for configuration management.\n\n![Cloud Native Application](/doc/voteapp.png)\n\nThe VPC will span 2 AZs, and have both public and private subnets. An internet gateway and NAT gateway will be deployed into it. Public and private route tables will be established. An application load balancer (ALB) will be installed which will load balance traffic across an auto scaling group (ASG) of Nginx web servers installed with the cloud native application frontend and API. A database instance running MongoDB will be installed in the private zone. Security groups will be created and deployed to secure all network traffic between the various components.\n\nFor demonstration purposes only - both the frontend and the API will be deployed to the same set of ASG instances - to reduce running costs.\n\nhttps://github.com/cloudacademy/terraform-aws/tree/main/exercises/exercise5\n\n![AWS Architecture](/doc/AWS-VPC-FullApp.png)\n\nThe auto scaling web application layer bootstraps itself with both the [Frontend](https://github.com/cloudacademy/voteapp-frontend-react-2020) and [API](https://github.com/cloudacademy/voteapp-api-go) components by pulling down their **latest** respective releases from the following repos:\n\n* Frontend: https://github.com/cloudacademy/voteapp-frontend-react-2020/releases/latest\n\n* API: https://github.com/cloudacademy/voteapp-api-go/releases/latest\n\nThe bootstrapping process for the [Frontend](https://github.com/cloudacademy/voteapp-frontend-react-2023) and [API](https://github.com/cloudacademy/voteapp-api-go) components is now performed by Ansible. An Ansible playbook is executed from within the root [main.tf](.exercises/exercise5/main.tf) file:\n\n```terraform\nresource \"null_resource\" \"ansible\" {\n  provisioner \"local-exec\" {\n    interpreter = [\"/bin/bash\", \"-c\"]\n    working_dir = \"${path.module}/ansible\"\n    command     = \u003c\u003cEOT\n      sleep 120 #time to allow VMs to come online and stabilize\n      mkdir -p ./logs\n\n      sed \\\n      -e 's/BASTION_IP/${module.bastion.public_ip}/g' \\\n      -e 's/WEB_IPS/${join(\"\\\\n\", module.application.private_ips)}/g' \\\n      -e 's/MONGO_IP/${module.storage.private_ip}/g' \\\n      ./templates/hosts \u003e hosts\n\n      sed \\\n      -e 's/BASTION_IP/${module.bastion.public_ip}/g' \\\n      -e 's/SSH_KEY_NAME/${var.key_name}/g' \\\n      ./templates/ssh_config \u003e ssh_config\n\n      #required for macos only\n      export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES\n\n      #ANSIBLE\n      ansible-playbook -v \\\n      -i hosts \\\n      --extra-vars \"ALB_DNS=${module.application.dns_name}\" \\\n      --extra-vars \"MONGODB_PRIVATEIP=${module.storage.private_ip}\" \\\n      ./playbooks/master.yml\n      echo finished!\n    EOT\n  }\n\n  depends_on = [\n    module.bastion,\n    module.application\n  ]\n}\n```\n\n#### ALB Target Group Configuration\n\nThe ALB will configured with a single listener (port 80). 2 target groups will be established. The frontend target group points to the Nginx web server (port 80). The API target group points to the custom API service (port 8080). \n\n![AWS Architecture](/doc/AWS-VPC-FullApp-TargetGrps.png)\n\n#### Project Structure\n\n```\n├── main.tf\n├── ansible\n│   ├── ansible.cfg\n│   ├── logs\n│   │   └── ansible.log\n│   ├── playbooks\n│   │   ├── database.yml\n│   │   ├── deployapp.yml\n│   │   ├── files\n│   │   │   ├── api.sh\n│   │   │   ├── db.sh\n│   │   │   └── frontend.sh\n│   │   └── master.yml\n│   └── templates\n│       ├── hosts\n│       └── ssh_config\n├── modules\n│   ├── application\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── bastion\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── network\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   ├── security\n│   │   ├── main.tf\n│   │   ├── outputs.tf\n│   │   └── vars.tf\n│   └── storage\n│       ├── install.sh\n│       ├── main.tf\n│       ├── outputs.tf\n│       └── vars.tf\n├── outputs.tf\n├── terraform.tfvars\n└── variables.tf\n```\n\n#### TF Variable Notes\n\n- `workstation_ip`: The Terraform variable `workstation_ip` represents your workstation's external perimeter public IP address, and needs to be represented using CIDR notation. This IP address is used later on within the Terraform infrastructure provisioning process to lock down SSH access on the instance(s) (provisioned by Terraform) - this is a security safety measure to prevent anyone else from attempting SSH access. The public IP address will be different and unique for each user - the easiest way to get this address is to type \"what is my ip address\" in a google search. As an example response, lets say Google responded with `202.10.23.16` - then the value assigned to the Terraform `workstation_ip` variable would be `202.10.23.16/32` (note the `/32` is this case indicates that it is a single IP address).\n\n- `key_name`: The Terraform variable `key_name` represents the AWS SSH Keypair name that will be used to allow SSH access to the Bastion Host that gets created at provisioning time. If you intend to use the Bastion Host - then you will need to create your own SSH Keypair (typically done within the AWS EC2 console) ahead of time.\n\n  - The required Terraform `workstation_ip` and `key_name` variables can be established multiple ways, one of which is to prefix the variable name with `TF_VAR_` and have it then set as an environment variable within your shell, something like:\n\n  - **Linux**: `export TF_VAR_workstation_ip=202.10.23.16/32` and `export TF_VAR_key_name=your_ssh_key_name`\n\n  - **Windows**: `set TF_VAR_workstation_ip=202.10.23.16/32` and `set TF_VAR_key_name=your_ssh_key_name`\n\n- Terraform environment variables are documented here:\n[https://www.terraform.io/cli/config/environment-variables](https://www.terraform.io/cli/config/environment-variables)\n\n### Exercise 6\nLaunch an EKS cluster and deploy a pre-built cloud native web app.\n\n![Stocks App](/doc/stocks.png)\n\nThe following EKS architecture will be provisioned using Terraform:\n\n![EKS Cloud Native Application](/doc/eks.png)\n\nThe cloud native web app that gets deployed is based on the following codebase:\n\n- https://github.com/cloudacademy/stocks-app\n- https://github.com/cloudacademy/stocks-api\n\nThe following public AWS **modules** are used to launch the EKS cluster:\n\n- [VPC](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest)\n- [EKS](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest)\n\nAdditionally, the following **providers** are utilised:\n\n- [hashicorp/helm](https://registry.terraform.io/providers/hashicorp/helm/latest)\n- [hashicorp/null](https://registry.terraform.io/providers/hashicorp/null/latest)\n\nThe EKS cluster will be provisioned with 2 worker nodes based on m5.large **spot** instances. This configuration is suitable for the demonstration purposes of this exercise. Production environments are likely more suited to **on-demand always on** instances.\n\nThe cloud native web app deployed is configured within the `./k8s` directory, and is installed automatically using the following null resource configuration:\n\n```terraform\nresource \"null_resource\" \"deploy_app\" {\n  triggers = {\n    always_run = \"${timestamp()}\"\n  }\n\n  provisioner \"local-exec\" {\n    interpreter = [\"/bin/bash\", \"-c\"]\n    working_dir = path.module\n    command     = \u003c\u003cEOT\n      echo deploying app...\n      ./k8s/app.install.sh\n    EOT\n  }\n\n  depends_on = [\n    helm_release.nginx_ingress\n  ]\n}\n```\n\nThe Helm provider is used to automatically install the Nginx Ingress Controller at provisioning time:\n\n```terraform\nprovider \"helm\" {\n  kubernetes {\n    host                   = module.eks.cluster_endpoint\n    cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)\n\n    exec {\n      api_version = \"client.authentication.k8s.io/v1beta1\"\n      command     = \"aws\"\n      args        = [\"eks\", \"get-token\", \"--cluster-name\", module.eks.cluster_name]\n    }\n  }\n}\n\nresource \"helm_release\" \"nginx_ingress\" {\n  name = \"nginx-ingress\"\n\n  repository       = \"https://helm.nginx.com/stable\"\n  chart            = \"nginx-ingress\"\n  namespace        = \"nginx-ingress\"\n  create_namespace = true\n\n  set {\n    name  = \"service.type\"\n    value = \"ClusterIP\"\n  }\n\n  set {\n    name  = \"controller.service.name\"\n    value = \"nginx-ingress-controller\"\n  }\n}\n```\n\n### Exercise 7\nDeploy a set of serverless apps using API Gateway and Lambda Functions.\n\n![Stocks App](/doc/stocks.png)\n\nThe following EKS architecture will be provisioned using Terraform:\n\n![EKS Cloud Native Application](/doc/eks.png)\n\nThe cloud native web app that gets deployed is based on the following codebase:\n\n- https://github.com/cloudacademy/stocks-app\n- https://github.com/cloudacademy/stocks-api\n\nThe following public AWS **modules** are used to launch the EKS cluster:\n\n- [VPC](https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest)\n- [EKS](https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest)\n\nAdditionally, the following **providers** are utilised:\n\n- [hashicorp/helm](https://registry.terraform.io/providers/hashicorp/helm/latest)\n- [hashicorp/null](https://registry.terraform.io/providers/hashicorp/null/latest)\n\nThe EKS cluster will be provisioned with 2 worker nodes based on m5.large **spot** instances. This configuration is suitable for the demonstration purposes of this exercise. Production environments are likely more suited to **on-demand always on** instances.\n\nThe cloud native web app deployed is configured within the `./k8s` directory, and is installed automatically using the following null resource configuration:\n\n```terraform\nresource \"null_resource\" \"deploy_app\" {\n  triggers = {\n    always_run = \"${timestamp()}\"\n  }\n\n  provisioner \"local-exec\" {\n    interpreter = [\"/bin/bash\", \"-c\"]\n    working_dir = path.module\n    command     = \u003c\u003cEOT\n      echo deploying app...\n      ./k8s/app.install.sh\n    EOT\n  }\n\n  depends_on = [\n    helm_release.nginx_ingress\n  ]\n}\n```\n\nThe Helm provider is used to automatically install the Nginx Ingress Controller at provisioning time:\n\n```terraform\nprovider \"helm\" {\n  kubernetes {\n    host                   = module.eks.cluster_endpoint\n    cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data)\n\n    exec {\n      api_version = \"client.authentication.k8s.io/v1beta1\"\n      command     = \"aws\"\n      args        = [\"eks\", \"get-token\", \"--cluster-name\", module.eks.cluster_name]\n    }\n  }\n}\n\nresource \"helm_release\" \"nginx_ingress\" {\n  name = \"nginx-ingress\"\n\n  repository       = \"https://helm.nginx.com/stable\"\n  chart            = \"nginx-ingress\"\n  namespace        = \"nginx-ingress\"\n  create_namespace = true\n\n  set {\n    name  = \"service.type\"\n    value = \"ClusterIP\"\n  }\n\n  set {\n    name  = \"controller.service.name\"\n    value = \"nginx-ingress-controller\"\n  }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudacademy%2Fterraform-aws","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudacademy%2Fterraform-aws","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudacademy%2Fterraform-aws/lists"}