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(| `[]` | 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) |
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
})
))
})
)
list(| `[]` | 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) |
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)
})
)
})
)
list(| `[]` | 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({
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)
})
)
})
)
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(| `[]` | no | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
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)
})
)
})
)
list(| `[]` | 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 |
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)
})
)
})
)
list(| `[]` | no | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
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)
})
)
})
)
list(| `[]` | 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)
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)
})
)
})
)