├── .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 | controlplaneTerraform 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-providerConfigure 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 | frontendCreate 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-serviceCreate 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-hostInstall 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 | dockerDocker 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-imageCreate 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-imageCreate 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-volumeCreate 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_networkCreate 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 | dbDefine 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 | webserverDefine 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_dashboardDefine 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-citadelCreate 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-scriptCreate 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_IPCreate 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 |
--------------------------------------------------------------------------------