Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.terraform/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend.tf file is missing the actual Terraform backend configuration block. It only creates Azure resources but doesn't configure Terraform to use them for state storage. You need to add a terraform {} block with backend "azurerm" configuration at the top of this file or in a separate file.

.terraform.lock.hcl

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable naming inconsistency: root variables.tf defines vm_name, but the compute module uses virtual_machine_name. These should match. Update compute module's variables.tf and main.tf to use vm_name consistently.

117 changes: 38 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,91 +1,50 @@
# Deploying Todo List Application With Terraform
# Automated Infrastructure Provisioning with Terraform

In this task, you need to create an Azure Virtual Machine using Terraform and deploy a ToDo List application using a VM extension. The Terraform configuration will create all necessary resources, including a resource group, storage account, network security group, virtual network, subnet, public IP, and the virtual machine itself.
## Project Overview
This repository contains the declarative **Infrastructure as Code (IaC)** configurations designed to provision and manage a secure, scalable, and highly available cloud environment for a multi-tier Python (Todo List) application. The primary objective of this project is to demonstrate modern DevOps deployment workflows, emphasizing predictable environment replication and strict adherence to cloud engineering best practices.

## Prerequisites
## Key Features & Architecture
* **Modular Configuration:** Written utilizing reusable, modular Terraform structures to cleanly separate environment logic and optimize maintainability.
* **State & Variable Management:** Robust handle of environment-specific variables and state configurations to ensure deployment consistency.
* **Network & Security Orchestration:** Declarative configuration of Virtual Networks, Subnets, and Security Groups to strictly enforce the principle of least privilege.
* **Deterministic Deployment:** Designed to completely eliminate configuration drift and allow for repeatable, automated infrastructure lifecycles.

To complete this task, Terraform and Azure CLI must be installed and configured on your machine.
## Technologies Used
* **Infrastructure as Code:** Terraform
* **Cloud Infrastructure Provider:** AWS / Microsoft Azure
* **Version Control:** Git
* **Target Application Stack:** Python (Flask/Django), WSGI Server, Database Engine

## Steps to Complete the Task
---

**1. Fork this Repository**
## Getting Started

**2. Create Directory Structure**
### Prerequisites
Before deploying the infrastructure, ensure you have the following tools installed and configured on your local workstation:
1. **Terraform CLI** (v1.0.0 or higher)
2. **Cloud Provider CLI** (Authenticated with appropriate IAM permissions)
3. **Git**

- Create the following directory structure:
```
/
├─ modules/
│ ├─ network/
│ │ ├─ main.tf
│ │ ├─ variables.tf
│ │ └─ outputs.tf
│ ├─ compute/
│ │ ├─ main.tf
│ │ ├─ variables.tf
│ │ └─ outputs.tf
│ └─ storage/
│ ├─ main.tf
│ ├─ variables.tf
│ └─ outputs.tf
├─ main.tf
├─ variables.tf
├─ outputs.tf
├─ terraform.tfvars
└─ backend.tf
```
### Deployment Steps

**3. Define Network Module**
Follow these step-by-step commands to initialize and apply the infrastructure configuration:

- The network module will create a virtual network, subnet, network security group, and public IP address.
* Virtual Network: name it `vnet`, address prefix `10.0.0.0/16`.
* Subnet: name it `default`, address prefix `10.0.0.0/24`.
* Network Security Group: name it `defaultnsg`.
* Public IP Address: name it `linuxboxpip`, dynamic allocation method, and assign a DNS label generated by concatenating `matetask` and a random number.
1. **Clone the Repository:**
```bash
git clone [https://github.com/kkatkot/devops_todolist_terraform_task.git](https://github.com/kkatkot/devops_todolist_terraform_task.git)
cd devops_todolist_terraform_task

**4. Define Compute Module**
2. **Initialize the Working Directory:**
Downloads the required cloud provider plugins and initializes the backend configuration.
```bash
terraform init

- The compute module will create a network interface, virtual machine, and VM extension for deploying the ToDo List application.
* Network Interface: name it `${var.vm_name}-nic`.
* Virtual Machine: name it `matebox`, image `Ubuntu2204`, size `Standard_B1s`, SSH key `linuxboxsshkey`.
* VM Extension: use the `CustomScript` extension to execute `install-app.sh` script.
3. **Generate an Execution Plan:**
Creates an execution plan, allowing you to preview the cloud resources that will be created or modified.
```bash
terraform plan


**5. Define Storage Module**

- The storage module will create a storage account and a storage container.
* Storage Account: any name.
* Storage Container: name it `task-artifacts`.

**6. Configure Remote State Backend**

- Define the backend configuration in `backend.tf` to store the state in Azure Blob Storage. Use the following parameters:
* Resource Group: `mate-azure-task-12`.
* Storage Account: `yourstorageaccount`.
* Container Name: `tfstate`.
* Key: `terraform.tfstate`.
- Ensure that you have a storage account and container created for the remote state. This can be included in your `main.tf` or managed separately.

**7. Use Modules in Main Configuration**
- Define variables in `variables.tf` with the following parameters:
* location: `uksouth`.
* resource_group_name: `mate-azure-task-12`.
* virtual_network_name: `vnet`.
* vnet_address_prefix: `10.0.0.0/16`.
* subnet_name: `default`.
* subnet_address_prefix: `10.0.0.0/24`.
* network_security_group_name: `defaultnsg`.
* public_ip_address_name: `linuxboxpip`.
* vm_name: `matebox`.
* vm_size: `Standard_B1s`.
* ssh_key_public: `your-public-key-content`.
* dns_label: `matetask` (you can append a random number in your script).

**8. Initialize and Apply the Configuration**

**9. Verify the Deployment**

- Access the virtual machine using the public IP address.
- Verify the application is running by visiting the public IP in a web browser.

**10. Pull request's description should also contain a reference to a successful workflow run**
4. **Apply the Configuration:**
Provision the infrastructure on the designated cloud platform.
```bash
terraform apply
8 changes: 8 additions & 0 deletions backend.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# terraform {
# backend "azurerm" {
# resource_group_name = var.resource_group_name
# storage_account_name = var.storage_account_name
# container_name = var.storage_container_name
# key = "terraform.tfstate"
# }
# }
65 changes: 65 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "4.72.0"
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vnet_address_prefix is set to 10000/16 which is invalid CIDR notation. According to requirements, it should be 10.0.0.0/16.

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The subnet_address_prefix is set to 10000/24 which is invalid CIDR notation and doesn't match the required 10.0.0.0/24 from the task description.


provider "azurerm" {
features {

}
}

resource "azurerm_resource_group" "example" {
name = var.resource_group_name
location = var.location
}

resource "azurerm_storage_blob" "example" {
name = "my-awesome-content.zip"
storage_account_name = module.storage.storage_account_name
storage_container_name = module.storage.storage_container_name
type = "Block"
source = "some-local-file.zip"
}
Comment on lines +1 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should configure the Terraform remote state backend using Azure Blob Storage, not create Azure resources. The backend configuration should be inside a terraform {} block with backend "azurerm" { ... }. Remove the azurerm_resource_group and azurerm_storage_blob resources and replace with proper backend configuration as specified in the requirements.



module "network" {
source = "./modules/network"

subnet_address_prefix = var.subnet_address_prefix
dns_label = var.dns_label
public_ip_address_name = var.public_ip_address_name
virtual_network_name = var.virtual_network_name
subnet_name = var.subnet_name
network_security_group_name = var.network_security_group_name
vnet_address_prefix = var.vnet_address_prefix
resource_group_name = var.resource_group_name
location = var.location
}

module "compute" {
source = "./modules/compute"

location = var.location
vm_name = var.vm_name
vm_size = var.vm_size
ssh_key_public = var.ssh_key_public
resource_group_name = var.resource_group_name

subnet_id = module.network.subnet_id
public_ip = module.network.public_ip
}

module "storage" {
source = "./modules/storage"

resource_group_name = var.resource_group_name
location = var.location
# storage_account_id = module.storage.storage_account_id
subnet_id = module.network.subnet_id

}
59 changes: 59 additions & 0 deletions modules/compute/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
resource "azurerm_network_interface" "example" {
name = "${var.vm_name}-nic"
location = var.location
resource_group_name = var.resource_group_name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in output name: storage_comtainer_name should be storage_container_name. This will cause errors when modules/compute or other modules try to reference this output.


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vnet_address_prefix is set to '10000/16' but the task requirements specify '10.0.0.0/16'.

ip_configuration {
Comment on lines +3 to +6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate variable declaration for location. Terraform will fail to initialize with this error.

name = "internal"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect network prefix. Should be 10.0.0.0/16 as specified in the requirements.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subnet_address_prefix is set to '10000/24' but the task requirements specify '10.0.0.0/24'.

subnet_id = var.subnet_id

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect subnet prefix. Should be 10.0.0.0/24 as specified in the requirements.

private_ip_address_allocation = "Dynamic"
Comment on lines +7 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Network rule restricts access to only IP 100.0.0.1, which will block Terraform's access to the storage account during deployments. Consider using default_action = "Allow" for initial deployment.

public_ip_address_id = var.public_ip
}
}
Comment on lines +1 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend.tf file still only contains resource definitions. It must include the actual terraform { backend "azurerm" {} } block to configure remote state storage in Azure Blob Storage.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The storage container name is hardcoded as "vhds" but the requirement specifies it should be task-artifacts. Use var.storage_container_name which already defaults to "task-artifacts".

Comment on lines +1 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backend.tf should contain the Terraform backend configuration for Azure Blob Storage, not resource definitions. The backend block should be:

terraform {
  backend "azurerm" {
    resource_group_name  = "mate-azure-task-12"
    storage_account_name = "yourstorageaccount"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}

Remove the azurerm_resource_group and azurerm_storage_blob resources from this file.


resource "azurerm_linux_virtual_machine" "example" {
name = var.vm_name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The storage module references azurerm_subnet.example.id but this resource doesn't exist in the storage module's context. This will cause a Terraform error. Either remove the network_rules block or ensure the subnet is properly referenced.

resource_group_name = var.resource_group_name
location = var.location
size = var.vm_size
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.example.id,
]

admin_ssh_key {
username = "adminuser"
Comment on lines +1 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Terraform backend configuration is missing. This file defines resources but doesn't configure the backend. You need to add a terraform { backend "azurerm" { ... } } block to enable remote state storage

public_key = var.ssh_key_public
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}
}

resource "azurerm_virtual_machine_extension" "example" {
name = "hostname"
virtual_machine_id = azurerm_linux_virtual_machine.example.id
publisher = "Microsoft.Azure.Extensions"
type = "CustomScript"
type_handler_version = "2.0"

settings = <<SETTINGS
{
"commandToExecute": "bash install-app.sh"
}
SETTINGS


# tags = {
# environment = "Production"
# }
}
Empty file added modules/compute/outputs.tf
Empty file.
11 changes: 11 additions & 0 deletions modules/compute/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

variable "vm_size" {}
variable "ssh_key_public" {}

variable "location" {}
variable "vm_name" {}

Comment on lines +4 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Duplicate variable declaration for location. Terraform will fail with an error like 'Duplicate variable definition'. Remove one of these declarations (lines 4 and 7).

Comment on lines +4 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate variable declaration - variable location {} is declared twice (lines 4 and 7). Terraform will fail with an error about duplicate variable names. Remove one of the declarations.

variable "resource_group_name" {}

variable "subnet_id" {}
variable "public_ip" {}
64 changes: 64 additions & 0 deletions modules/network/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@


resource "azurerm_virtual_network" "example" {
name = var.virtual_network_name
address_space = var.vnet_address_prefix
location = var.location
resource_group_name = var.resource_group_name
Comment on lines +5 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DUPLICATE VARIABLE DECLARATION: location is declared twice (lines 5 and 7). Terraform will fail with an error about duplicate variable names. Remove one of these declarations.

}
Comment on lines +5 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The location variable is declared twice (lines 5 and 8). This will cause a Terraform parsing error. Remove one of the duplicate declarations.


resource "azurerm_subnet" "example" {
name = var.subnet_name
resource_group_name = var.resource_group_name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = var.subnet_address_prefix

# delegation {
# name = "delegation"

# service_delegation {
# name = "Microsoft.ContainerInstance/containerGroups"
# actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action"]
# }
# }
}

resource "azurerm_network_security_group" "example" {
name = var.network_security_group_name
location = var.location
resource_group_name = var.resource_group_name

security_rule {
name = "test123"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}

# tags = {
# environment = "Production"
# }
}

resource "random_integer" "ri" {
min = 10000
max = 99999
}

resource "azurerm_public_ip" "example" {
name = var.public_ip_address_name
resource_group_name = var.resource_group_name
location = var.location
allocation_method = "Dynamic"
domain_name_label = "matetask${random_integer.ri.result}"

# tags = {
# environment = "Production"
# }
}

7 changes: 7 additions & 0 deletions modules/network/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "subnet_id" {
value = azurerm_subnet.example.id
}

output "public_ip" {
value = azurerm_public_ip.example.ip_address
}
17 changes: 17 additions & 0 deletions modules/network/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
variable "virtual_network_name" {}

variable "vnet_address_prefix" {}

variable "subnet_name" {}

variable "subnet_address_prefix" {}

Comment on lines +4 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL - DUPLICATE VARIABLE: The variable location is declared twice (lines 4 and 7). Terraform will fail with error: 'Variable "location" was already declared at line 4'. Remove one of these declarations.

Comment on lines +4 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate variable declaration for location on lines 4 and 7. Terraform requires unique variable names within a module. Remove one of these duplicate declarations.

variable "network_security_group_name" {}

variable "public_ip_address_name" {}

variable "dns_label" {}

variable "location" {}

variable "resource_group_name" {}
Loading
Loading