From a054338274c3133f34ebab7456efc1e3a3673794 Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Tue, 30 Apr 2024 16:37:40 -0400 Subject: [PATCH 1/6] feat: add authorization policy for spoke -> hub DNS access --- README.md | 4 ++ examples/existing_vpc/variables.tf | 2 +- examples/hub-spoke-delegated-resolver/main.tf | 3 ++ examples/hub-spoke-manual-resolver/main.tf | 2 + main.tf | 37 +++++++++++++++++++ variables.tf | 12 ++++++ 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index badeb234..78d8f56b 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ To attach access management tags to resources in this module, you need the follo |------|------| | [ibm_dns_custom_resolver.custom_resolver_hub](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/dns_custom_resolver) | resource | | [ibm_iam_authorization_policy.policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | +| [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | | [ibm_is_flow_log.flow_logs](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_flow_log) | resource | | [ibm_is_network_acl.network_acl](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_network_acl) | resource | | [ibm_is_public_gateway.gateway](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_public_gateway) | resource | @@ -132,6 +133,7 @@ To attach access management tags to resources in this module, you need the follo | [ibm_is_vpc_routing_table_route.routing_table_routes](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_vpc_routing_table_route) | resource | | [ibm_resource_instance.dns_instance_hub](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_instance) | resource | | [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [ibm_iam_account_settings.iam_account_settings](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/iam_account_settings) | data source | | [ibm_is_subnet.subnet](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_subnet) | data source | | [ibm_is_vpc.vpc](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_vpc) | data source | | [ibm_is_vpc_address_prefixes.get_address_prefixes](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/is_vpc_address_prefixes) | data source | @@ -164,6 +166,7 @@ To attach access management tags to resources in this module, you need the follo | [existing\_storage\_bucket\_name](#input\_existing\_storage\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no | | [existing\_subnets](#input\_existing\_subnets) | The detail of the existing subnets and required mappings to other resources. Required if 'create\_subnets' is false. |
list(object({
id = string
public_gateway = optional(bool, false)
}))
| `[]` | no | | [existing\_vpc\_id](#input\_existing\_vpc\_id) | The ID of the existing vpc. Required if 'create\_vpc' is false. | `string` | `null` | no | +| [hub\_account\_id](#input\_hub\_account\_id) | ID of the hub account for DNS resolution, required if 'skip\_spoke\_auth\_policy' is false. | `string` | `null` | no | | [hub\_vpc\_crn](#input\_hub\_vpc\_crn) | Indicates the crn of the hub VPC for DNS resolution. See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model. Mutually exclusive with hub\_vpc\_id. | `string` | `null` | no | | [hub\_vpc\_id](#input\_hub\_vpc\_id) | Indicates the id of the hub VPC for DNS resolution. See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model. Mutually exclusive with hub\_vpc\_crn. | `string` | `null` | no | | [is\_flow\_log\_collector\_active](#input\_is\_flow\_log\_collector\_active) | Indicates whether the collector is active. If false, this collector is created in inactive mode. | `bool` | `true` | no | @@ -180,6 +183,7 @@ To attach access management tags to resources in this module, you need the follo | [routing\_table\_name](#input\_routing\_table\_name) | The name to give the provisioned routing tables. If not set, the module generates a name based on the `prefix` and `name` variables. | `string` | `null` | no | | [security\_group\_rules](#input\_security\_group\_rules) | A list of security group rules to be added to the default vpc security group (default empty) |
list(
object({
name = string
direction = string
remote = string
tcp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
udp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
icmp = optional(
object({
type = optional(number)
code = optional(number)
})
)
})
)
| `[]` | no | | [skip\_custom\_resolver\_hub\_creation](#input\_skip\_custom\_resolver\_hub\_creation) | Indicates whether to skip the configuration of a custom resolver in the hub VPC. Only relevant if enable\_hub is set to true. | `bool` | `false` | no | +| [skip\_spoke\_auth\_policy](#input\_skip\_spoke\_auth\_policy) | Set to true to skip the creation of an authorization policy between the DNS resolution spoke and hub, only enable this if a policy already exists between these two VPCs. See https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=ui for more details. | `bool` | `false` | no | | [subnets](#input\_subnets) | List of subnets for the vpc. For each item in each array, a subnet will be created. Items can be either CIDR blocks or total ipv4 addressess. Public gateways will be enabled only in zones where a gateway has been created |
object({
zone-1 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true
}))
zone-2 = optional(list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true
})))
zone-3 = optional(list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true
})))
})
|
{
"zone-1": [
{
"acl_name": "vpc-acl",
"cidr": "10.10.10.0/24",
"name": "subnet-a",
"no_addr_prefix": false,
"public_gateway": true
}
],
"zone-2": [
{
"acl_name": "vpc-acl",
"cidr": "10.20.10.0/24",
"name": "subnet-b",
"no_addr_prefix": false,
"public_gateway": true
}
],
"zone-3": [
{
"acl_name": "vpc-acl",
"cidr": "10.30.10.0/24",
"name": "subnet-c",
"no_addr_prefix": false,
"public_gateway": false
}
]
}
| no | | [tags](#input\_tags) | List of Tags for the resource created | `list(string)` | `null` | no | | [update\_delegated\_resolver](#input\_update\_delegated\_resolver) | If set to true, and if the vpc is configured to be a spoke for DNS resolution (enable\_hub\_vpc\_crn or enable\_hub\_vpc\_id set), then the spoke VPC resolver will be updated to a delegated resolver. | `bool` | `false` | no | diff --git a/examples/existing_vpc/variables.tf b/examples/existing_vpc/variables.tf index f2c5f7cd..2620ed5c 100644 --- a/examples/existing_vpc/variables.tf +++ b/examples/existing_vpc/variables.tf @@ -32,5 +32,5 @@ variable "existing_resource_group_name" { variable "name" { description = "The string is used as a prefix for the naming of VPC resources." type = string - default = null + default = "existing-vpc" } diff --git a/examples/hub-spoke-delegated-resolver/main.tf b/examples/hub-spoke-delegated-resolver/main.tf index 6bfbb16f..71fa0183 100644 --- a/examples/hub-spoke-delegated-resolver/main.tf +++ b/examples/hub-spoke-delegated-resolver/main.tf @@ -51,6 +51,8 @@ module "hub_vpc" { } +data "ibm_iam_account_settings" "iam_account_settings" {} + module "spoke_vpc" { source = "../../" resource_group_id = module.resource_group.resource_group_id @@ -58,6 +60,7 @@ module "spoke_vpc" { name = "spoke" prefix = "${var.prefix}-spoke" tags = var.resource_tags + hub_account_id = data.ibm_iam_account_settings.iam_account_settings.account_id hub_vpc_crn = module.hub_vpc.vpc_crn enable_hub_vpc_crn = true update_delegated_resolver = var.update_delegated_resolver diff --git a/examples/hub-spoke-manual-resolver/main.tf b/examples/hub-spoke-manual-resolver/main.tf index 0501aa61..a089e5f5 100644 --- a/examples/hub-spoke-manual-resolver/main.tf +++ b/examples/hub-spoke-manual-resolver/main.tf @@ -50,6 +50,7 @@ module "hub_vpc" { } } +data "ibm_iam_account_settings" "iam_account_settings" {} module "spoke_vpc" { source = "../../" @@ -58,6 +59,7 @@ module "spoke_vpc" { name = "spoke" prefix = "${var.prefix}-spoke" tags = var.resource_tags + hub_account_id = data.ibm_iam_account_settings.iam_account_settings.account_id hub_vpc_crn = module.hub_vpc.vpc_crn enable_hub_vpc_crn = true update_delegated_resolver = false diff --git a/main.tf b/main.tf index e534cab8..867cfbae 100644 --- a/main.tf +++ b/main.tf @@ -116,6 +116,43 @@ resource "ibm_is_vpc" "vpc" { # See https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model for context ############################################################################## +# fetch this account ID +data "ibm_iam_account_settings" "iam_account_settings" {} + +# spoke -> hub auth policy based on https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=terraform +resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { + count = (var.enable_hub == false && var.skip_spoke_auth_policy == false) ? 1 : 0 + roles = ["DNS Binding Connector"] + subject_attributes { + name = "accountId" + value = data.ibm_iam_account_settings.iam_account_settings.account_id + } + subject_attributes { + name = "serviceName" + value = "is" + } + subject_attributes { + name = "resourceType" + value = "vpc" + } + subject_attributes { + name = "resource" + value = local.vpc_id + } + resource_attributes { + name = "accountId" + value = var.hub_account_id + } + resource_attributes { + name = "serviceName" + value = "is" + } + resource_attributes { + name = "vpcId" + value = var.enable_hub_vpc_id ? var.hub_vpc_id : split(":", var.hub_vpc_crn)[7] + } +} + # Enable Hub to dns resolve in spoke VPC resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" { count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0 diff --git a/variables.tf b/variables.tf index 260c0649..ae46c2c3 100644 --- a/variables.tf +++ b/variables.tf @@ -542,6 +542,18 @@ variable "enable_hub" { default = false } +variable "skip_spoke_auth_policy" { + description = "Set to true to skip the creation of an authorization policy between the DNS resolution spoke and hub, only enable this if a policy already exists between these two VPCs. See https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=ui for more details." + type = bool + default = false +} + +variable "hub_account_id" { + description = "ID of the hub account for DNS resolution, required if 'skip_spoke_auth_policy' is false." + type = string + default = null +} + variable "enable_hub_vpc_id" { description = "Indicates whether Hub VPC ID is passed." type = bool From 45850402c5e121dd8b9bd22061cbb8d701b5a27e Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Wed, 1 May 2024 09:20:23 -0400 Subject: [PATCH 2/6] refactor: add conditional on hub vpc id or crn being present --- main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.tf b/main.tf index 867cfbae..acf93922 100644 --- a/main.tf +++ b/main.tf @@ -121,7 +121,7 @@ data "ibm_iam_account_settings" "iam_account_settings" {} # spoke -> hub auth policy based on https://cloud.ibm.com/docs/vpc?topic=vpc-vpe-dns-sharing-s2s-auth&interface=terraform resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { - count = (var.enable_hub == false && var.skip_spoke_auth_policy == false) ? 1 : 0 + count = (var.enable_hub == false && var.skip_spoke_auth_policy == false && (var.enable_hub_vpc_id || var.enable_hub_vpc_crn)) ? 1 : 0 roles = ["DNS Binding Connector"] subject_attributes { name = "accountId" From 32cec585304c8925b91a069a57d972517bee8718 Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Wed, 1 May 2024 11:14:30 -0400 Subject: [PATCH 3/6] refactor: correct indexing in crn for vpc id --- main.tf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.tf b/main.tf index acf93922..cf14af38 100644 --- a/main.tf +++ b/main.tf @@ -149,13 +149,14 @@ resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { } resource_attributes { name = "vpcId" - value = var.enable_hub_vpc_id ? var.hub_vpc_id : split(":", var.hub_vpc_crn)[7] + value = var.enable_hub_vpc_id ? var.hub_vpc_id : split(":", var.hub_vpc_crn)[9] } } # Enable Hub to dns resolve in spoke VPC resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" { - count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0 + count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0 + depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy] # Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination. name = coalesce( @@ -169,7 +170,8 @@ resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" { } resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_crn" { - count = (var.enable_hub == false && var.enable_hub_vpc_crn) ? 1 : 0 + count = (var.enable_hub == false && var.enable_hub_vpc_crn) ? 1 : 0 + depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy] # Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination. name = coalesce( From 2987d5b718fb5451145293b8defb0b91c6158265 Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Wed, 1 May 2024 11:16:31 -0400 Subject: [PATCH 4/6] refactor: add comment for depends_on --- main.tf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main.tf b/main.tf index cf14af38..16f5e1df 100644 --- a/main.tf +++ b/main.tf @@ -155,7 +155,8 @@ resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { # Enable Hub to dns resolve in spoke VPC resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" { - count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0 + count = (var.enable_hub == false && var.enable_hub_vpc_id) ? 1 : 0 + # Depends on required as the authorization policy cannot be directly referenced depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy] # Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination. @@ -170,7 +171,8 @@ resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_id" { } resource "ibm_is_vpc_dns_resolution_binding" "vpc_dns_resolution_binding_crn" { - count = (var.enable_hub == false && var.enable_hub_vpc_crn) ? 1 : 0 + count = (var.enable_hub == false && var.enable_hub_vpc_crn) ? 1 : 0 + # Depends on required as the authorization policy cannot be directly referenced depends_on = [ibm_iam_authorization_policy.vpc_dns_resolution_auth_policy] # Use var.dns_binding_name if not null, otherwise, use var.prefix and var.name combination. From 34c26dac834dac6b7c961bf48f00a652885d12e7 Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Thu, 2 May 2024 13:13:27 -0400 Subject: [PATCH 5/6] refactor: update based on PR feedback --- README.md | 2 +- examples/existing_vpc/variables.tf | 1 - examples/hub-spoke-delegated-resolver/README.md | 1 + main.tf | 5 +++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 78d8f56b..0c1c1ec1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This module creates the following IBM Cloud® Virtual Private Cloud (VPC) net - Network ACLs: Create network ACLs with multiple rules. By default, VPC network ACLs can have no more than 25 rules. - VPN gateways: Create VPN gateways on your subnets by using the `vpn_gateways` variable. For more information about VPN gateways on VPC, see [About site-to-site VPN gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-using-vpn) in the IBM Cloud docs. - VPN gateway connections: Add connections to a VPN gateway. -- Hub and spoke DNS-sharing model: Optionally create a hub or spoke VPC, with associated custom resolver and DNS resolution binding. See [About DNS sharing for VPE gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model) in the IBM Cloud docs for details. +- Hub and spoke DNS-sharing model: Optionally create a hub or spoke VPC, with associated custom resolver and DNS resolution binding, as well as a service-to-service authorization policy which supports the hub and spoke VPCs to be in separate accounts. See [About DNS sharing for VPE gateways](https://cloud.ibm.com/docs/vpc?topic=vpc-hub-spoke-model) in the IBM Cloud docs for details. ![vpc-module](https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/main/.docs/vpc-module.png) diff --git a/examples/existing_vpc/variables.tf b/examples/existing_vpc/variables.tf index 2620ed5c..4e46e0a4 100644 --- a/examples/existing_vpc/variables.tf +++ b/examples/existing_vpc/variables.tf @@ -32,5 +32,4 @@ variable "existing_resource_group_name" { variable "name" { description = "The string is used as a prefix for the naming of VPC resources." type = string - default = "existing-vpc" } diff --git a/examples/hub-spoke-delegated-resolver/README.md b/examples/hub-spoke-delegated-resolver/README.md index 17cbb14b..3e4551b3 100644 --- a/examples/hub-spoke-delegated-resolver/README.md +++ b/examples/hub-spoke-delegated-resolver/README.md @@ -4,6 +4,7 @@ This example demonstrates how to deploy hub and spoke VPCs, inclusive of enablin - The 2 VPCs are connected through a transit gateway. - The hub VPC is configured with a custom resolver. - The spoke VPC is configured with a delegated DNS resolver. DNS requests are resolved by the hub VPC. +- An authorization policy for the DNS Binding Connector role is created to allow the spoke VPC to use the DNS resolution of the hub VPC, this also allows the hub and spoke VPCs to be in separate accounts. - A DNS resolution binding relationship is configured to enable the hub VPC to DNS resolve VPE in the spoke VPC. diff --git a/main.tf b/main.tf index 16f5e1df..c984aa0e 100644 --- a/main.tf +++ b/main.tf @@ -37,6 +37,9 @@ locals { # tflint-ignore: terraform_unused_declarations validate_vpc_flow_logs_inputs = (var.enable_vpc_flow_logs) ? ((var.create_authorization_policy_vpc_to_cos) ? ((var.existing_cos_instance_guid != null && var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS instance & bucket name to create flow logs collector.")) : ((var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS bucket name to create flow logs collector"))) : false + + # tflint-ignore: terraform_unused_declarations + validate_skip_spoke_auth_policy_input = (!var.skip_spoke_auth_policy && var.hub_account_id == null) ? tobool("var.hub_account_id must be set when var.skip_spoke_auth_policy is False.") : true } ############################################################################## @@ -123,6 +126,7 @@ data "ibm_iam_account_settings" "iam_account_settings" {} resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { count = (var.enable_hub == false && var.skip_spoke_auth_policy == false && (var.enable_hub_vpc_id || var.enable_hub_vpc_crn)) ? 1 : 0 roles = ["DNS Binding Connector"] + # subject is the spoke subject_attributes { name = "accountId" value = data.ibm_iam_account_settings.iam_account_settings.account_id @@ -139,6 +143,7 @@ resource "ibm_iam_authorization_policy" "vpc_dns_resolution_auth_policy" { name = "resource" value = local.vpc_id } + # resource is the hub resource_attributes { name = "accountId" value = var.hub_account_id From b60104e4fc0fc2904323a774bcb303604f61127c Mon Sep 17 00:00:00 2001 From: "Matthew.Lemmond@ibm.com" Date: Thu, 2 May 2024 16:53:00 -0400 Subject: [PATCH 6/6] refactor: adjust validation for auth policy --- main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.tf b/main.tf index c984aa0e..dcac73f1 100644 --- a/main.tf +++ b/main.tf @@ -39,7 +39,7 @@ locals { validate_vpc_flow_logs_inputs = (var.enable_vpc_flow_logs) ? ((var.create_authorization_policy_vpc_to_cos) ? ((var.existing_cos_instance_guid != null && var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS instance & bucket name to create flow logs collector.")) : ((var.existing_storage_bucket_name != null) ? true : tobool("Please provide COS bucket name to create flow logs collector"))) : false # tflint-ignore: terraform_unused_declarations - validate_skip_spoke_auth_policy_input = (!var.skip_spoke_auth_policy && var.hub_account_id == null) ? tobool("var.hub_account_id must be set when var.skip_spoke_auth_policy is False.") : true + validate_skip_spoke_auth_policy_input = (var.hub_account_id == null && !var.skip_spoke_auth_policy && !var.enable_hub && (var.enable_hub_vpc_id || var.enable_hub_vpc_crn)) ? tobool("var.hub_account_id must be set when var.skip_spoke_auth_policy is False and either var.enable_hub_vpc_id or var.enable_hub_vpc_crn is true.") : true } ##############################################################################