├── README.md ├── main.tf ├── modules ├── compute │ ├── main.tf │ └── vars.tf ├── database │ ├── main.tf │ └── vars.tf ├── networking │ ├── main.tf │ ├── output.tf │ └── vars.tf ├── resourcegroup │ ├── main.tf │ ├── output.tf │ └── vars.tf └── securitygroup │ ├── main.tf │ └── vars.tf ├── terraform.tfvars └── vars.tf /README.md: -------------------------------------------------------------------------------- 1 | # Terraform code to deploy three-tier architecture on azure 2 | 3 | ## What is three-tier architecture? 4 | Three-tier architecture is a well-established software application architecture that organizes applications into three logical and physical computing tiers: the presentation tier, or user interface; the application tier, where data is processed; and the data tier, where the data associated with the application is stored and managed. 5 | 6 | ## What is terraform? 7 | Terraform is an open-source infrastructure as code software tool created by HashiCorp. Users define and provision data center infrastructure using a declarative configuration language known as HashiCorp Configuration Language, or optionally JSON. 8 | 9 | ## Installation 10 | - [Terraform](https://www.terraform.io/downloads.html) 11 | 12 | ## Problem Statement 13 | 14 | 1. One virtual network tied in three subnets. 15 | 2. Each subnet will have one virtual machine. 16 | 3. First virtual machine -> allow inbound traffic from internet only. 17 | 4. Second virtual machine -> entertain traffic from first virtual machine only and can reply the same virtual machine again. 18 | 5. App can connect to database and database can connect to app but database cannot connect to web. 19 | 20 | _Note: Keep main and variable files different for each component_ 21 | 22 | ## Solution 23 | 24 | ### The Terraform resources will consists of following structure 25 | 26 | ``` 27 | ├── main.tf // The primary entrypoint for terraform resources. 28 | ├── vars.tf // It contain the declarations for variables. 29 | ├── output.tf // It contain the declarations for outputs. 30 | ├── terraform.tfvars // The file to pass the terraform variables values. 31 | ``` 32 | 33 | ### Module 34 | 35 | A module is a container for multiple resources that are used together. Modules can be used to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects. 36 | 37 | For the solution, we have created and used five modules: 38 | 1. resourcegroup - creating resourcegroup 39 | 2. networking - creating azure virtual network and required subnets 40 | 3. securitygroup - creating network security group, setting desired security rules and associating them to subnets 41 | 4. compute - creating availability sets, network interfaces and virtual machines 42 | 5. database - creating database server and database 43 | 44 | All the stacks are placed in the modules folder and the variable are stored under **terraform.tfvars** 45 | 46 | To run the code you need to append the variables in the terraform.tfvars 47 | 48 | Each module consists minimum two files: main.tf, vars.tf 49 | 50 | resourcegroup and networking modules consists of one extra file named output.tf 51 | 52 | ## Deployment 53 | 54 | ### Steps 55 | 56 | **Step 0** `terraform init` 57 | 58 | used to initialize a working directory containing Terraform configuration files 59 | 60 | **Step 1** `terraform plan` 61 | 62 | used to create an execution plan 63 | 64 | **Step 2** `terraform validate` 65 | 66 | validates the configuration files in a directory, referring only to the configuration and not accessing any remote services such as remote state, provider APIs, etc 67 | 68 | **Step 3** `terraform apply` 69 | 70 | used to apply the changes required to reach the desired state of the configuration 71 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | } 4 | 5 | module "resourcegroup" { 6 | source = "./modules/resourcegroup" 7 | name = var.name 8 | location = var.location 9 | } 10 | 11 | module "networking" { 12 | source = "./modules/networking" 13 | location = module.resourcegroup.location_id 14 | resource_group = module.resourcegroup.resource_group_name 15 | vnetcidr = var.vnetcidr 16 | websubnetcidr = var.websubnetcidr 17 | appsubnetcidr = var.appsubnetcidr 18 | dbsubnetcidr = var.dbsubnetcidr 19 | } 20 | 21 | module "securitygroup" { 22 | source = "./modules/securitygroup" 23 | location = module.resourcegroup.location_id 24 | resource_group = module.resourcegroup.resource_group_name 25 | web_subnet_id = module.networking.websubnet_id 26 | app_subnet_id = module.networking.appsubnet_id 27 | db_subnet_id = module.networking.dbsubnet_id 28 | } 29 | 30 | module "compute" { 31 | source = "./modules/compute" 32 | location = module.resourcegroup.location_id 33 | resource_group = module.resourcegroup.resource_group_name 34 | web_subnet_id = module.networking.websubnet_id 35 | app_subnet_id = module.networking.appsubnet_id 36 | web_host_name = var.web_host_name 37 | web_username = var.web_username 38 | web_os_password = var.web_os_password 39 | app_host_name = var.app_host_name 40 | app_username = var.app_username 41 | app_os_password = var.app_os_password 42 | } 43 | 44 | module "database" { 45 | source = "./modules/database" 46 | location = module.resourcegroup.location_id 47 | resource_group = module.resourcegroup.resource_group_name 48 | primary_database = var.primary_database 49 | primary_database_version = var.primary_database_version 50 | primary_database_admin = var.primary_database_admin 51 | primary_database_password = var.primary_database_password 52 | } 53 | -------------------------------------------------------------------------------- /modules/compute/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_availability_set" "web_availabilty_set" { 2 | name = "web_availabilty_set" 3 | location = var.location 4 | resource_group_name = var.resource_group 5 | } 6 | 7 | resource "azurerm_network_interface" "web-net-interface" { 8 | name = "web-network" 9 | resource_group_name = var.resource_group 10 | location = var.location 11 | 12 | ip_configuration{ 13 | name = "web-webserver" 14 | subnet_id = var.web_subnet_id 15 | private_ip_address_allocation = "Dynamic" 16 | } 17 | } 18 | 19 | resource "azurerm_virtual_machine" "web-vm" { 20 | name = "web-vm" 21 | location = var.location 22 | resource_group_name = var.resource_group 23 | network_interface_ids = [ azurerm_network_interface.web-net-interface.id ] 24 | availability_set_id = azurerm_availability_set.web_availabilty_set.id 25 | vm_size = "Standard_D2s_v3" 26 | delete_os_disk_on_termination = true 27 | 28 | storage_image_reference { 29 | publisher = "Canonical" 30 | offer = "UbuntuServer" 31 | sku = "18.04-LTS" 32 | version = "latest" 33 | } 34 | 35 | storage_os_disk { 36 | name = "web-disk" 37 | caching = "ReadWrite" 38 | create_option = "FromImage" 39 | managed_disk_type = "Standard_LRS" 40 | } 41 | 42 | os_profile { 43 | computer_name = var.web_host_name 44 | admin_username = var.web_username 45 | admin_password = var.web_os_password 46 | } 47 | 48 | os_profile_linux_config { 49 | disable_password_authentication = false 50 | } 51 | } 52 | 53 | 54 | resource "azurerm_availability_set" "app_availabilty_set" { 55 | name = "app_availabilty_set" 56 | location = var.location 57 | resource_group_name = var.resource_group 58 | } 59 | 60 | resource "azurerm_network_interface" "app-net-interface" { 61 | name = "app-network" 62 | resource_group_name = var.resource_group 63 | location = var.location 64 | 65 | ip_configuration{ 66 | name = "app-webserver" 67 | subnet_id = var.app_subnet_id 68 | private_ip_address_allocation = "Dynamic" 69 | } 70 | } 71 | 72 | resource "azurerm_virtual_machine" "app-vm" { 73 | name = "app-vm" 74 | location = var.location 75 | resource_group_name = var.resource_group 76 | network_interface_ids = [ azurerm_network_interface.app-net-interface.id ] 77 | availability_set_id = azurerm_availability_set.web_availabilty_set.id 78 | vm_size = "Standard_D2s_v3" 79 | delete_os_disk_on_termination = true 80 | 81 | storage_image_reference { 82 | publisher = "Canonical" 83 | offer = "UbuntuServer" 84 | sku = "18.04-LTS" 85 | version = "latest" 86 | } 87 | 88 | storage_os_disk { 89 | name = "app-disk" 90 | caching = "ReadWrite" 91 | create_option = "FromImage" 92 | managed_disk_type = "Standard_LRS" 93 | } 94 | 95 | os_profile { 96 | computer_name = var.app_host_name 97 | admin_username = var.app_username 98 | admin_password = var.app_os_password 99 | } 100 | 101 | os_profile_linux_config { 102 | disable_password_authentication = false 103 | } 104 | } 105 | 106 | -------------------------------------------------------------------------------- /modules/compute/vars.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group" {} 2 | variable "location" {} 3 | variable "web_subnet_id" {} 4 | variable "app_subnet_id" {} 5 | variable "web_host_name"{} 6 | variable "web_username" {} 7 | variable "web_os_password" {} 8 | variable "app_host_name"{} 9 | variable "app_username" {} 10 | variable "app_os_password" {} 11 | -------------------------------------------------------------------------------- /modules/database/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_sql_server" "primary" { 2 | name = var.primary_database 3 | resource_group_name = var.resource_group 4 | location = var.location 5 | version = var.primary_database_version 6 | administrator_login = var.primary_database_admin 7 | administrator_login_password = var.primary_database_password 8 | } 9 | 10 | resource "azurerm_sql_database" "db" { 11 | name = "db" 12 | resource_group_name = var.resource_group 13 | location = var.location 14 | server_name = azurerm_sql_server.primary.name 15 | } 16 | 17 | -------------------------------------------------------------------------------- /modules/database/vars.tf: -------------------------------------------------------------------------------- 1 | variable "primary_database" {} 2 | variable "resource_group" {} 3 | variable "location" {} 4 | variable "primary_database_version" {} 5 | variable "primary_database_admin" {} 6 | variable "primary_database_password" {} 7 | 8 | -------------------------------------------------------------------------------- /modules/networking/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_virtual_network" "vnet01" { 2 | name = "vnet01" 3 | resource_group_name = var.resource_group 4 | location = var.location 5 | address_space = [var.vnetcidr] 6 | } 7 | 8 | resource "azurerm_subnet" "web-subnet" { 9 | name = "web-subnet" 10 | virtual_network_name = azurerm_virtual_network.vnet01.name 11 | resource_group_name = var.resource_group 12 | address_prefixes = [var.websubnetcidr] 13 | } 14 | 15 | resource "azurerm_subnet" "app-subnet" { 16 | name = "app-subnet" 17 | virtual_network_name = azurerm_virtual_network.vnet01.name 18 | resource_group_name = var.resource_group 19 | address_prefixes = [var.appsubnetcidr] 20 | } 21 | 22 | resource "azurerm_subnet" "db-subnet" { 23 | name = "db-subnet" 24 | virtual_network_name = azurerm_virtual_network.vnet01.name 25 | resource_group_name = var.resource_group 26 | address_prefixes = [var.dbsubnetcidr] 27 | } 28 | -------------------------------------------------------------------------------- /modules/networking/output.tf: -------------------------------------------------------------------------------- 1 | output "network_name" { 2 | value = azurerm_virtual_network.vnet01.name 3 | description = "Name of the Virtual network" 4 | } 5 | 6 | output "websubnet_id" { 7 | value = azurerm_subnet.web-subnet.id 8 | description = "Id of websubnet in the network" 9 | } 10 | 11 | output "appsubnet_id" { 12 | value = azurerm_subnet.app-subnet.id 13 | description = "Id of appsubnet in the network" 14 | } 15 | 16 | output "dbsubnet_id" { 17 | value = azurerm_subnet.db-subnet.id 18 | description = "Id of dbsubnet in the network" 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /modules/networking/vars.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group" {} 2 | variable "location" {} 3 | variable "vnetcidr" {} 4 | variable "websubnetcidr" {} 5 | variable "appsubnetcidr" {} 6 | variable "dbsubnetcidr" {} 7 | -------------------------------------------------------------------------------- /modules/resourcegroup/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "azure-stack-rs" { 2 | name = var.name 3 | location = var.location 4 | } 5 | -------------------------------------------------------------------------------- /modules/resourcegroup/output.tf: -------------------------------------------------------------------------------- 1 | output "resource_group_name" { 2 | value = azurerm_resource_group.azure-stack-rs.name 3 | description = "Name of the resource group." 4 | } 5 | 6 | output "location_id" { 7 | value = azurerm_resource_group.azure-stack-rs.location 8 | description = "Location id of the resource group" 9 | } 10 | -------------------------------------------------------------------------------- /modules/resourcegroup/vars.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | variable "location" {} 3 | -------------------------------------------------------------------------------- /modules/securitygroup/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_network_security_group" "web-nsg" { 2 | name = "web-nsg" 3 | location = var.location 4 | resource_group_name = var.resource_group 5 | 6 | security_rule { 7 | name = "ssh-rule-1" 8 | priority = 101 9 | direction = "Inbound" 10 | access = "Allow" 11 | protocol = "Tcp" 12 | source_address_prefix = "*" 13 | source_port_range = "*" 14 | destination_address_prefix = "*" 15 | destination_port_range = "22" 16 | } 17 | 18 | security_rule { 19 | name = "ssh-rule-2" 20 | priority = 100 21 | direction = "Inbound" 22 | access = "Deny" 23 | protocol = "Tcp" 24 | source_address_prefix = "192.168.3.0/24" 25 | source_port_range = "*" 26 | destination_address_prefix = "*" 27 | destination_port_range = "22" 28 | } 29 | } 30 | 31 | resource "azurerm_subnet_network_security_group_association" "web-nsg-subnet" { 32 | subnet_id = var.web_subnet_id 33 | network_security_group_id = azurerm_network_security_group.web-nsg.id 34 | } 35 | 36 | 37 | resource "azurerm_network_security_group" "app-nsg" { 38 | name = "app-nsg" 39 | location = var.location 40 | resource_group_name = var.resource_group 41 | 42 | security_rule { 43 | name = "ssh-rule-1" 44 | priority = 100 45 | direction = "Inbound" 46 | access = "Allow" 47 | protocol = "Tcp" 48 | source_address_prefix = "192.168.1.0/24" 49 | source_port_range = "*" 50 | destination_address_prefix = "*" 51 | destination_port_range = "22" 52 | } 53 | 54 | security_rule { 55 | name = "ssh-rule-2" 56 | priority = 101 57 | direction = "Outbound" 58 | access = "Allow" 59 | protocol = "Tcp" 60 | source_address_prefix = "192.168.1.0/24" 61 | source_port_range = "*" 62 | destination_address_prefix = "*" 63 | destination_port_range = "22" 64 | } 65 | } 66 | 67 | resource "azurerm_subnet_network_security_group_association" "app-nsg-subnet" { 68 | subnet_id = var.app_subnet_id 69 | network_security_group_id = azurerm_network_security_group.app-nsg.id 70 | } 71 | 72 | 73 | resource "azurerm_network_security_group" "db-nsg" { 74 | name = "db-nsg" 75 | location = var.location 76 | resource_group_name = var.resource_group 77 | 78 | security_rule { 79 | name = "ssh-rule-1" 80 | priority = 101 81 | direction = "Inbound" 82 | access = "Allow" 83 | protocol = "Tcp" 84 | source_address_prefix = "192.168.2.0/24" 85 | source_port_range = "*" 86 | destination_address_prefix = "*" 87 | destination_port_range = "3306" 88 | } 89 | 90 | security_rule { 91 | name = "ssh-rule-2" 92 | priority = 102 93 | direction = "Outbound" 94 | access = "Allow" 95 | protocol = "Tcp" 96 | source_address_prefix = "192.168.2.0/24" 97 | source_port_range = "*" 98 | destination_address_prefix = "*" 99 | destination_port_range = "3306" 100 | } 101 | 102 | security_rule { 103 | name = "ssh-rule-3" 104 | priority = 100 105 | direction = "Outbound" 106 | access = "Deny" 107 | protocol = "Tcp" 108 | source_address_prefix = "192.168.1.0/24" 109 | source_port_range = "*" 110 | destination_address_prefix = "*" 111 | destination_port_range = "3306" 112 | } 113 | } 114 | 115 | resource "azurerm_subnet_network_security_group_association" "db-nsg-subnet" { 116 | subnet_id = var.db_subnet_id 117 | network_security_group_id = azurerm_network_security_group.db-nsg.id 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /modules/securitygroup/vars.tf: -------------------------------------------------------------------------------- 1 | variable "location" {} 2 | variable "resource_group" {} 3 | variable "web_subnet_id" {} 4 | variable "app_subnet_id" {} 5 | variable "db_subnet_id" {} 6 | 7 | 8 | -------------------------------------------------------------------------------- /terraform.tfvars: -------------------------------------------------------------------------------- 1 | name = "azure-stack" 2 | location = "Central India" 3 | vnetcidr = "192.168.0.0/16" 4 | websubnetcidr = "192.168.1.0/24" 5 | appsubnetcidr = "192.168.2.0/24" 6 | dbsubnetcidr = "192.168.3.0/24" 7 | web_host_name = "webserver" 8 | web_username = "web_user" 9 | web_os_password = "@Webuser1" 10 | app_host_name = "appserver" 11 | app_username = "app_user" 12 | app_os_password = "@Appuser1" 13 | primary_database = "sql-primary-database" 14 | primary_database_admin = "sqladmin" 15 | primary_database_password = "pa$$w0rd" 16 | primary_database_version = "12.0" 17 | 18 | -------------------------------------------------------------------------------- /vars.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | variable "location" {} 3 | variable "vnetcidr" {} 4 | variable "websubnetcidr" {} 5 | variable "appsubnetcidr" {} 6 | variable "dbsubnetcidr" {} 7 | variable "web_host_name"{} 8 | variable "web_username" {} 9 | variable "web_os_password" {} 10 | variable "app_host_name"{} 11 | variable "app_username" {} 12 | variable "app_os_password" {} 13 | variable "primary_database" {} 14 | variable "primary_database_admin" {} 15 | variable "primary_database_password" {} 16 | variable "primary_database_version" {} 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------