Azure Infrastructure Automation with Terraform ( Part 2 )
Infrastructure as Code (IaC)
Part 1:rohitexplainstech.hashnode.dev/azure-infras..
- We are going to create two important Bastion Resources
Azure Bastion Host
Azure Bastion Service
- We are going to use the following Azure Resources for the same.
Terraform Input Variables
azurerm_public_ip
azurerm_network_interface
azurerm_linux_virtual_machine
Terraform Null Resource
null_resource
Terraform File Provisioner
Terraform remote-exec Provisioner
azurerm_bastion_host
Terraform Output Values
Steps:
Bastion Linux VM input variables ( Create File: 8_01_bastion_host_input_variables.tf )
# Bastion linux vm input variables variable "bastion_service_subenet_name" { description = "Bastion service subnet name" default = "AzureBastionSubnet" } variable "bastion_service_address_prefixs" { description = "Bastion Service address prefixs" default = ["10.0.101.0/27"] }
Bastion Host Linux Virtual Machine ( Create File: 8_02_bastion_host_linuxvm.tf )
# create Public ip address resource "azurerm_public_ip" "bastion_host_publicip" { name = "${local.resource_name_prefix}-bastion_host_publicip" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location allocation_method = "Static" sku = "Standard" } # Create network interface resource "azurerm_network_interface" "bastion_host_linuxvm_nic" { name = "${local.resource_name_prefix}-bastion_host_linuxvm_nic" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "bastion-host-ip-1" subnet_id = azurerm_subnet.bastionsubnet.id private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.bastion_host_publicip.id } } # Azure Linux Virtual Machine - Bastion Host resource "azurerm_linux_virtual_machine" "bastion_host_linuxvm" { name = "${local.resource_name_prefix}-bastion_host_linuxvm" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location size = "Standard_DS1_v2" admin_username = "azureuser" network_interface_ids = [azurerm_network_interface.bastion_host_linuxvm_nic.id] admin_ssh_key { username = "azureuser" public_key = file("${path.module}/ssh-keys/terraform-azure.pub") } os_disk { caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_reference { publisher = "RedHat" offer = "RHEL" sku = "83-gen2" version = "latest" } }
Add Null Provider in c1-versions.tf
# Terraform Settings Block terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "3.110.0" } random = { source = "hashicorp/random" version = "3.6.2" } null = { source = "hashicorp/null" version = ">= 3.0" } } } # Providers-block provider "azurerm" { features {} }
Move the ssh key to the bastion host VM, Create Null Resource, Connection Block, File and Remote Exec Provisioner.
( Create file: 8_03_move_ssh_key_to_bastion_host.tf )
# Create a null resource and provisioners resource "null_resource" "null_copy_ssh_key_to_bastion" { depends_on = [ azurerm_linux_virtual_machine.bastion_host_linuxvm ] # Connection Block for Provisioners to connect to Azure VM Instance connection { type = "ssh" host = azurerm_linux_virtual_machine.bastion_host_linuxvm.public_ip_address user = azurerm_linux_virtual_machine.bastion_host_linuxvm.admin_username private_key = file("${path.module}/ssh-keys/terraform-azure.pem") } # File Provisioner: Copies the terraform-key.pem file to /tmp/terraform-key.pem provisioner "file" { source = "ssh-keys/terraform-azure.pem" destination = "/tmp/terraform-azure.pem" } ## Remote Exec Provisioner: Using remote-exec provisioner fix the private key permissions on Bastion Host provisioner "remote-exec" { inline = ["sudo chmod 400 /tmp/terraform-azure.pem"] } }
Till this, we have completed the bastion host configuration.
Azure Bastion Service Resources.
(Create file: 8_04_AzureBastionService.tf)
# Azure Bastion Service - Resources ## Resource -1 : Azure Bastion Subnet resource "azurerm_subnet" "bastion_service_subnet" { name = var.bastion_service_subenet_name resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = var.bastion_service_address_prefixs } ## Resource - 2: Azure Bastion Public IP resource "azurerm_public_ip" "bastion_service_publicip" { name = "${local.resource_name_prefix}-bastion_service_publicip" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name allocation_method = "Static" sku = "Standard" } ## Resource -3: Azure Bastion Service Host resource "azurerm_bastion_host" "bastion_host" { name = "${local.resource_name_prefix}-bastion_service" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "configuration" subnet_id = azurerm_subnet.bastion_service_subnet.id public_ip_address_id = azurerm_public_ip.bastion_service_publicip.id } }
Remove Public Access to Web Linux VM
( Comment 7-02-web-linuxvm-publicip.tf )
/* #Resorce 1: Public-IP resource "azurerm_public_ip" "web_linuxvm_publicip" { name = "${local.resource_name_prefix}-web_linuxvm_publicip" resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location allocation_method = "Static" sku = "Standard" domain_name_label = "app1-vm-${random_string.my-random.id}" tags = local.comman_tags } */
In
7-03-web-linuxvm-network-interface.tf
comment public IP association-related argument in Network Interface Resourcepublic_ip_address_id = azurerm_public_ip.web_linuxvm_publicip.id
#Resource:Network Interface {NIC} resource "azurerm_network_interface" "web_linuxvm_nic" { name = "${local.resource_name_prefix}-web_linuxvm_nic" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "web-linuxvm-ip-1" subnet_id = azurerm_subnet.websubnet.id private_ip_address_allocation = "Dynamic" # public_ip_address_id = azurerm_public_ip.web_linuxvm_publicip.id } }
In
c7-06-web-linuxvm-outputs.tf
comment Outputs related to Public IP Address.# Public IP Outputs /* ## Public IP Address output "web_linuxvm_public_ip" { description = "Web Linux VM Public Address" value = azurerm_public_ip.web_linuxvm_publicip.ip_address } */ # Network Interface Outputs ## Network Interface ID output "web_linuxvm_network_interface_id" { description = "Web Linux VM Network Interface ID" value = azurerm_network_interface.web_linuxvm_nic.id } ## Network Interface Private IP Addresses output "web_linuxvm_network_interface_private_ip_addresses" { description = "Web Linux VM Private IP Addresses" value = [azurerm_network_interface.web_linuxvm_nic.private_ip_addresses] } # Linux VM Outputs ## Virtual Machine Public IP /* output "web_linuxvm_public_ip_address" { description = "Web Linux Virtual Machine Public IP" value = azurerm_linux_virtual_machine.web_linuxvm.public_ip_address } */ ## Virtual Machine Private IP output "web_linuxvm_private_ip_address" { description = "Web Linux Virtual Machine Private IP" value = azurerm_linux_virtual_machine.web_linuxvm.private_ip_address } ## Virtual Machine 128-bit ID output "web_linuxvm_virtual_machine_id_128bit" { description = "Web Linux Virtual Machine ID - 128-bit identifier" value = azurerm_linux_virtual_machine.web_linuxvm.virtual_machine_id } ## Virtual Machine ID output "web_linuxvm_virtual_machine_id" { description = "Web Linux Virtual Machine ID " value = azurerm_linux_virtual_machine.web_linuxvm.id }
Update terraform.tfvars
business_division = "hr" environment = "dev" resource_group_name = "rg" resource_group_location = "eastus" vnet_name = "vnet" vnet_address_space = ["10.1.0.0/16"] web_subnet_name = "websubnet" web_subnet_address = ["10.1.1.0/24"] app_subnet_name = "appsubnet" app_subnet_address = ["10.1.11.0/24"] db_subnet_name = "dbsubnet" db_subnet_address = ["10.1.21.0/24"] bastion_subnet_name = "bastionsubnet" bastion_subnet_address = ["10.1.100.0/24"] # Newly added bastion_service_subenet_name = "AzureBastionSubnet" bastion_service_address_prefixs = ["10.1.101.0/27"]
Create file: 8-05-bastion-outputs.tf for Bastion Host Public IP Output
## Bastion Host Public IP Output output "bastion_host_linuxvm_public_ip_address" { description = "Bastion Host Linux VM Public Address" value = azurerm_public_ip.bastion_host_publicip.ip_address }
Execute Terraform Commands.
# Terraform Initialize terraform init # Terraform Validate terraform validate # Terraform Plan terraform plan # Terraform Apply terraform apply -auto-approve ## -- Important Note --- # --> Azure Bastions Service takes 10 to 15 minutes to create.
Verify Resources - Bastion Host
# Verify Resources - Virtual Network 1. Azure Resource Group 2. Azure Virtual Network 3. Azure Subnets (Web, App, DB, Bastion) 4. Azure Network Security Groups (Web, App, DB, Bastion) 5. View the topology 6. Verify Terraform Outputs in Terraform CLI # Verify Resources - Web Linux VM 1. Verify Network Interface created for Web Linux VM 2. Verify Web Linux VM 3. Verify Network Security Groups associated with VM (web Subnet NSG) 4. View Topology at Web Linux VM -> Networking 5. Verify if only private IP associated with Web Linux VM # Verify Resources - Bastion Host 1. Verify Bastion Host VM Public IP 2. Verify Bastion Host VM Network Interface 3. Verify Bastion VM 4. Verify Bastion VM -> Networking -> NSG Rules 5. Verify Bastion VM Topology # Connect to Bastion Host VM 1. Connect to Bastion Host Linux VM ssh -i ssh-keys/terraform-azure.pem azureuser@<Bastion-Host-LinuxVM-PublicIP> sudo su - cd /tmp ls 2. terraform-azure.pem file should be present in /tmp directory # Connect to Web Linux VM using Bastion Host VM 1. Connect to Web Linux VM ssh -i ssh-keys/terraform-azure.pem azureuser@<Web-LinuxVM-PrivateIP> sudo su - cd /var/log tail -100f cloud-init-output.log cd /var/www/html ls -lrt cd /var/www/html/app1 ls -lrt exit exit
Connecting to bastion host Linux VM.
Now ssh to the web-linuxvm using its private IP
Now we can connect web-linuxvm through bastion-host-linuxvm.
Verify Resources - Bastion Service
# Verify Azure Bastion Service 1. Go to Azure Management Porta Console -> Bastions 2. Verify Bastion Service -> hr-dev-bastion-service 3. Verify Settings -> Sessions 4. Verify Settings -> Configuration # Connect to Web Linux VM using Bastion Service 1. Go to Web Linux VM using Azure Portal Console 2. Portal Console -> Virtual machines -> hr-dev-web-linuxvm ->Settings -> Connect 3. Select "Bastion" tab -> Click on "Use Bastion" - Open in new window: checked - Username: azureuser - Authentication Type: SSH Private Key from Local File - Local File: Browse from ssh-keys/terraform-azure.pem - Click on Connect 4. In new tab, we should be logged in to VM "hr-dev-web-linuxvm" 5. Run additional commands sudo su - cd /var/www/html ls cd /var/www/html/app1 ls # Verify Bastion Sessions 1. Go to Azure Management Porta Console -> Bastions 2. Verify Bastion Service -> hr-dev-bastion-service 3. Verify Settings -> Sessions
Now go to
hr-dev-web-linuxvm
and click on connect.Click on
Go to Bastion
Now we can connect web-linuxvm through Azure-Bastion-Service.
Check for the session in Azure Bastion Service.
Delete Resources
# Delete Resources
terraform destroy
[or]
terraform apply -destroy -auto-approve
# Clean-Up Files
rm -rf .terraform*
rm -rf terraform.tfstate*
We have completed the implementation of the Bastion host on Linux VM and also implemented Azure Bastion Service.๐
In the next blog, we will set up the Azure Standard LoadBalancer.
GitHub Repo Link: https://github.com/DeoreRohit4/Azure-Infrastructure-Automation-with-Terraform-
Keep Exploring...