Skip to main contentIBM Cloud Patterns

IaC to Manage Network Resources

IaC to manage Network Services Resources

Every infrastructure requires network resources in order to build everything on top of it. This page explain all the Network Resources needed to provision and deploy an application exposing an API about movies. The following diagram shows the network architecture to build in this page.

Architecture

The code to build these resources can be downloaded from the GitHub repository https://github.com/IBM/cloud-enterprise-examples/ in the directory 06-network.

VPC

IBM Cloud Virtual Private Cloud (VPC) allows you to create your own space in the IBM Cloud, to run an isolated environment within the public cloud. VPC gives you the security of a private cloud, with the agility and ease of a public cloud.

To create a VPC with Terraform use the resource ibm_is_vpc providing the name and optionally the resource_group, add this code to the network.tf file.

network.tf
resource "ibm_is_vpc" "iac_app_vpc" {
name = "${var.project_name}-${var.environment}-vpc"
}

More parameters are accepted, to know more about them read the ibm_is_vpc documentation.

Subnets and Zones

A VPC is divided into subnets, using a range of private IP addresses. After creating the VPC you can add one or more subnets. Bu default all resources within the same VPC can communicate with each other, regardless of the subnets.

Each subnet must be contained within a single Zone and cannot span zones. Zones are distinct locations that are engineered to be isolated from failures in other Zones. By launching instances in separate Zones, you can protect your applications from the failure of a single location, helping with security, with reducing latency, and with high availability.

To create a Subnet with Terraform we use the resource ibm_is_subnet, it requires the name, the vpc and the zone where the subnet belongs.

An example of a simple subnet would be like this

resource "ibm_is_subnet" "iac_app_subnet" {
name = "${var.project_name}-${var.environment}-subnet"
vpc = ibm_is_vpc.iac_app_vpc.id
zone = "us-south-1"
ipv4_cidr_block = "10.240.0.0/24"
}

This example creates a subnet named from the project name and environment, in the VPC previously created, in the zone us-south-1 and with CIDR block 10.240.0.0/24. These and other parameters are:

Input parameterDescription
namename of the subnet
vpcVPC ID where the subnet is created

| zone | Zone name where the subnet is created | | ipv4_cidr_block | (optional) IPv4 range of the subnet | | total_ipv4_address_count | (optional) total number of IPv4 addresses | | network_acl | (optional) ID of the network ACL for the subnet | | public_gateway | (optional) ID of the public gateway for the subnet that you want to attach to the subnet. You create the public gateway with the ibm_is_public_gateway resource | | resource_group | (optional) ID of the resource group where you want to create the subnet |

For more information read the ibm_is_subnet documentation.

In our use case we need multiple subnets, append the following code to network.tf to create them all using the index as part of the name.

network.tf
resource "ibm_is_subnet" "iac_app_subnet" {
count = local.max_size
name = "${var.project_name}-${var.environment}-subnet-${format("%02s", count.index)}"
zone = var.vpc_zone_names[count.index]
vpc = ibm_is_vpc.iac_app_vpc.id
total_ipv4_address_count = 16
public_gateway = element(ibm_is_public_gateway.pgw.*.id, count.index)
resource_group = data.ibm_resource_group.group.id
}

Instead of define a CIDR block lets define the total number of IP address and get the default VPC address prefixes.

This code creates the subnets in different zones, defined in the list variable vpc_zone_names which is in the variables.tf file like this.

variables.tf
variable "region" {
default = "us-south"
}
variable "vpc_zone_names" {
type = list(string)
default = ["us-south-1", "us-south-2", "us-south-3"]
}
locals {

As you can see the number of subnets to create depends on the number of zones in the list vpc_zone_names.

Security Groups

A security group is like a virtual firewall for your VSI to control inbound and outbound traffic through filtering rules. The default rule (create the security group without rules) denies all traffic, you need to add rules to define the filters for inbound and outbound traffic. These rules allow reverse traffic, this means a rule opening port 80 for inbound traffic also open the port 80 for outbound traffic to the original host.

To create the security groups and the rules we use the resources ibm_is_security_group and ibm_is_security_group_rule. Adding the following code to network.tf we open the port defined by the user (defaults to 8080) and port 22 to allow SSH connections (just for testing, we can remove it later) only to the VSI in the first security group.

network.tf
resource "ibm_is_security_group" "iac_app_security_group" {
name = "${var.project_name}-${var.environment}-sg-public"
vpc = ibm_is_vpc.iac_app_vpc.id
resource_group = data.ibm_resource_group.group.id
}
resource "ibm_is_security_group_rule" "iac_app_security_group_rule_tcp_http" {
count = local.max_size
group = ibm_is_security_group.iac_app_security_group.id

Other option to set access rules to the security group is through the Network Access Control List (ACL) with the ibm_is_network_acl resource.

Three options are available for enabling communications from your VSI to the public internet:

  • Use a public gateway (PGW) to enable communication to the internet for all virtual server instances on the attached subnet. There is no charge for using a PGW, except for the bandwidth used.
  • Use a Floating IP (FIP) to enable communication to and from a single VSI to the internet.
  • Use a VPN gateway.

Public Gateways

A Public Gateway (PGW) enables a subnet to connect to the Internet. PGW uses Many-to-1 NAT, which means every VSI with private addresses will use 1 public IP address to talk to the public Internet. The PGW are tie to the VPC and the Zone, allowing internet connection to all the subnets and VSI within that zone.

To create a PGW we use the ibm_is_public_gateway resources like the following code appended to the network.tf file.

network.tf
resource "ibm_is_public_gateway" "pgw" {
count = local.max_size
name = "${var.project_name}-${var.environment}-pgw-${format("%02s", count.index)}"
vpc = ibm_is_vpc.iac_app_vpc.id
zone = var.vpc_zone_names[count.index]
}

Besides the parameters in the example you can also have the optional parameters resource_group and tags. Among the output parameters we have floating_ip with a collection of floating IP addresses that are bound to the public gateway. Every floating IP address is listed with the floating IP id and address. To report these IP address, lets use the following output variables in the file output.tf

output.tf
output "pgw_ip_addresses" {
value = ibm_is_public_gateway.pgw[*].floating_ip.address
}

Floating IP

Floating IP addresses are IP addresses that are provided by IBM Cloud and are reachable from the public Internet. An Floating IP is linked to a VSI, Load Balancer or VPN Gateway, allowing internet access to this linked resource only.

Having a public gateway defined we do not need Floating IP but if you’d like to have them instead of PWG this is the code to add to network.tf to link every Floating IP to a VSI.

network.tf
resource "ibm_is_floating_ip" "iac_app_floating_ip" {
count = var.max_size
name = "${var.project_name}-${var.environment}-ip-${format("%02s", count.index)}"
target = ibm_is_instance.iac_app_instance[count.index].primary_network_interface.0.id
}

To list the Floating IPs we can use the following code with output variables.

output.tf
output "ip_address" {
value = ibm_is_floating_ip.iac_app_floating_ip[*].address
}

VPN

The last resource to allow internet connection is Virtual Private Network (VPN), a service that is available for users to connect to their IBM Cloud VPC from the Internet, securely. This method may be the best option to connect to your VPC but it may not be suitable for every architecture or requirements.

A simple example, not to be used in this story would be like this.

resource "ibm_is_vpn_gateway" "iac_app_vpn_gateway" {
name = "${var.project_name}-${var.environment}-vpn"
subnet = ibm_is_subnet.iac_app_subnet[0].ipv4_cidr_block
}
resource "ibm_is_vpn_gateway_connection" "VPNGatewayConnection" {
name = "${var.project_name}-${var.environment}-vpn-gw"
vpn_gateway = ibm_is_vpn_gateway.iac_app_vpn_gateway.id

Having all the resources ready in the network.tf file and the variables set, we just need to execute the following terraform commands.

terraform init # Only the first time
terraform plan
terraform apply

Final Terraform code

You can download the code from the GitHub repository https://github.com/IBM/cloud-enterprise-examples/ in the directory 06-network where the main files are:

network.tfresource
name = "${var.project_name}-${var.environment}-vpc"
}
resource "ibm_is_subnet" "iac_app_subnet" {
count = local.max_size
name = "${var.project_name}-${var.environment}-subnet-${format("%02s", count.index)}"
zone = var.vpc_zone_names[count.index]
vpc = ibm_is_vpc.iac_app_vpc.id
total_ipv4_address_count = 16
variables.tf
variable "project_name" {}
variable "environment" {}
variable "port" {
default = 8080
}
variable "resource_group" {
default = "Default"
output.rf
// output "ip_address" {
// value = ibm_is_floating_ip.iac_app_floating_ip[*].address
// }
output "pgw_ip_addresses" {
value = ibm_is_public_gateway.pgw[*].floating_ip.address
}

Clean up

To clean up everything just execute:

terraform destroy