diff --git a/README.md b/README.md index 767afaae..2a0fb3c2 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Expected network connectivity downtime of typically around 20 seconds. * [workload-vpc](./modules/workload-vpc) * [Examples](./examples) * [Basic Example](./examples/basic) + * [Custom Security Group Example](./examples/custom_security_group) * [Default Example](./examples/default) * [Existing VPC and subnets Example](./examples/existing_vpc) * [Hub and Spoke VPC Example](./examples/hub-spoke-delegated-resolver) @@ -241,7 +242,7 @@ To attach access management tags to resources in this module, you need the follo | [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the VPC to be created | `string` | n/a | yes | | [routes](#input\_routes) | OPTIONAL - Allows you to specify the next hop for packets based on their destination address |
list(
object({
name = string
route_direct_link_ingress = optional(bool)
route_transit_gateway_ingress = optional(bool)
route_vpc_zone_ingress = optional(bool)
routes = optional(
list(
object({
action = optional(string)
zone = number
destination = string
next_hop = string
})
))
})
)
| `[]` | no | | [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 = optional(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 | +| [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 = optional(string)
local = optional(string)
ip_version = optional(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 addresses. 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
subnet_tags = optional(list(string), [])
}))
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
subnet_tags = optional(list(string), [])
})))
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
subnet_tags = optional(list(string), [])
})))
})
|
{
"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 | diff --git a/default_security_group.tf b/default_security_group.tf index 013e085b..3c2f95c4 100644 --- a/default_security_group.tf +++ b/default_security_group.tf @@ -11,10 +11,12 @@ locals { } resource "ibm_is_security_group_rule" "default_vpc_rule" { - for_each = local.security_group_rule_object - group = var.create_vpc == true ? ibm_is_vpc.vpc[0].default_security_group : data.ibm_is_vpc.vpc.default_security_group - direction = each.value.direction - remote = each.value.remote + for_each = local.security_group_rule_object + group = var.create_vpc == true ? ibm_is_vpc.vpc[0].default_security_group : data.ibm_is_vpc.vpc.default_security_group + direction = each.value.direction + remote = each.value.remote + local = each.value.local + ip_version = each.value.ip_version dynamic "tcp" { for_each = each.value.tcp == null ? [] : [each.value] diff --git a/examples/custom_security_group/README.md b/examples/custom_security_group/README.md new file mode 100644 index 00000000..6cf07383 --- /dev/null +++ b/examples/custom_security_group/README.md @@ -0,0 +1,8 @@ +# Custom Security Group Example + +A simple example to provision a Secure Landing Zone (SLZ) Virtual Private Cloud (VPC) with Security Group Rules set. + +The following resources are provisioned by this example: + +* A new resource group, if an existing one is not passed in. +* An IBM Virtual Private Cloud (VPC) with custom security group rules. diff --git a/examples/custom_security_group/main.tf b/examples/custom_security_group/main.tf new file mode 100644 index 00000000..9c59ef61 --- /dev/null +++ b/examples/custom_security_group/main.tf @@ -0,0 +1,33 @@ + +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.3.0" + # 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 VPC +# (if var.vpc_id is null, create a new VPC) +############################################################################## + +module "vpc" { + source = "../.." + resource_group_id = module.resource_group.resource_group_id + region = var.region + prefix = var.prefix + name = "sg-vpc" + tags = var.resource_tags + security_group_rules = [{ + name = "allow-all-inbound-sg" + direction = "inbound" + remote = "0.0.0.0/0" # source of the traffic. 0.0.0.0/0 traffic from all across the internet. + local = "0.0.0.0/0" # A CIDR block of 0.0.0.0/0 allows traffic to all local IP addresses (or from all local IP addresses, for outbound rules). + ip_version = "ipv4" + }] +} diff --git a/examples/custom_security_group/outputs.tf b/examples/custom_security_group/outputs.tf new file mode 100644 index 00000000..82f4dad6 --- /dev/null +++ b/examples/custom_security_group/outputs.tf @@ -0,0 +1,13 @@ +############################################################################## +# Outputs +############################################################################## + +output "vpc_id" { + value = module.vpc.vpc_id + description = "VPC id" +} + +output "vpc_crn" { + value = module.vpc.vpc_crn + description = "VPC crn" +} diff --git a/examples/custom_security_group/provider.tf b/examples/custom_security_group/provider.tf new file mode 100644 index 00000000..df45ef50 --- /dev/null +++ b/examples/custom_security_group/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/examples/custom_security_group/variables.tf b/examples/custom_security_group/variables.tf new file mode 100644 index 00000000..bba380d7 --- /dev/null +++ b/examples/custom_security_group/variables.tf @@ -0,0 +1,27 @@ +variable "ibmcloud_api_key" { + description = "APIkey that's associated with the account to provision resources to" + type = string + sensitive = true +} + +variable "region" { + description = "The region to which to deploy the VPC" + type = string +} + +variable "prefix" { + description = "The prefix that you would like to append to your resources" + type = string +} + +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 "resource_tags" { + description = "List of Tags for the resource created" + type = list(string) + default = null +} diff --git a/examples/custom_security_group/version.tf b/examples/custom_security_group/version.tf new file mode 100644 index 00000000..f5d62ad6 --- /dev/null +++ b/examples/custom_security_group/version.tf @@ -0,0 +1,11 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main + # module's version.tf (basic), and 1 example that will always use the latest provider version. + ibm = { + source = "IBM-Cloud/ibm" + version = ">=1.59.0" + } + } +} diff --git a/modules/management-vpc/README.md b/modules/management-vpc/README.md index 5f6469d7..75a0cf41 100644 --- a/modules/management-vpc/README.md +++ b/modules/management-vpc/README.md @@ -45,7 +45,7 @@ No resources. | [default\_network\_acl\_name](#input\_default\_network\_acl\_name) | Override default ACL name | `string` | `null` | no | | [default\_routing\_table\_name](#input\_default\_routing\_table\_name) | Override default VPC routing table name | `string` | `null` | no | | [default\_security\_group\_name](#input\_default\_security\_group\_name) | Override default VPC security group name | `string` | `null` | no | -| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
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 | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
list(
object({
name = string
direction = string
remote = optional(string)
local = optional(string)
ip_version = optional(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 | | [enable\_vpc\_flow\_logs](#input\_enable\_vpc\_flow\_logs) | Enable VPC Flow Logs, it will create Flow logs collector if set to true | `bool` | `false` | no | | [existing\_cos\_bucket\_name](#input\_existing\_cos\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no | | [existing\_cos\_instance\_guid](#input\_existing\_cos\_instance\_guid) | GUID of the COS instance to create Flow log collector | `string` | `null` | no | diff --git a/modules/management-vpc/variables.tf b/modules/management-vpc/variables.tf index 8ceacb0d..e62207fb 100644 --- a/modules/management-vpc/variables.tf +++ b/modules/management-vpc/variables.tf @@ -59,9 +59,11 @@ variable "default_security_group_rules" { description = "Override default security group rules" type = list( object({ - name = string - direction = string - remote = string + name = string + direction = string + remote = optional(string) + local = optional(string) + ip_version = optional(string) tcp = optional( object({ port_max = optional(number) diff --git a/modules/workload-vpc/README.md b/modules/workload-vpc/README.md index 80462728..80b8e6b3 100644 --- a/modules/workload-vpc/README.md +++ b/modules/workload-vpc/README.md @@ -45,7 +45,7 @@ No resources. | [default\_network\_acl\_name](#input\_default\_network\_acl\_name) | Override default ACL name | `string` | `null` | no | | [default\_routing\_table\_name](#input\_default\_routing\_table\_name) | Override default VPC routing table name | `string` | `null` | no | | [default\_security\_group\_name](#input\_default\_security\_group\_name) | Override default VPC security group name | `string` | `null` | no | -| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
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 | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
list(
object({
name = string
direction = string
remote = optional(string)
local = optional(string)
ip_version = optional(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 | | [enable\_vpc\_flow\_logs](#input\_enable\_vpc\_flow\_logs) | Enable VPC Flow Logs, it will create Flow logs collector if set to true | `bool` | `false` | no | | [existing\_cos\_bucket\_name](#input\_existing\_cos\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no | | [existing\_cos\_instance\_guid](#input\_existing\_cos\_instance\_guid) | GUID of the COS instance to create Flow log collector | `string` | `null` | no | diff --git a/modules/workload-vpc/variables.tf b/modules/workload-vpc/variables.tf index 914b9c38..093933ff 100644 --- a/modules/workload-vpc/variables.tf +++ b/modules/workload-vpc/variables.tf @@ -59,9 +59,11 @@ variable "default_security_group_rules" { description = "Override default security group rules" type = list( object({ - name = string - direction = string - remote = string + name = string + direction = string + remote = optional(string) + local = optional(string) + ip_version = optional(string) tcp = optional( object({ port_max = optional(number) diff --git a/solutions/fully-configurable/DA-types.md b/solutions/fully-configurable/DA-types.md index 4f0dc3d1..2edafdfd 100644 --- a/solutions/fully-configurable/DA-types.md +++ b/solutions/fully-configurable/DA-types.md @@ -131,6 +131,8 @@ This variable configuration allows you to specify the list of security group rul - `name` (required): The name of the security group rule. - `direction` (required): The direction of the traffic. Allowed values are `inbound` or `outbound`. - `remote` (optional): Security group ID or an IP address or a CIDR block. +- `local` (optional): The local IP address or range of local IP addresses to which this rule will allow inbound traffic (or from which, for outbound traffic). +- `ip_version` (optional): The IP version to enforce. - `tcp` (optional): - `port_min` - `port_max` diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf index f86da000..6bebc067 100644 --- a/solutions/fully-configurable/variables.tf +++ b/solutions/fully-configurable/variables.tf @@ -327,9 +327,11 @@ variable "security_group_rules" { default = [] type = list( object({ - name = string - direction = string - remote = optional(string) + name = string + direction = string + remote = optional(string) + local = optional(string) + ip_version = optional(string) tcp = optional( object({ port_max = optional(number) diff --git a/tests/other_test.go b/tests/other_test.go index 058aae85..7351cdb8 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -38,3 +38,18 @@ func TestRunSpecificZoneExample(t *testing.T) { assert.Nil(t, err, "This should not have errored") assert.NotNil(t, output, "Expected some output") } + +func TestRunCustomSecurityGroupExample(t *testing.T) { + t.Parallel() + + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: customSecurityGroupExampleTerraformDir, + Prefix: "sg-slz", + ResourceGroup: resourceGroup, + }) + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} diff --git a/tests/pr_test.go b/tests/pr_test.go index b3a08359..16559c12 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -22,6 +22,7 @@ import ( ) const basicExampleTerraformDir = "examples/basic" +const customSecurityGroupExampleTerraformDir = "examples/custom_security_group" const defaultExampleTerraformDir = "examples/default" const landingZoneExampleTerraformDir = "examples/landing_zone" const hubAndSpokeDelegatedExampleTerraformDir = "examples/hub-spoke-delegated-resolver" diff --git a/variables.tf b/variables.tf index 2937beb3..c10953ed 100644 --- a/variables.tf +++ b/variables.tf @@ -425,9 +425,11 @@ variable "security_group_rules" { default = [] type = list( object({ - name = string - direction = string - remote = optional(string) + name = string + direction = string + remote = optional(string) + local = optional(string) + ip_version = optional(string) tcp = optional( object({ port_max = optional(number)