Skip to content

Commit

Permalink
feat: Support added to create or restore VSI volumes to existing stor…
Browse files Browse the repository at this point in the history
…age snapshots. The following new input variables have been added for this feature:<br>- `snapshot_consistency_group_id`<br>- `boot_volume_snapshot_id`<br>- `block_storage_volumes.snapshot_id` (new property in existing map)<br>- `manage_reserved_ips` (use to assign same reserved VSI instance IP address if recreating)<br>- `use_static_boot_volume_name` (use to keep boot volume names tied to VSI hostnames, instead of random generated name) (#685)
  • Loading branch information
toddgiguere authored Jul 30, 2024
1 parent 75b1f87 commit 666f518
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 21 deletions.
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,24 @@ Brewfile.lock.json

# Ignore schematics json
/update/*.json

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum
46 changes: 44 additions & 2 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/basic/version.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = "= 1.59.0"
version = "= 1.65.0"
}
tls = {
source = "hashicorp/tls"
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/version.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = ">= 1.54.0"
version = ">= 1.65.0"
}
tls = {
source = "hashicorp/tls"
Expand Down
2 changes: 1 addition & 1 deletion examples/fscloud/version.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ terraform {
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = ">= 1.54.0"
version = ">= 1.65.0"
}
tls = {
source = "hashicorp/tls"
Expand Down
11 changes: 11 additions & 0 deletions examples/snapshot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Basic example using a Snapshot Consistency Group for volumes

An end-to-end basic example that will provision the following, using previously created snapshots for storage volumes:

- A new resource group if one is not passed in.
- A new public SSH key if one is not passed in.
- A new VPC with 3 subnets
- A VSI in each subnet
- Two additional block storage attached to each VSI
- Reserved and Floating IPs managed by Terraform for each VSI
- Boot volume and additional storage volumes will be based on snapshots from consistency group, if ID is supplied
91 changes: 91 additions & 0 deletions examples/snapshot/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
##############################################################################
# Locals
##############################################################################

locals {
ssh_key_id = var.ssh_key != null ? data.ibm_is_ssh_key.existing_ssh_key[0].id : resource.ibm_is_ssh_key.ssh_key[0].id
}

##############################################################################
# Resource Group
##############################################################################

module "resource_group" {
source = "terraform-ibm-modules/resource-group/ibm"
version = "1.1.5"
# if an existing resource group is not set (null) create a new one using prefix
resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null
existing_resource_group_name = var.resource_group
}

##############################################################################
# Create new SSH key
##############################################################################

resource "tls_private_key" "tls_key" {
count = var.ssh_key != null ? 0 : 1
algorithm = "RSA"
rsa_bits = 4096
}

resource "ibm_is_ssh_key" "ssh_key" {
count = var.ssh_key != null ? 0 : 1
name = "${var.prefix}-ssh-key"
public_key = resource.tls_private_key.tls_key[0].public_key_openssh
}

data "ibm_is_ssh_key" "existing_ssh_key" {
count = var.ssh_key != null ? 1 : 0
name = var.ssh_key
}

#############################################################################
# Provision VPC
#############################################################################

module "slz_vpc" {
source = "terraform-ibm-modules/landing-zone-vpc/ibm"
version = "7.18.1"
resource_group_id = module.resource_group.resource_group_id
region = var.region
prefix = var.prefix
tags = var.resource_tags
name = "vpc"
}

#############################################################################
# Provision VSI
#############################################################################

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
machine_type = var.machine_type
vsi_per_subnet = 1
ssh_key_ids = [local.ssh_key_id]
user_data = null
manage_reserved_ips = true
enable_floating_ip = true
use_static_boot_volume_name = true
block_storage_volumes = [
{
name = "vsi-block-1"
profile = "general-purpose"
# snapshot_id = <you can also specify a specific snapshot ID if requried>
},
{
name = "vsi-block-2"
profile = "general-purpose"
# snapshot_id = <you can also specify a specific snapshot ID if requried>
}]
# if specifying a group ID, snapshot IDs will be automatically determined from group using system labels
snapshot_consistency_group_id = var.snapshot_consistency_group_id
# boot_volume_snapshot_id = <you can also specify a specific snapshot ID if requried>
}
9 changes: 9 additions & 0 deletions examples/snapshot/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
output "slz_vpc" {
value = module.slz_vpc
description = "VPC module values"
}

output "slz_vsi" {
value = module.slz_vsi
description = "VSI module values"
}
4 changes: 4 additions & 0 deletions examples/snapshot/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
provider "ibm" {
ibmcloud_api_key = var.ibmcloud_api_key
region = var.region
}
59 changes: 59 additions & 0 deletions examples/snapshot/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
variable "ibmcloud_api_key" {
description = "APIkey that's associated with the account to provision resources to"
type = string
sensitive = true
}

variable "resource_group" {
type = string
description = "An existing resource group name to use for this example, if unset a new resource group will be created"
default = null
}

variable "region" {
description = "The region to which to deploy the VPC"
type = string
default = "us-east"
}

variable "prefix" {
description = "The prefix that you would like to append to your resources"
type = string
default = "slz-vsi"
}

variable "resource_tags" {
description = "List of Tags for the resource created"
type = list(string)
default = null
}

variable "access_tags" {
type = list(string)
description = "A list of access tags to apply to the VSI resources created by the module."
default = []
}

variable "image_id" {
description = "Image ID used for VSI. Run 'ibmcloud is images' to find available images. Be aware that region is important for the image since the id's are different in each region."
type = string
default = "r014-2a39e91e-e899-4ac4-b309-57e107a7819f" # NOTE: this ID is for us-east region, Redhat 8.10 minimal
}

variable "machine_type" {
description = "VSI machine type"
type = string
default = "cx2-2x4"
}

variable "ssh_key" {
type = string
description = "An existing ssh key name to use for this example, if unset a new ssh key will be created"
default = null
}

variable "snapshot_consistency_group_id" {
type = string
description = "An existing Snapshot Consistency Group Id, used to automatically determine volume snapshots for deployment"
default = null
}
13 changes: 13 additions & 0 deletions examples/snapshot/version.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
terraform {
required_version = ">= 1.3.0, <1.7.0"
required_providers {
ibm = {
source = "IBM-Cloud/ibm"
version = "= 1.65.0"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0.4"
}
}
}
21 changes: 20 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ locals {
}
]
])

# 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)
}

# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478
Expand Down Expand Up @@ -89,10 +92,17 @@ resource "ibm_iam_authorization_policy" "block_storage_policy" {
description = "Allow block storage volumes to be encrypted by Key Management instance."
}

resource "ibm_is_subnet_reserved_ip" "vsi_ip" {
for_each = { for vsi_key, vsi_value in local.vsi_map : vsi_key => vsi_value if var.manage_reserved_ips }
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
image = var.image_id
image = (local.vsi_boot_volume_snapshot_id == null) ? var.image_id : null # image and snapshot are mutually exclusive
profile = var.machine_type
resource_group = var.resource_group_id
vpc = var.vpc_id
Expand All @@ -116,6 +126,12 @@ resource "ibm_is_instance" "vsi" {
(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
}
}
}

dynamic "network_interfaces" {
Expand Down Expand Up @@ -146,6 +162,9 @@ resource "ibm_is_instance" "vsi" {

boot_volume {
encryption = var.boot_volume_encryption_key
name = var.use_static_boot_volume_name ? "${each.value.vsi_name}-boot" : null
# determine snapshot in following order: input variable -> from consistency group -> null (none)
snapshot = local.vsi_boot_volume_snapshot_id
}

# Only add volumes if volumes are being created by the module
Expand Down
6 changes: 5 additions & 1 deletion modules/fscloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,26 @@ No resources.
|------|-------------|------|---------|:--------:|
| <a name="input_access_tags"></a> [access\_tags](#input\_access\_tags) | A list of access tags to apply to the VSI resources created by the module. For more information, see https://cloud.ibm.com/docs/account?topic=account-access-tags-tutorial. | `list(string)` | `[]` | no |
| <a name="input_allow_ip_spoofing"></a> [allow\_ip\_spoofing](#input\_allow\_ip\_spoofing) | Allow IP spoofing on the primary network interface | `bool` | `false` | no |
| <a name="input_block_storage_volumes"></a> [block\_storage\_volumes](#input\_block\_storage\_volumes) | List describing the block storage volumes that will be attached to each vsi | <pre>list(<br> object({<br> name = string<br> profile = string<br> capacity = optional(number)<br> iops = optional(number)<br> encryption_key = optional(string)<br> })<br> )</pre> | `[]` | no |
| <a name="input_block_storage_volumes"></a> [block\_storage\_volumes](#input\_block\_storage\_volumes) | List describing the block storage volumes that will be attached to each vsi | <pre>list(<br> object({<br> name = string<br> profile = string<br> capacity = optional(number)<br> iops = optional(number)<br> encryption_key = optional(string)<br> snapshot_id = optional(string) # set if you would like to base volume on a snapshot<br> })<br> )</pre> | `[]` | no |
| <a name="input_boot_volume_encryption_key"></a> [boot\_volume\_encryption\_key](#input\_boot\_volume\_encryption\_key) | CRN of boot volume encryption key | `string` | n/a | yes |
| <a name="input_boot_volume_snapshot_id"></a> [boot\_volume\_snapshot\_id](#input\_boot\_volume\_snapshot\_id) | The snapshot id of the volume to be used for creating boot volume attachment (if specified, the `image_id` parameter will not be used) | `string` | `null` | no |
| <a name="input_create_security_group"></a> [create\_security\_group](#input\_create\_security\_group) | Create security group for VSI. If this is passed as false, the default will be used | `bool` | n/a | yes |
| <a name="input_enable_floating_ip"></a> [enable\_floating\_ip](#input\_enable\_floating\_ip) | Create a floating IP for each virtual server created | `bool` | `false` | no |
| <a name="input_existing_kms_instance_guid"></a> [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services or Key Protect instance in which the key specified in var.kms\_key\_crn and var.backup\_encryption\_key\_crn is coming from. Required only if var.skip\_iam\_authorization\_policy is set to false. | `string` | `null` | no |
| <a name="input_image_id"></a> [image\_id](#input\_image\_id) | Image ID used for VSI. Run 'ibmcloud is images' to find available images in a region | `string` | n/a | yes |
| <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 = number<br> listener_protocol = string<br> connection_limit = 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> 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_prefix"></a> [prefix](#input\_prefix) | The prefix that you would like to append to your 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 |
| <a name="input_security_group"></a> [security\_group](#input\_security\_group) | Security group created for VSI | <pre>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> })</pre> | n/a | yes |
| <a name="input_security_group_ids"></a> [security\_group\_ids](#input\_security\_group\_ids) | IDs of additional security groups to be added to VSI deployment primary interface. A VSI interface can have a maximum of 5 security groups. | `list(string)` | `[]` | no |
| <a name="input_skip_iam_authorization_policy"></a> [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all Storage Blocks to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the existing\_kms\_instance\_guid variable. | `bool` | `false` | no |
| <a name="input_snapshot_consistency_group_id"></a> [snapshot\_consistency\_group\_id](#input\_snapshot\_consistency\_group\_id) | The snapshot consistency group Id. If supplied, the group will be queried for snapshots that are matched with both boot volume and attached (attached are matched based on name suffix). You can override specific snapshot Ids by setting the appropriate input variables as well. | `string` | `null` | no |
| <a name="input_ssh_key_ids"></a> [ssh\_key\_ids](#input\_ssh\_key\_ids) | ssh key ids to use in creating vsi | `list(string)` | n/a | yes |
| <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 = 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_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 |
| <a name="input_vsi_per_subnet"></a> [vsi\_per\_subnet](#input\_vsi\_per\_subnet) | Number of VSI instances for each subnet | `number` | n/a | yes |
Expand Down
4 changes: 4 additions & 0 deletions modules/fscloud/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ module "fscloud_vsi" {
skip_iam_authorization_policy = var.skip_iam_authorization_policy
boot_volume_encryption_key = var.boot_volume_encryption_key
kms_encryption_enabled = true
manage_reserved_ips = var.manage_reserved_ips
use_static_boot_volume_name = var.use_static_boot_volume_name
enable_floating_ip = var.enable_floating_ip
allow_ip_spoofing = var.allow_ip_spoofing
create_security_group = var.create_security_group
Expand All @@ -22,4 +24,6 @@ module "fscloud_vsi" {
block_storage_volumes = var.block_storage_volumes
load_balancers = var.load_balancers
access_tags = var.access_tags
snapshot_consistency_group_id = var.snapshot_consistency_group_id
boot_volume_snapshot_id = var.boot_volume_snapshot_id
}
Loading

0 comments on commit 666f518

Please sign in to comment.