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.

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.