top of page

Azure Infrastructure as Code - Part Four

Writer's picture: Tyler WallTyler Wall

Updated: Feb 10

Azure Cybersecurity Labs

Azure Infrastructure as Code - Part Four

Let's get started on Azure Infrastructure as Code - Part Four. In this lab we are going to continue our Terraform exercises by deploying a honeypot via Terraform. If you have been following along, previously on this blog I had you installed a T-Pot manually using the GUI in Azure. There's a much easier way to do this, so let's get rollin'.


Create the Terraform Configuration File

First, in the terminal on Mac we will issue the following commands to create a directory that will contain our Terraform configuration:


mkdir ~/tpot
cd ~/tpot

And open up a file for main.tf


code main.tf
On Windows create a folder anywhere called "tpot" and create a new file called "main" with the file extension ".tf" and open that file with Visual Studio Code

Now we need to write configuration to create a few new resources. Copy and paste the code snippet into the "main.tf" file


terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "3.90.0"
    }
  }
}

provider "azurerm" {
  # Configuration options
  features {
    
  }
}

variable "prefix" {
  default = "tpot"
}

resource "azurerm_resource_group" "tpot-rg" {
  name     = "${var.prefix}-resources"
  location = "East US"
}

resource "azurerm_virtual_network" "main" {
  name                = "${var.prefix}-network"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.tpot-rg.location
  resource_group_name = azurerm_resource_group.tpot-rg.name
}

resource "azurerm_subnet" "internal" {
  name                 = "internal"
  resource_group_name  = azurerm_resource_group.tpot-rg.name
  virtual_network_name = azurerm_virtual_network.main.name
  address_prefixes     = ["10.0.2.0/24"]
}

resource "azurerm_virtual_machine" "main" {
  depends_on = [ azurerm_resource_group.tpot-rg ]
  name                  = "${var.prefix}-vm"
  location              = azurerm_resource_group.tpot-rg.location
  resource_group_name   = azurerm_resource_group.tpot-rg.name
  network_interface_ids = [azurerm_network_interface.tpot-vm-nic.id]
  vm_size               = "Standard_A2m_v2"

  # Uncomment this line to delete the OS disk automatically when deleting the VM
  delete_os_disk_on_termination = true

  # Uncomment this line to delete the data disks automatically when deleting the VM
  delete_data_disks_on_termination = true

  storage_image_reference {
    publisher = "canonical"
    offer     = "ubuntu-24_04-lts"
    sku       = "minimal-gen1"
    version   = "latest"
  }
  storage_os_disk {
    name              = "tpot-disk"
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }
  os_profile {
    computer_name  = "hostname"
    admin_username = "azureuser"
    admin_password = "CyberNOW!"
  }
  os_profile_linux_config {
    disable_password_authentication = false
  }
}
# Create Security Group to access linux
resource "azurerm_network_security_group" "tpot-nsg" {
  depends_on=[azurerm_resource_group.tpot-rg]
  name                = "linux-vm-nsg"
  location            = azurerm_resource_group.tpot-rg.location
  resource_group_name = azurerm_resource_group.tpot-rg.name
  security_rule {
    name                       = "AllowALL"
    description                = "AllowALL"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "*"
    source_address_prefix      = "Internet"
    destination_address_prefix = "*"
  }
  security_rule {
    name                       = "AllowSSH"
    description                = "Allow SSH"
    priority                   = 150
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "Internet"
    destination_address_prefix = "*"
  }
}
# Associate the linux NSG with the subnet
resource "azurerm_subnet_network_security_group_association" "tpot-vm-nsg-association" {
  depends_on=[azurerm_resource_group.tpot-rg]
  subnet_id                 = azurerm_subnet.internal.id
  network_security_group_id = azurerm_network_security_group.tpot-nsg.id
}
# Get a Static Public IP
resource "azurerm_public_ip" "tpot-vm-ip" {
  depends_on=[azurerm_resource_group.tpot-rg]
  name                = "tpot-vm-ip"
  location            = azurerm_resource_group.tpot-rg.location
  resource_group_name = azurerm_resource_group.tpot-rg.name
  allocation_method   = "Static"
}
  # Create Network Card for linux VM
resource "azurerm_network_interface" "tpot-vm-nic" {
  depends_on=[azurerm_resource_group.tpot-rg]
  name                = "tpot-vm-nic"
  location            = azurerm_resource_group.tpot-rg.location
  resource_group_name = azurerm_resource_group.tpot-rg.name
    ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.internal.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.tpot-vm-ip.id
  }
}
output "public_ip" {
    value = azurerm_public_ip.tpot-vm-ip.ip_address
  }

Something I'm just going to note here because it's difficult information to find, is if you want to find the SKU of a particular image you can search for it like this syntax:
az vm image list --publisher Canonical --sku gen1 --output table --all 

Type az login in the terminal to establish your credentials


az login

Initialize the directory


terraform init

Now terraform plan


terraform plan
Note: Take a look at the Terraform Plan and see the 8 resources that we are creating. While not mandatory, it's good practice to 'Terraform Plan' to review your changes BEFORE deploying.

Now terraform apply


terraform apply

It will output the public IP address. Just SSH into it with the credentials

(ssh azureuser@<ipaddress>)


Username: azureuser
Password: CyberNOW!

And install the honeypot


env bash -c "$(curl -sL https://github.com/telekom-security/tpotce/raw/master/install.sh)"

Select "Hive" install

sudo reboot (when finished)

Note: The installation script changes the port to SSH on, so if you want to ssh to it you have to use this syntax "ssh azureuser@<ip address> -p 64295"

You can now login to the honeypot web interface via


https://<ipaddress>:64297

See how much easier this is than configuring it manually? This blog series won't go into detail about how to create a Terraform for scratch, but at this point you understand the basic Terraform lifecycle and understand its application and what its used for.


I recommend now picking up a Udemy course on the Terraform Associate exam and spend the next couple of days studying for the exam. The Terraform Associate exam itself isn't very costly, and makes great wall art.


When you are finished with the Tpot, make sure you aren't charged anything further and use the "terraform destroy” command to remove everything you did in one swoop. Easy peasy. Join us next in this series as conduct automated scans of Terraform files for configuration issues using the open source tool Checkov.




Azure Infrastructure as Code

Tyler Wall is the founder of Cyber NOW Education. He holds bills for a Master of Science from Purdue University and also CISSP, CCSK, CFSR, CEH, Sec+, Net+, and A+ certifications. He mastered the SOC after having held every position from analyst to architect and is the author of three books, 100+ professional articles, four online courses, and regularly holds webinars for new cybersecurity talent.


You can connect with him on LinkedIn.


To view my dozens of courses, visit my homepage and watch the trailers!


Become a Black Badge member of Cyber NOW® and enjoy all-access for life.


Check out my latest book, Jump-start Your SOC Analyst Career: A Roadmap to Cybersecurity Success, winner of the 2024 Cybersecurity Excellence Awards.




bottom of page