Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for creating instances using the new VNI #737

Merged
merged 17 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 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,10 @@ 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_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 @@ -200,6 +204,7 @@ No modules.
| <a name="input_load_balancers"></a> [load\_balancers](#input\_load\_balancers) | Load balancers to add to VSI | <pre>list(<br/> object({<br/> name = string<br/> type = string<br/> listener_port = optional(number)<br/> listener_port_max = optional(number)<br/> listener_port_min = optional(number)<br/> listener_protocol = string<br/> connection_limit = optional(number)<br/> idle_connection_timeout = optional(number)<br/> algorithm = string<br/> protocol = string<br/> health_delay = number<br/> health_retries = number<br/> health_timeout = number<br/> health_type = string<br/> pool_member_port = string<br/> profile = optional(string)<br/> accept_proxy_protocol = optional(bool)<br/> subnet_id_to_provision_nlb = optional(string) # Required for Network Load Balancer. If no value is provided, the first one from the VPC subnet list will be selected.<br/> dns = optional(<br/> object({<br/> instance_crn = string<br/> zone_id = string<br/> })<br/> )<br/> security_group = optional(<br/> object({<br/> name = string<br/> rules = list(<br/> object({<br/> name = string<br/> direction = string<br/> source = string<br/> tcp = optional(<br/> object({<br/> port_max = number<br/> port_min = number<br/> })<br/> )<br/> udp = optional(<br/> object({<br/> port_max = number<br/> port_min = number<br/> })<br/> )<br/> icmp = optional(<br/> object({<br/> type = number<br/> code = number<br/> })<br/> )<br/> })<br/> )<br/> })<br/> )<br/> })<br/> )</pre> | `[]` | no |
| <a name="input_machine_type"></a> [machine\_type](#input\_machine\_type) | VSI machine type. Run 'ibmcloud is instance-profiles' to get a list of regional profiles | `string` | n/a | yes |
| <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_number_of_secondary_reserved_ips"></a> [number\_of\_secondary\_reserved\_ips](#input\_number\_of\_secondary\_reserved\_ips) | 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_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_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 |
Expand All @@ -216,6 +221,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
number_of_secondary_reserved_ips = 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
166 changes: 147 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.number_of_secondary_reserved_ips) : [
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,59 @@ 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.number_of_secondary_reserved_ips > 0 ? { for count in range(var.number_of_secondary_reserved_ips) : 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
Aashiq-J marked this conversation as resolved.
Show resolved Hide resolved
}

##############################################################################
# Create Virtual Servers
##############################################################################
Expand All @@ -99,6 +186,13 @@ 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.number_of_secondary_reserved_ips > 0 && !var.use_legacy_network_interface }
name = "${each.value.name}-ip"
subnet = each.value.subnet_id
auto_delete = false
}

resource "ibm_is_instance" "vsi" {
for_each = local.vsi_map
name = each.value.vsi_name
Expand All @@ -118,26 +212,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 {
toddgiguere marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all of the VNIs should be managed by terraform outside the VSI block, just like you have done with the primary. That will make it much easier to leave them alone and simply reattach if the VSI gets recreated, and they will retain all of their settings and IPs. Basically we want the VSI block here to manage as little as possible and we would "attach" resources managed outside the block instead. This way if a rollback occurs (or the VSI gets recreated for any reason) it will not destroy some of these other attachments (like VNIs) so that they maintain their state when reattached.

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 +301,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
}
##############################################################################
Loading