├── .gitignore ├── README.md ├── challenge-1 ├── README.md └── resources │ ├── frontend.tf │ ├── provider.tf │ └── webapp-service.tf ├── challenge-2 ├── README.md └── resources │ ├── db-dashboard.tf │ ├── db.tf │ ├── mariadb-custom-image.tf │ ├── mariadb-volume.tf │ ├── php-httpd-image.tf │ ├── private-network.tf │ └── webserver.tf └── challenge-3 ├── README.md └── resources ├── citadel.tf ├── ec2_elastic_ip.tf ├── ssh-key-citadel.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .infracost 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Terraform Challenges Series](https://kodekloud.com/courses/terraform-challenges/) 2 | 3 | These are fun and exciting Terraform challenges from the **[Terraform Challenges](https://kodekloud.com/courses/terraform-challenges/)** Series hosted on **KodeKloud** platform available for free. 4 | 5 | These challenges are specially designed to give you more hands-on and challenges that would help you excel in Terraform. 6 | 7 | # Sections 8 | 9 | - [Challenge-1](https://kodekloud.com/topic/terraform-challenge-1/) 10 | - [Solutions](./challenge-1) 11 | 12 | - [Challenge-2](https://kodekloud.com/topic/terraform-challenge-2/) 13 | - [Solutions](./challenge-2) 14 | 15 | - [Challenge-3](https://kodekloud.com/topic/terraform-challenge-3/) 16 | - [Solutions](./challenge-3) 17 | 18 | -------------------------------------------------------------------------------- /challenge-1/README.md: -------------------------------------------------------------------------------- 1 | # Challenge 1 2 | 3 | In this challenge we will deploy several Kubernetes resources using terraform. If you don't know Kubernetes, this is going to be somewhat confusing as the resource schema really only makes sense if you understand Kubernetes. 4 | 5 | Note that `kubectl` is installed on the host, so you can check your deployed resources in the usual way. 6 | 7 | Utilize `/root/terraform_challenge` directory to store your Terraform configuration files. 8 | 9 | We will create the resources in the order of their dependencies, thus you may run plan/apply at any stage, or wait till the end. 10 | 11 | Note that these challenges do not use an embedded VSCode IDE, therefore we must use the venerable `vi` to create and edit files. Now you can put all the resources into a single file e.g. `main.tf` or as shown here in separate files. Terraform doesn't care! 12 | 13 | 1.
14 | controlplane
Terraform version: 1.1.5 installed on controlplane?
15 | 16 | ```bash 17 | which terraform 18 | ``` 19 | 20 | > Nothing! Therefore we must install it. Note that unzip is also not installed, and we need that too! 21 | 22 | ```bash 23 | apt update 24 | apt install unzip -y 25 | curl -L -o /tmp/terraform_1.1.5_linux_amd64.zip https://releases.hashicorp.com/terraform/1.1.5/terraform_1.1.5_linux_amd64.zip 26 | unzip -d /usr/local/bin /tmp/terraform_1.1.5_linux_amd64.zip 27 | ``` 28 |
29 | 1.
30 | kubernetes-provider
Configure terraform and provider settings within provider.tf file
31 | 32 | ```bash 33 | cd /root/terraform_challenge 34 | ``` 35 | 36 | You should now refer to the documentation for this provider. Go to the [Terraform Registry](https://registry.terraform.io/) and paste `hashicorp/kubernetes` into the search bar. This will give you the latest version, so adjust the URL in your browser to `2.11.0` 37 | 38 | Click on the **USE PROVIDER** button for the configuration block. Copy this, and use `vi` to create [provider.tf](./resources/provider.tf). Paste in and adjust as per the question requirements. 39 | 40 | Now we can initialize the provider 41 | 42 | ```bash 43 | terraform init 44 | ``` 45 |
46 | 1.
47 | frontend
Create a terraform resource frontend for kubernetes deployment
48 | 49 | Refer to the provider documentation for [kubernetes_deployment](https://registry.terraform.io/providers/hashicorp/kubernetes/2.11.0/docs/resources/deployment) 50 | 51 | If you know Kubernetes, you will see that the resource schema is logically arranged very similarly to the corresponding YAML manifest. 52 | 53 | Using `vi`, create [frontend.tf](./resources/frontend.tf) 54 | 55 |
56 | 1.
57 | webapp-service
Create a terraform resource webapp-service for kubernetes service
58 | 59 | Refer to the provider documentation for [kubernetes_service](https://registry.terraform.io/providers/hashicorp/kubernetes/2.11.0/docs/resources/service) 60 | 61 | Now this one's a bit tricky. We need to associate the service with the label assigned to the pods of the deployment. This means that the section 62 | 63 | ``` 64 | spec { 65 | selector { 66 | 67 | } 68 | } 69 | ``` 70 | 71 | ...is going to be rather complex as we have to drill right down into the pod template spec to get the label value. 72 | You could cheat and just put `name = "webapp"` but that defeats the object and doesn't create a dependency between deployment and service. 73 | 74 | Using `vi`, create [webapp-service.tf](./resources/webapp-service.tf) 75 | 76 | 1.
77 | DEPLOY! 78 | 79 | ```bash 80 | terraform plan 81 | terraform apply 82 | ``` 83 |
84 | 85 | 86 | -------------------------------------------------------------------------------- /challenge-1/resources/frontend.tf: -------------------------------------------------------------------------------- 1 | resource "kubernetes_deployment" "frontend" { 2 | metadata { 3 | name = "frontend" 4 | labels = { 5 | name = "frontend" 6 | } 7 | } 8 | spec { 9 | replicas = 4 10 | selector { 11 | match_labels = { 12 | name = "webapp" 13 | } 14 | } 15 | template { 16 | metadata { 17 | labels = { 18 | name = "webapp" 19 | } 20 | } 21 | spec { 22 | container { 23 | image = "kodekloud/webapp-color:v1" 24 | name = "simple-webapp" 25 | port { 26 | container_port = 8080 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /challenge-1/resources/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | kubernetes = { 4 | source = "hashicorp/kubernetes" 5 | version = "2.11.0" 6 | } 7 | } 8 | } 9 | 10 | provider "kubernetes" { 11 | config_path = "/root/.kube/config" 12 | } -------------------------------------------------------------------------------- /challenge-1/resources/webapp-service.tf: -------------------------------------------------------------------------------- 1 | resource "kubernetes_service" "webapp-service" { 2 | metadata { 3 | name = "webapp-service" 4 | } 5 | spec { 6 | selector = { 7 | name = kubernetes_deployment.frontend.spec.0.template.0.metadata.0.labels.name 8 | } 9 | port { 10 | port = 8080 11 | target_port = 8080 12 | node_port = 30080 13 | } 14 | 15 | type = "NodePort" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /challenge-2/README.md: -------------------------------------------------------------------------------- 1 | # Challenge 2 2 | 3 | In this challenge we will implement a simple LAMP stack using terraform and docker. 4 | 5 | Utilize `/root/code/terraform-challenges/challenge2` directory to store your Terraform configuration files. 6 | 7 | We will create the resources in the order of their dependencies, thus you may run plan/apply at any stage, or wait till the end. 8 | 9 | Note that these challenges do not use an embedded VSCode IDE, therefore we must use the venerable `vi` to create and edit files. Now you can put all the resources into a single file e.g. `main.tf` or as shown here in separate files. Terraform doesn't care! 10 | 11 | 1.
12 | terraform-jump-host
Install terraform binary version=1.1.5 on iac-server
13 | 14 | ```bash 15 | curl -L -o /tmp/terraform_1.1.5_linux_amd64.zip https://releases.hashicorp.com/terraform/1.1.5/terraform_1.1.5_linux_amd64.zip 16 | unzip -d /usr/local/bin /tmp/terraform_1.1.5_linux_amd64.zip 17 | ``` 18 |
19 | 1.
20 | docker
Docker provider has already been configured using kreuzwerker/docker provider.
Check out the provider.tf given at /root/code/terraform-challenges/challenge2
21 | 22 | ```bash 23 | cd /root/code/terraform-challenges/challenge2 24 | cat provider.tf 25 | ``` 26 | 27 | Let's initialize the provider now. 28 | 29 | ``` 30 | terraform init 31 | ``` 32 | 33 | You should now refer to the documentation for this provider. Go to the [Terraform Registry](https://registry.terraform.io/) and paste `kreuzwerker/docker` into the search bar. 34 | 35 |
36 | 1.
37 | php-httpd-image
Create a terraform resource named php-httpd-image
38 | 39 | Refer to the provider documentation for [docker-image](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image) 40 | 41 | Using `vi`, create [php-httpd-image.tf](./resources/php-httpd-image.tf) 42 |
43 | 1.
44 | mariadb-custom-image
Create a terraform resource named mariadb-image
45 | 46 | Refer to the provider documentation for [docker-image](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/image) 47 | 48 | Using `vi`, create [mariadb-custom-image.tf](./resources/mariadb-custom-image.tf) 49 |
50 | 1.
51 | mariadb-volume
Create a terraform resource named mariadb_volume
52 | 53 | Refer to the provider documentation for [docker-volume](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/volume) 54 | 55 | Using `vi`, create [mariadb-volume.tf](./resources/mariadb-volume.tf) 56 |
57 | 1.
58 | private_network
Create a terraform resource named private_network
59 | 60 | Refer to the provider documentation for [docker-network](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/network) 61 | 62 | Using `vi`, create [private-network.tf](./resources/private-network.tf) 63 |
64 | 1.
65 | db
Define a terraform resource mariadb for creating docker container
66 | 67 | Refer to the provider documentation for [docker-container](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container) 68 | 69 | Using `vi`, create [db.tf](./resources/db.tf) 70 |
71 | 1.
72 | webserver
Define a terraform resource php-httpd for creating docker container
73 | 74 | Refer to the provider documentation for [docker-container](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container) 75 | 76 | Using `vi`, create [webserver.tf](./resources/webserver.tf) 77 |
78 | 1.
79 | db_dashboard
Define a terraform resource phpmyadmin for docker container
80 | 81 | Refer to the provider documentation for [docker-container](https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs/resources/container) 82 | 83 | Using `vi`, create [db-dashboard.tf](./resources/db-dashboard.tf) 84 |
85 | 1.
86 | DEPLOY! 87 | 88 | ```bash 89 | terraform plan 90 | terraform apply 91 | ``` 92 |
93 | -------------------------------------------------------------------------------- /challenge-2/resources/db-dashboard.tf: -------------------------------------------------------------------------------- 1 | resource "docker_container" "phpmyadmin" { 2 | name = "db_dashboard" 3 | image = "phpmyadmin/phpmyadmin" 4 | hostname = "phpmyadmin" 5 | networks_advanced { 6 | name = docker_network.private_network.id 7 | } 8 | depends_on = [ 9 | docker_container.mariadb 10 | ] 11 | links = [ 12 | docker_container.mariadb.name 13 | ] 14 | labels { 15 | label = "challenge" 16 | value = "second" 17 | } 18 | ports { 19 | internal = "80" 20 | external = "8081" 21 | ip = "0.0.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /challenge-2/resources/db.tf: -------------------------------------------------------------------------------- 1 | resource "docker_container" "mariadb" { 2 | name = "db" 3 | image = docker_image.mariadb-image.name 4 | hostname = "db" 5 | networks_advanced { 6 | name = docker_network.private_network.id 7 | } 8 | ports { 9 | internal = 3306 10 | external = 3306 11 | ip = "0.0.0.0" 12 | } 13 | labels { 14 | label = "challenge" 15 | value = "second" 16 | } 17 | env = [ 18 | "MYSQL_ROOT_PASSWORD=1234", 19 | "MYSQL_DATABASE=simple-website" 20 | ] 21 | volumes { 22 | container_path = "/var/lib/mysql" 23 | volume_name = docker_volume.mariadb_volume.name 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /challenge-2/resources/mariadb-custom-image.tf: -------------------------------------------------------------------------------- 1 | resource "docker_image" "mariadb-image" { 2 | name = "mariadb:challenge" 3 | build { 4 | path = "lamp_stack/custom_db" 5 | tag = ["mariadb:challenge"] 6 | label = { 7 | challenge : "second" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /challenge-2/resources/mariadb-volume.tf: -------------------------------------------------------------------------------- 1 | resource "docker_volume" "mariadb_volume" { 2 | name = "mariadb-volume" 3 | } 4 | -------------------------------------------------------------------------------- /challenge-2/resources/php-httpd-image.tf: -------------------------------------------------------------------------------- 1 | resource "docker_image" "php-httpd-image" { 2 | name = "php-httpd:challenge" 3 | build { 4 | path = "lamp_stack/php_httpd" 5 | tag = ["php-httpd:challenge"] 6 | label = { 7 | challenge : "second" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /challenge-2/resources/private-network.tf: -------------------------------------------------------------------------------- 1 | resource "docker_network" "private_network" { 2 | name = "my_network" 3 | attachable = true 4 | labels { 5 | label = "challenge" 6 | value = "second" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /challenge-2/resources/webserver.tf: -------------------------------------------------------------------------------- 1 | resource "docker_container" "php-httpd" { 2 | name = "webserver" 3 | image = docker_image.php-httpd-image.name 4 | hostname = "php-httpd" 5 | networks_advanced { 6 | name = docker_network.private_network.id 7 | } 8 | ports { 9 | internal = 80 10 | external = 80 11 | ip = "0.0.0.0" 12 | } 13 | labels { 14 | label = "challenge" 15 | value = "second" 16 | } 17 | volumes { 18 | container_path = "/var/www/html" 19 | host_path = "/root/code/terraform-challenges/challenge2/lamp_stack/website_content/" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /challenge-3/README.md: -------------------------------------------------------------------------------- 1 | # Challenge 3 2 | 3 | In this challenge we will implement a simple EC2 instance with some preinstalled packages. 4 | 5 | Utilize `/root/terraform-challenges/project-citadel` directory to store your Terraform configuration files. 6 | 7 | We will create the resources in the order of their dependencies, thus you may run plan/apply at any stage, or wait till the end. 8 | 9 | Note that these challenges do not use an embedded VSCode IDE, therefore we must use the venerable `vi` to create and edit files. Now you can put all the resources into a single file e.g. `main.tf` or as shown here in separate files. Terraform doesn't care! 10 | 11 | 1.
12 | Enter the project directory 13 | 14 | ```bash 15 | cd /root/terraform-challenges/project-citadel 16 | ``` 17 | 18 | You should now refer to the documentation for this provider. Go to the [Terraform Registry](https://registry.terraform.io/). The AWS provider is on the front page. 19 | 20 |
21 | 1.
22 | Declare variables 23 | 24 | Both in `provider.tf` which is already present, and in the `citadel` task, variables will be referenced. Let's create them now. 25 | 26 | Using `vi`, create [variables.tf](./resources/variables.tf) 27 | 28 | We can now initialize the provider 29 | 30 | ```bash 31 | terraform init 32 | ``` 33 | 34 | 1.
35 | SSH-key-citadel
Create a terraform key-pair citadel-key with key_name citadel
36 | 37 | Refer to the provider documentation for [aws_key_pair](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/key_pair) and the core documentation for the [file](https://www.terraform.io/language/functions/file) function. 38 | 39 | Using `vi`, create [ssh-key-citadel.tf](./resources/ssh-key-citadel.tf) 40 | 41 | 1.
42 | citadel/nginx-script
Create the EC2 instance
43 | 44 | This step covers both the `citadel` and `Nginx-script` tasks, since the installation of nginx is performed by the user data script of the EC2 instance. 45 | 46 | Refer to the provider documentation for [aws_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) and the core documentation for the [file](https://www.terraform.io/language/functions/file) function. 47 | 48 | Using `vi`, create [citadel.tf](./resources/citadel.tf) 49 | 50 |
51 | 1.
52 | EC2_Elastic_IP
Create a local-exec provisioner for the eip resource and use it to print the attribute called public_dns to a file /root/citadel_public_dns.txt on the iac-server
53 | 54 | Refer to the provider documentation for [aws_eip](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) and the core documentation for [local-exec provisioner](https://www.terraform.io/language/resources/provisioners/local-exec) 55 | 56 | Using `vi`, create [ec2_elastic_ip.tf](./resources/ec2_elastic_ip.tf) 57 | 58 | 59 |
60 | 1.
61 | DEPLOY! 62 | 63 | ```bash 64 | terraform plan 65 | terraform apply 66 | ``` 67 |
68 | 69 | -------------------------------------------------------------------------------- /challenge-3/resources/citadel.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "citadel" { 2 | ami = var.ami 3 | instance_type = var.instance_type 4 | key_name = aws_key_pair.citadel-key.key_name 5 | user_data = file("/root/terraform-challenges/project-citadel/install-nginx.sh") 6 | } 7 | -------------------------------------------------------------------------------- /challenge-3/resources/ec2_elastic_ip.tf: -------------------------------------------------------------------------------- 1 | resource "aws_eip" "eip" { 2 | vpc = true 3 | instance = aws_instance.citadel.id 4 | provisioner "local-exec" { 5 | command = "echo ${self.public_dns} >> /root/citadel_public_dns.txt" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /challenge-3/resources/ssh-key-citadel.tf: -------------------------------------------------------------------------------- 1 | resource "aws_key_pair" "citadel-key" { 2 | key_name = "citadel" 3 | public_key = file("/root/terraform-challenges/project-citadel/.ssh/ec2-connect-key.pub") 4 | } 5 | 6 | -------------------------------------------------------------------------------- /challenge-3/resources/variables.tf: -------------------------------------------------------------------------------- 1 | variable "ami" { 2 | type = string 3 | default = "ami-06178cf087598769c" 4 | } 5 | 6 | variable "region" { 7 | type = string 8 | default = "eu-west-2" 9 | } 10 | 11 | variable "instance_type" { 12 | type = string 13 | default = "m5.large" 14 | } 15 | --------------------------------------------------------------------------------