Week 3 ‐ Introduction to Terraform - AlanK23/Sec-350-01 GitHub Wiki
Me and lab partner never really talked about who uses what port so we'll figure that out eventually later
Terraform Docker Code
Completed Terraform configuration that defines a full Docker stack using the student_name variable for resource isolation. All resources use the ${var.student_name}- prefix. The stack is applied and the container is verified running.
main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.9"
}
}
}
provider "docker" {}
variable "student_name" {
type = string
description = "Your student identifier (e.g., jsmith, mgarcia)"
validation {
condition = can(regex("^[a-z]{2,10}$", var.student_name))
error_message = "Student name must be 2-10 lowercase letters."
}
}
variable "host_port" {
description = "Port on the host (coordinate with your partner)"
type = number
default = 8080
}
resource "docker_image" "nginx" {
name = "nginx:latest"
}
resource "docker_network" "lan" {
name = "${var.student_name}-sys350-lan"
}
resource "docker_volume" "web_data" {
name = "${var.student_name}-nginx-html"
}
resource "docker_container" "web_server" {
name = "${var.student_name}-web-server"
image = docker_image.nginx.image_id
ports {
internal = 80
external = var.host_port
}
networks_advanced {
name = docker_network.lan.name
}
volumes {
volume_name = docker_volume.web_data.name
container_path = "/usr/share/nginx/html"
read_only = false
}
}
output "container_ip" {
value = docker_container.web_server.network_data[0].ip_address
}
Terraform apply
- I have it on port 8080 for now
Terraform libvirt Code
Completed Terraform configuration that defines a full libvirt stack using the student_name variable for resource isolation. All resources use the ${var.student_name}- prefix. The stack is applied and the VM is verified running.
main-libvirt.tf
provider "libvirt" {
uri = "qemu:///system"
}
variable "vm_memory" {
description = "VM memory in KiB"
type = number
default = 1048576 # 1 GB
}
variable "vm_vcpu" {
description = "Number of virtual CPUs"
type = number
default = 4
}
resource "libvirt_network" "lan" {
name = "${var.student_name}-lan"
autostart = true
forward = {
mode = "nat"
}
ips = [
{
address = "192.168.100.1"
prefix = 24
family = "ipv4"
dhcp = {
ranges = [
{
start = "192.168.100.100"
end = "192.168.100.254"
}
]
}
}
]
}
resource "libvirt_pool" "default" {
name = "${var.student_name}-default"
type = "dir"
target = {
path = "/var/lib/libvirt/images"
permissions = {
owner = 64055
group = 64055
mode = "0750"
}
}
}
resource "libvirt_volume" "ubuntu_base" {
name = "ubuntu-base.qcow2"
pool = "${var.student_name}-default"
target = {
format = {
type = "qcow2"
}
}
create = {
content = {
url = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
}
}
depends_on = [libvirt_pool.default]
}
resource "libvirt_volume" "ubuntu_disk" {
name = "${var.student_name}-ubuntu-server.qcow2"
pool = libvirt_pool.default.name
capacity = 42949672960 # 40 GB
target = {
format = {
type = "qcow2"
}
}
backing_store = {
path = libvirt_volume.ubuntu_base.path
format = {
type = "qcow2"
}
}
}
resource "libvirt_domain" "ubuntu_server" {
name = "${var.student_name}-ubuntu-server"
memory = var.vm_memory
vcpu = var.vm_vcpu
type = "kvm"
os = {
type = "hvm"
type_arch = "x86_64"
type_machine = "q35"
}
devices = {
disks = [
{
source = {
volume = {
pool = "${var.student_name}-default"
volume = libvirt_volume.ubuntu_disk.name
}
}
target = {
bus = "virtio"
dev = "vda"
}
driver = {
type = "qcow2"
}
}
]
interfaces = [
{
type = "network"
model = { type = "virtio" }
source = {
network = {
network = libvirt_network.lan.name
}
}
}
]
graphics = [
{
vnc = {
auto_port = true
listen = "127.0.0.1"
}
}
]
}
running = true
}
output "vm_network" {
value = libvirt_network.lan.name
}
output "vm_name" {
value = libvirt_domain.ubuntu_server.name
}
libvirt config up
Git Version Control
Git repository initialized in the Terraform project directory. All .tf files and .gitignore committed with descriptive messages. Code pushed to a remote repository (VM #3 or GitHub). State file and .terraform/ directory excluded via .gitignore.
- Pushed to this github
Bugs/Fixes
When applying for the libvirt configuration, I got an error that went something like:
Could not open '/var/lib/libvirt/images/ubuntu-base.qcow2': Permission denied
The fix was stopping and disabling the service apparmor
Alan's Terraform Instructions
main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
libvirt = {
source = "dmacvicar/libvirt"
version = "~> 0.9"
}
}
}
provider "docker" {}
variable "student_name" {
type = string
description = "Your student identifier (e.g., jsmith, mgarcia)"
validation {
condition = can(regex("^[a-z]{2,10}$", var.student_name))
error_message = "Student name must be 2-10 lowercase letters."
}
}
variable "host_port" {
description = "Port on the host (coordinate with your partner)"
type = number
default = 8081
}
resource "docker_image" "nginx" {
name = "nginx:latest"
}
resource "docker_network" "lan" {
name = "${var.student_name}-sys350-lan"
}
resource "docker_volume" "web_data" {
name = "${var.student_name}-nginx-html"
}
resource "docker_container" "web_server" {
name = "${var.student_name}-web-server"
image = docker_image.nginx.image_id
ports {
internal = 80
external = var.host_port
}
networks_advanced {
name = docker_network.lan.name
}
volumes {
volume_name = docker_volume.web_data.name
container_path = "/usr/share/nginx/html"
read_only = false
}
}
output "container_ip" {
value = docker_container.web_server.network_data[0].ip_address
}
main-libvirt.tf
provider "libvirt" {
uri = "qemu:///system"
}
variable "vm_memory" {
description = "VM memory in KiB"
type = number
default = 1048576 # 1 GB
}
variable "vm_vcpu" {
description = "Number of virtual CPUs"
type = number
default = 4
}
resource "libvirt_network" "lan" {
name = "${var.student_name}-lan"
autostart = true
forward = {
mode = "nat"
}
ips = [
{
address = "192.168.101.1"
prefix = 24
family = "ipv4"
dhcp = {
ranges = [
{
start = "192.168.101.100"
end = "192.168.101.254"
}
]
}
}
]
}
resource "libvirt_pool" "default" {
name = "${var.student_name}-default"
type = "dir"
target = {
path = "/var/lib/libvirt/images/${var.student_name}"
permissions = {
owner = 64055
group = 64055
mode = "0750"
}
}
}
resource "libvirt_volume" "ubuntu_base" {
name = "${var.student_name}-ubuntu-base.qcow2"
pool = libvirt_pool.default.name
target = {
format = {
type = "qcow2"
}
}
create = {
content = {
url = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
}
}
depends_on = [libvirt_pool.default]
}
resource "libvirt_volume" "ubuntu_disk" {
name = "${var.student_name}-ubuntu-server.qcow2"
pool = libvirt_pool.default.name
capacity = 42949672960 # 40 GB
target = {
format = {
type = "qcow2"
}
}
backing_store = {
path = libvirt_volume.ubuntu_base.path
format = {
type = "qcow2"
}
}
}
resource "libvirt_domain" "ubuntu_server" {
name = "${var.student_name}-ubuntu-server"
memory = var.vm_memory
vcpu = var.vm_vcpu
type = "kvm"
os = {
type = "hvm"
type_arch = "x86_64"
type_machine = "q35"
}
devices = {
disks = [
{
source = {
volume = {
pool = libvirt_pool.default.name
volume = libvirt_volume.ubuntu_disk.name
}
}
target = {
bus = "virtio"
dev = "vda"
}
driver = {
type = "qcow2"
}
}
]
interfaces = [
{
type = "network"
model = { type = "virtio" }
source = {
network = {
network = libvirt_network.lan.name
}
}
}
]
graphics = [
{
vnc = {
auto_port = true
listen = "127.0.0.1"
}
}
]
}
running = true
}
output "network_address" {
value = "Network '${libvirt_network.lan.name}' CIDR: 192.168.101.0/24, Gateway: 192.168.101.1"
}
output "vm_name" {
value = libvirt_domain.ubuntu_server.name
}
Confirm the host is clear before applying
sudo ss -tlnp | grep 8081
virsh pool-list --all
virsh net-list --all
ls -la /var/lib/libvirt/images/
Set my variables
nano terraform.tfvars
student_name = "alank"
host_port = 8081
Initialize and apply
terraform init
terraform plan
terraform apply
Verify the Docker stack
docker ps
curl -I http://localhost:8081
Verify the libvirt stack
virsh list --all
virsh net-list --all
virsh pool-list --all
Confirm all resources are tracked in state
terraform state list
Demonstrate the destroy and apply cycle
terraform destroy -target=docker_container.web_server
docker ps
terraform apply -target=docker_container.web_server
docker ps
.terraform/
terraform.tfstate
terraform.tfstate.backup
terraform.tfstate.*.backup
*.tfvars
!example.tfvars
crash.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json
Format and validate
terraform fmt
terraform validate
Tear down
terraform destroy