Skip to content

Commit

Permalink
feat: add support for [Virtual Network Interface](https://cloud.ibm.c…
Browse files Browse the repository at this point in the history
…om/docs/vpc?topic=vpc-vni-about)<br>- The module now creates VSI using the next gen virtual network interface by default, these VNIs are created independent of the VSIs.<br>- *IMPORTANT* When upgrading from a previous version, VSIs may be destroyed and recreated. To prevent re-creation or to use the legacy network interface, set `var.use_legacy_network_interface` to `true`. (#737)
  • Loading branch information
Aashiq-J authored Nov 11, 2024
1 parent 5b3fe9e commit 46cd958
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 38 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ No modules.
|------|------|
| [ibm_iam_authorization_policy.block_storage_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource |
| [ibm_is_floating_ip.secondary_fip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_floating_ip) | resource |
| [ibm_is_floating_ip.vni_secondary_fip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_floating_ip) | resource |
| [ibm_is_floating_ip.vsi_fip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_floating_ip) | resource |
| [ibm_is_instance.vsi](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_instance) | resource |
| [ibm_is_lb.lb](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb) | resource |
Expand All @@ -176,7 +177,11 @@ No modules.
| [ibm_is_lb_pool_member.nlb_pool_members](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_lb_pool_member) | resource |
| [ibm_is_security_group.security_group](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group) | resource |
| [ibm_is_security_group_rule.security_group_rules](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group_rule) | resource |
| [ibm_is_subnet_reserved_ip.secondary_vni_ip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_subnet_reserved_ip) | resource |
| [ibm_is_subnet_reserved_ip.secondary_vsi_ip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_subnet_reserved_ip) | resource |
| [ibm_is_subnet_reserved_ip.vsi_ip](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_subnet_reserved_ip) | resource |
| [ibm_is_virtual_network_interface.primary_vni](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_virtual_network_interface) | resource |
| [ibm_is_virtual_network_interface.secondary_vni](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_virtual_network_interface) | resource |
| [ibm_is_volume.volume](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_volume) | resource |
| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
| [ibm_is_snapshot.snapshots_from_group](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_snapshot) | data source |
Expand All @@ -202,6 +207,7 @@ No modules.
| <a name="input_manage_reserved_ips"></a> [manage\_reserved\_ips](#input\_manage\_reserved\_ips) | Set to `true` if you want this terraform module to manage the reserved IP addresses that are assigned to VSI instances. If this option is enabled, when any VSI is recreated it should retain its original IP. | `bool` | `false` | no |
| <a name="input_placement_group_id"></a> [placement\_group\_id](#input\_placement\_group\_id) | Unique Identifier of the Placement Group for restricting the placement of the instance, default behaviour is placement on any host | `string` | `null` | no |
| <a name="input_prefix"></a> [prefix](#input\_prefix) | The IBM Cloud platform API key needed to deploy IAM enabled resources | `string` | n/a | yes |
| <a name="input_primary_vni_additional_ip_count"></a> [primary\_vni\_additional\_ip\_count](#input\_primary\_vni\_additional\_ip\_count) | The number of secondary reversed IPs to attach to a Virtual Network Interface (VNI). Additional IPs are created only if `manage_reserved_ips` is set to true. | `number` | `0` | no |
| <a name="input_resource_group_id"></a> [resource\_group\_id](#input\_resource\_group\_id) | ID of resource group to create VSI and block storage volumes. If you wish to create the block storage volumes in a different resource group, you can optionally set that directly in the 'block\_storage\_volumes' variable. | `string` | n/a | yes |
| <a name="input_secondary_allow_ip_spoofing"></a> [secondary\_allow\_ip\_spoofing](#input\_secondary\_allow\_ip\_spoofing) | Allow IP spoofing on additional network interfaces | `bool` | `false` | no |
| <a name="input_secondary_floating_ips"></a> [secondary\_floating\_ips](#input\_secondary\_floating\_ips) | List of secondary interfaces to add floating ips | `list(string)` | `[]` | no |
Expand All @@ -216,6 +222,7 @@ No modules.
| <a name="input_subnets"></a> [subnets](#input\_subnets) | A list of subnet IDs where VSI will be deployed | <pre>list(<br/> object({<br/> name = string<br/> id = string<br/> zone = string<br/> cidr = optional(string)<br/> })<br/> )</pre> | n/a | yes |
| <a name="input_tags"></a> [tags](#input\_tags) | List of tags to apply to resources created by this module. | `list(string)` | `[]` | no |
| <a name="input_use_boot_volume_key_as_default"></a> [use\_boot\_volume\_key\_as\_default](#input\_use\_boot\_volume\_key\_as\_default) | Set to true to use the key specified in the `boot_volume_encryption_key` input as default for all volumes, overriding any key value that may be specified in the `encryption_key` option of the `block_storage_volumes` input variable. If set to `false`, the value passed for the `encryption_key` option of the `block_storage_volumes` will be used instead. | `bool` | `false` | no |
| <a name="input_use_legacy_network_interface"></a> [use\_legacy\_network\_interface](#input\_use\_legacy\_network\_interface) | Set this to true to use legacy network interface for the created instances. | `bool` | `false` | no |
| <a name="input_use_static_boot_volume_name"></a> [use\_static\_boot\_volume\_name](#input\_use\_static\_boot\_volume\_name) | Sets the boot volume name for each VSI to a static name in the format `{hostname}_boot`, instead of a random name. Set this to `true` to have a consistent boot volume name even when VSIs are recreated. | `bool` | `false` | no |
| <a name="input_user_data"></a> [user\_data](#input\_user\_data) | User data to initialize VSI deployment | `string` | n/a | yes |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | ID of VPC | `string` | n/a | yes |
Expand Down
44 changes: 25 additions & 19 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -160,25 +160,31 @@ locals {
}

module "slz_vsi" {
source = "../../"
resource_group_id = module.resource_group.resource_group_id
image_id = var.image_id
create_security_group = false
tags = var.resource_tags
access_tags = var.access_tags
subnets = module.slz_vpc.subnet_zone_list
vpc_id = module.slz_vpc.vpc_id
prefix = var.prefix
placement_group_id = ibm_is_placement_group.placement_group.id
machine_type = "cx2-2x4"
user_data = null
boot_volume_encryption_key = module.key_protect_all_inclusive.keys["slz-vsi.${var.prefix}-vsi"].crn
kms_encryption_enabled = true
existing_kms_instance_guid = module.key_protect_all_inclusive.kms_guid
vsi_per_subnet = 1
ssh_key_ids = [local.ssh_key_id]
secondary_subnets = local.secondary_subnet_zone_list
secondary_security_groups = local.secondary_security_groups
source = "../../"
resource_group_id = module.resource_group.resource_group_id
image_id = var.image_id
create_security_group = false
tags = var.resource_tags
access_tags = var.access_tags
subnets = module.slz_vpc.subnet_zone_list
vpc_id = module.slz_vpc.vpc_id
prefix = var.prefix
placement_group_id = ibm_is_placement_group.placement_group.id
machine_type = "cx2-2x4"
user_data = null
boot_volume_encryption_key = module.key_protect_all_inclusive.keys["slz-vsi.${var.prefix}-vsi"].crn
kms_encryption_enabled = true
existing_kms_instance_guid = module.key_protect_all_inclusive.kms_guid
vsi_per_subnet = 1
primary_vni_additional_ip_count = 2
ssh_key_ids = [local.ssh_key_id]
secondary_subnets = local.secondary_subnet_zone_list
secondary_security_groups = local.secondary_security_groups
# Create a floating IPs for the additional VNI
secondary_floating_ips = [
for subnet in local.secondary_subnet_zone_list :
subnet.name
]
# Create a floating IP for each virtual server created
enable_floating_ip = true
secondary_use_vsi_security_group = var.secondary_use_vsi_security_group
Expand Down
179 changes: 160 additions & 19 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,24 @@ locals {
server.name => server
}

secondary_fip_list = flatten([
# List of additional private IP addresses to bind to the primary virtual network interface.
secondary_reserved_ips_list = flatten([
for count in range(var.primary_vni_additional_ip_count) : [
for vsi_key, vsi_value in local.vsi_map :
{
name = "${vsi_key}-${count}"
subnet_id = vsi_value.subnet_id
}
]
])

secondary_reserved_ips_map = {
for ip in local.secondary_reserved_ips_list :
ip.name => ip
}

# Old approach to create floating IPs for the secondary network interface.
legacy_secondary_fip_list = var.use_legacy_network_interface ? flatten([
# For each interface in list of floating ips
for interface in var.secondary_floating_ips :
[
Expand All @@ -55,7 +72,24 @@ locals {
target = instance.network_interfaces[index(var.secondary_subnets[*].name, interface)].id
}
]
])
]) : []

# List of secondary Virtual network interface for which floating IPs needs to be added.
secondary_fip_list = !var.use_legacy_network_interface && length(var.secondary_floating_ips) != 0 ? flatten([
for instance in ibm_is_instance.vsi : [
for network_attachment in instance.network_attachments :
network_attachment if contains([for subnet in var.secondary_floating_ips : subnet], network_attachment.name)
]
]) : []

secondary_fip_map = {
for vni in local.secondary_fip_list :
vni.name => {
vni_name = vni.virtual_network_interface[0].name
subnet_name = vni.name
vni_id = vni.virtual_network_interface[0].id
}
}

# determine snapshot in following order: input variable -> from consistency group -> null (none)
vsi_boot_volume_snapshot_id = try(coalesce(var.boot_volume_snapshot_id, local.consistency_group_boot_snapshot_id), null)
Expand All @@ -76,6 +110,65 @@ data "ibm_is_vpc" "vpc" {
identifier = var.vpc_id
}

##############################################################################
# Create Virtual Network Interface
##############################################################################
resource "ibm_is_virtual_network_interface" "primary_vni" {
for_each = { for vsi_key, vsi_value in local.vsi_map : vsi_key => vsi_value if !var.use_legacy_network_interface }
name = "${each.key}-vni"
subnet = each.value.subnet_id
security_groups = flatten([
(var.create_security_group ? [ibm_is_security_group.security_group[var.security_group.name].id] : []),
var.security_group_ids,
(var.create_security_group == false && length(var.security_group_ids) == 0 ? [data.ibm_is_vpc.vpc.default_security_group] : []),
])
allow_ip_spoofing = var.allow_ip_spoofing
auto_delete = false
enable_infrastructure_nat = true
dynamic "primary_ip" {
for_each = var.manage_reserved_ips ? [1] : []
content {
reserved_ip = ibm_is_subnet_reserved_ip.vsi_ip[each.value.name].reserved_ip
}
}
dynamic "ips" {
for_each = var.primary_vni_additional_ip_count > 0 ? { for count in range(var.primary_vni_additional_ip_count) : count => count } : {}
content {
reserved_ip = ibm_is_subnet_reserved_ip.secondary_vsi_ip["${each.value.name}-${ips.key}"].reserved_ip
}
}
}

resource "ibm_is_virtual_network_interface" "secondary_vni" {
for_each = { for k in var.secondary_subnets : k.zone => k if !var.use_legacy_network_interface }
name = each.value.name
subnet = each.value.id
# If security_groups is empty(list is len(0)) then default list to data.ibm_is_vpc.vpc.default_security_group.
# If list is empty it will fail on reapply as when vsi is passed an empty security group list it will attach the default security group.
allow_ip_spoofing = var.secondary_allow_ip_spoofing
security_groups = length(flatten([
(var.create_security_group && var.secondary_use_vsi_security_group ? [ibm_is_security_group.security_group[var.security_group.name].id] : []),
[
for group in var.secondary_security_groups :
group.security_group_id if group.interface_name == each.value.name
]
])) == 0 ? [data.ibm_is_vpc.vpc.default_security_group] : flatten([
(var.create_security_group && var.secondary_use_vsi_security_group ? [ibm_is_security_group.security_group[var.security_group.name].id] : []),
[
for group in var.secondary_security_groups :
group.security_group_id if group.interface_name == each.value.name
]
])
auto_delete = false
enable_infrastructure_nat = true
dynamic "primary_ip" {
for_each = var.manage_reserved_ips ? [1] : []
content {
reserved_ip = ibm_is_subnet_reserved_ip.secondary_vni_ip[each.key].reserved_ip
}
}
}

##############################################################################
# Create Virtual Servers
##############################################################################
Expand All @@ -99,6 +192,20 @@ resource "ibm_is_subnet_reserved_ip" "vsi_ip" {
auto_delete = false
}

resource "ibm_is_subnet_reserved_ip" "secondary_vsi_ip" {
for_each = { for key, value in local.secondary_reserved_ips_map : key => value if var.primary_vni_additional_ip_count > 0 && !var.use_legacy_network_interface }
name = "${each.value.name}-ip"
subnet = each.value.subnet_id
auto_delete = false
}

resource "ibm_is_subnet_reserved_ip" "secondary_vni_ip" {
for_each = { for k in var.secondary_subnets : k.zone => k if !var.use_legacy_network_interface && var.manage_reserved_ips }
name = "${each.value.name}-ip"
subnet = each.value.id
auto_delete = false
}

resource "ibm_is_instance" "vsi" {
for_each = local.vsi_map
name = each.value.vsi_name
Expand All @@ -118,26 +225,52 @@ resource "ibm_is_instance" "vsi" {
]
}

primary_network_interface {
subnet = each.value.subnet_id
security_groups = flatten([
(var.create_security_group ? [ibm_is_security_group.security_group[var.security_group.name].id] : []),
var.security_group_ids,
(var.create_security_group == false && length(var.security_group_ids) == 0 ? [data.ibm_is_vpc.vpc.default_security_group] : []),
])
allow_ip_spoofing = var.allow_ip_spoofing
dynamic "primary_ip" {
for_each = var.manage_reserved_ips ? [1] : []
content {
reserved_ip = ibm_is_subnet_reserved_ip.vsi_ip[each.value.name].reserved_ip
# Primary Virtual Network Interface
dynamic "primary_network_attachment" {
for_each = var.use_legacy_network_interface ? [] : [1]
content {
name = "${each.value.subnet_name}-eth0"
virtual_network_interface {
id = ibm_is_virtual_network_interface.primary_vni[each.key].id
}
}
}

# Additional Virtual Network Interface
dynamic "network_attachments" {
for_each = { for key, value in ibm_is_virtual_network_interface.secondary_vni : key => value if key == each.value.zone && !var.use_legacy_network_interface }
content {
name = network_attachments.value.name
virtual_network_interface {
id = network_attachments.value.id
}
}
}

# Legacy Network Interface
dynamic "primary_network_interface" {
for_each = var.use_legacy_network_interface ? [1] : []
content {
subnet = each.value.subnet_id
security_groups = flatten([
(var.create_security_group ? [ibm_is_security_group.security_group[var.security_group.name].id] : []),
var.security_group_ids,
(var.create_security_group == false && length(var.security_group_ids) == 0 ? [data.ibm_is_vpc.vpc.default_security_group] : []),
])
allow_ip_spoofing = var.allow_ip_spoofing
dynamic "primary_ip" {
for_each = var.manage_reserved_ips ? [1] : []
content {
reserved_ip = ibm_is_subnet_reserved_ip.vsi_ip[each.value.name].reserved_ip
}
}
}
}
# Legacy additional Network Interface
dynamic "network_interfaces" {
for_each = {
for k in var.secondary_subnets : k.zone => k
if k.zone == each.value.zone
if k.zone == each.value.zone && var.use_legacy_network_interface
}
content {
subnet = network_interfaces.value.id
Expand Down Expand Up @@ -181,22 +314,30 @@ resource "ibm_is_instance" "vsi" {
resource "ibm_is_floating_ip" "vsi_fip" {
for_each = var.enable_floating_ip ? ibm_is_instance.vsi : {}
name = "${each.value.name}-fip"
target = each.value.primary_network_interface[0].id
target = var.use_legacy_network_interface ? each.value.primary_network_interface[0].id : each.value.primary_network_attachment[0].virtual_network_interface[0].id
tags = var.tags
access_tags = var.access_tags
resource_group = var.resource_group_id
}

resource "ibm_is_floating_ip" "secondary_fip" {
for_each = length(var.secondary_floating_ips) == 0 ? {} : {
for interface in local.secondary_fip_list :
for_each = var.use_legacy_network_interface ? length(var.secondary_floating_ips) == 0 ? {} : {
for interface in local.legacy_secondary_fip_list :
(interface.name) => interface
}
} : {}
name = each.key
target = each.value.target
tags = var.tags
access_tags = var.access_tags
resource_group = var.resource_group_id
}

resource "ibm_is_floating_ip" "vni_secondary_fip" {
for_each = local.secondary_fip_map
name = each.key
target = each.value.vni_id
tags = var.tags
access_tags = var.access_tags
resource_group = var.resource_group_id
}
##############################################################################
13 changes: 13 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ variable "manage_reserved_ips" {
default = false
}

variable "primary_vni_additional_ip_count" {
description = "The number of secondary reversed IPs to attach to a Virtual Network Interface (VNI). Additional IPs are created only if `manage_reserved_ips` is set to true."
type = number
nullable = false
default = 0
}

variable "use_static_boot_volume_name" {
description = "Sets the boot volume name for each VSI to a static name in the format `{hostname}_boot`, instead of a random name. Set this to `true` to have a consistent boot volume name even when VSIs are recreated."
type = bool
Expand Down Expand Up @@ -488,3 +495,9 @@ variable "snapshot_consistency_group_id" {
}

##############################################################################

variable "use_legacy_network_interface" {
description = "Set this to true to use legacy network interface for the created instances."
type = bool
default = false
}

0 comments on commit 46cd958

Please sign in to comment.