Created
August 26, 2025 11:28
-
-
Save markus-hentsch/e729c16aad7291b5187d0a3e08f5e411 to your computer and use it in GitHub Desktop.
Sample Terraform recipe for OpenStack to create VMs and a load balancer with floating IP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ######### VARIABLES ######### # | |
| # name of the image in Glance to use for the | |
| # backend VMS that will be load-balanced | |
| variable "image_name" { | |
| type = string | |
| default = "cirros" | |
| } | |
| # name of the flavor to use for the load-balanced backend VMs | |
| variable "flavor_name" { | |
| type = string | |
| default = "S" | |
| } | |
| # name of the shared external network with floating ip pool | |
| variable "provider_network_name" { | |
| type = string | |
| default = "provider-network" | |
| } | |
| # CIDR of the tenant network to be created where | |
| # backend VMs and load balancer will live | |
| variable "tenant_network_cidr" { | |
| type = string | |
| default = "192.168.142.0/24" | |
| } | |
| # names of the backend VMs | |
| # NOTE: will create a dedicated VM for each name specified | |
| variable "server_names" { | |
| type = set(string) | |
| default = ["tf-test-instance-1", "tf-test-instance-2"] | |
| } | |
| # load balancing method to be set for the load balancer | |
| variable "loadbalance_method" { | |
| type = string | |
| default = "ROUND_ROBIN" | |
| } | |
| # ######### TERRAFORM PLUGINS ######### # | |
| terraform { | |
| required_version = ">= 0.14.0" | |
| required_providers { | |
| openstack = { | |
| source = "terraform-provider-openstack/openstack" | |
| version = "~> 1.53.0" | |
| } | |
| } | |
| } | |
| provider "openstack" { | |
| user_name = "terraform" | |
| tenant_name = "terraform-project" | |
| password = "tfpwd?§" | |
| auth_url = "https://keystone.openstack.local:5000/v3" | |
| region = "MyRegion" | |
| endpoint_type = "internal" | |
| } | |
| # ######### SCENARIO ######### # | |
| # acquire provider network ID from name | |
| data "openstack_networking_network_v2" "provider_net" { | |
| name = var.provider_network_name | |
| } | |
| # BEGIN:networking | |
| resource "openstack_networking_network_v2" "tenant_net" { | |
| name = "tf-tenant-network" | |
| } | |
| resource "openstack_networking_subnet_v2" "tenant_subnet" { | |
| name = "tf-tenant-subnet" | |
| network_id = openstack_networking_network_v2.tenant_net.id | |
| cidr = var.tenant_network_cidr | |
| } | |
| resource "openstack_networking_router_v2" "tentant_router" { | |
| name = "tf-tenant-router" | |
| external_network_id = data.openstack_networking_network_v2.provider_net.id | |
| depends_on = [data.openstack_networking_network_v2.provider_net] | |
| } | |
| resource "openstack_networking_router_interface_v2" "internal_iface" { | |
| router_id = openstack_networking_router_v2.tentant_router.id | |
| subnet_id = openstack_networking_subnet_v2.tenant_subnet.id | |
| } | |
| resource "openstack_networking_secgroup_v2" "ssh" { | |
| name = "tf-ssh" | |
| description = "Open input ssh port" | |
| } | |
| resource "openstack_networking_secgroup_rule_v2" "ssh_rule" { | |
| direction = "ingress" | |
| ethertype = "IPv4" | |
| protocol = "tcp" | |
| port_range_min = 22 | |
| port_range_max = 22 | |
| remote_ip_prefix = "0.0.0.0/0" | |
| security_group_id = openstack_networking_secgroup_v2.ssh.id | |
| } | |
| resource "openstack_networking_secgroup_v2" "icmp" { | |
| name = "tf-icmp" | |
| description = "Allow ping" | |
| } | |
| resource "openstack_networking_secgroup_rule_v2" "icmp_rule" { | |
| direction = "ingress" | |
| ethertype = "IPv4" | |
| protocol = "icmp" | |
| security_group_id = openstack_networking_secgroup_v2.icmp.id | |
| } | |
| # END:networking | |
| # BEGIN:servers | |
| resource "openstack_compute_instance_v2" "servers" { | |
| for_each = var.server_names | |
| name = each.key | |
| image_name = var.image_name | |
| flavor_name = var.flavor_name | |
| security_groups = [openstack_networking_secgroup_v2.ssh.name, openstack_networking_secgroup_v2.icmp.name] | |
| network { | |
| uuid = openstack_networking_network_v2.tenant_net.id | |
| } | |
| } | |
| # END:server | |
| # BEGIN:load-balancer | |
| resource "openstack_lb_loadbalancer_v2" "lb1" { | |
| name = "tf-lb-main" | |
| vip_subnet_id = openstack_networking_subnet_v2.tenant_subnet.id | |
| depends_on = [openstack_compute_instance_v2.servers] | |
| } | |
| resource "openstack_lb_listener_v2" "lb1" { | |
| name = "tf-listener-main" | |
| protocol = "TCP" | |
| protocol_port = 22 | |
| loadbalancer_id = openstack_lb_loadbalancer_v2.lb1.id | |
| depends_on = [openstack_lb_loadbalancer_v2.lb1] | |
| } | |
| resource "openstack_lb_pool_v2" "lb1" { | |
| name = "tf-lb-pool-main" | |
| protocol = "TCP" | |
| lb_method = var.loadbalance_method | |
| listener_id = openstack_lb_listener_v2.lb1.id | |
| depends_on = [openstack_lb_listener_v2.lb1] | |
| } | |
| resource "openstack_lb_member_v2" "lb1members" { | |
| for_each = var.server_names | |
| address = openstack_compute_instance_v2.servers[each.key].access_ip_v4 | |
| protocol_port = 22 | |
| pool_id = openstack_lb_pool_v2.lb1.id | |
| subnet_id = openstack_networking_subnet_v2.tenant_subnet.id | |
| depends_on = [openstack_lb_pool_v2.lb1] | |
| } | |
| resource "openstack_lb_monitor_v2" "lb1" { | |
| name = "tf-lb-mon-main" | |
| pool_id = openstack_lb_pool_v2.lb1.id | |
| type = "TCP" | |
| delay = 5 | |
| timeout = 5 | |
| max_retries = 3 | |
| depends_on = [openstack_lb_member_v2.lb1members] | |
| } | |
| # END:load-balancer | |
| # BEGIN:floating-ip | |
| resource "openstack_networking_floatingip_v2" "lb_floatip" { | |
| pool = var.provider_network_name | |
| description = "Load Balancer Floating IP created by Terraform" | |
| depends_on = [openstack_lb_loadbalancer_v2.lb1] | |
| } | |
| resource "openstack_networking_floatingip_associate_v2" "fip_1" { | |
| floating_ip = openstack_networking_floatingip_v2.lb_floatip.address | |
| port_id = openstack_lb_loadbalancer_v2.lb1.vip_port_id | |
| depends_on = [openstack_networking_floatingip_v2.lb_floatip] | |
| } | |
| # END:floating-ip | |
| # ######### OUTPUTS ######### # | |
| output "instance_public_ip" { | |
| description = "Floating IP of the load balancer" | |
| value = openstack_networking_floatingip_v2.lb_floatip.address | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # NOTE: make sure to adjust all variables at the top of the *.tf script | |
| # AND the connection settings in the `provider "openstack"` section! | |
| # (preparation) | |
| openstack user create terraform --password "tfpwd?§" | |
| openstack project create terraform-project | |
| openstack role add --user terraform --project terraform-project member | |
| openstack role add --user terraform --project terraform-project load-balancer_member | |
| # (terraform usage) | |
| TF_VERSION="1.13.0" | |
| apt install -y wget unzip | |
| wget https://releases.hashicorp.com/terraform/$TF_VERSION/terraform_$TF_VERSION_linux_amd64.zip | |
| unzip terraform_$TF_VERSION_linux_amd64.zip | |
| ./terraform init | |
| ./terraform plan | |
| ./terraform apply | |
| # (after successful creation, use the resulting floating IP address to verify the load balancing) | |
| ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" cirros@$FLOATING_IP_ADDR hostname | |
| # (this will print the backend VM's hostname and with round-robin it will alternate every call) | |
| # (cleanup) | |
| ./terraform destroy |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment