Skip to content

Commit

Permalink
More accurate control of create before destroy behaviors (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nuru authored Jul 7, 2022
1 parent cad3268 commit a7ff89b
Show file tree
Hide file tree
Showing 13 changed files with 644 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
":preserveSemverRanges"
],
"labels": ["auto-update"],
"dependencyDashboardAutoclose": true,
"enabledManagers": ["terraform"],
"terraform": {
"ignorePaths": ["**/context.tf", "examples/**"]
}
}

232 changes: 212 additions & 20 deletions README.md

Large diffs are not rendered by default.

218 changes: 201 additions & 17 deletions README.yaml

Large diffs are not rendered by default.

14 changes: 11 additions & 3 deletions docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.14.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 3.0 |
| <a name="requirement_null"></a> [null](#requirement\_null) | >= 3.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 3.0 |
| <a name="provider_null"></a> [null](#provider\_null) | >= 3.0 |
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.0 |

## Modules

Expand All @@ -24,17 +28,20 @@
|------|------|
| [aws_security_group.cbd](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.dbc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.keyed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [null_resource.sync_rules_and_sg_lifecycles](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
| [random_id.rule_change_forces_new_security_group](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_additional_tag_map"></a> [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.<br>This is for some rare cases where resources want additional configuration of tags<br>and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no |
| <a name="input_allow_all_egress"></a> [allow\_all\_egress](#input\_allow\_all\_egress) | A convenience that adds to the rules specified elsewhere a rule that allows all egress.<br>If this is false and no egress rules are specified via `rules` or `rule-matrix`, then no egress will be allowed. | `bool` | `false` | no |
| <a name="input_allow_all_egress"></a> [allow\_all\_egress](#input\_allow\_all\_egress) | A convenience that adds to the rules specified elsewhere a rule that allows all egress.<br>If this is false and no egress rules are specified via `rules` or `rule-matrix`, then no egress will be allowed. | `bool` | `true` | no |
| <a name="input_attributes"></a> [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,<br>in the order they appear in the list. New attributes are appended to the<br>end of the list. The elements of the list are joined by the `delimiter`<br>and treated as a single ID element. | `list(string)` | `[]` | no |
| <a name="input_context"></a> [context](#input\_context) | Single object for setting entire context at once.<br>See description of individual variables for details.<br>Leave string and numeric variables as `null` to use default value.<br>Individual variable settings (non-null) override settings in context object,<br>except for attributes, tags, and additional\_tag\_map, which are merged. | `any` | <pre>{<br> "additional_tag_map": {},<br> "attributes": [],<br> "delimiter": null,<br> "descriptor_formats": {},<br> "enabled": true,<br> "environment": null,<br> "id_length_limit": null,<br> "label_key_case": null,<br> "label_order": [],<br> "label_value_case": null,<br> "labels_as_tags": [<br> "unset"<br> ],<br> "name": null,<br> "namespace": null,<br> "regex_replace_chars": null,<br> "stage": null,<br> "tags": {},<br> "tenant": null<br>}</pre> | no |
| <a name="input_create_before_destroy"></a> [create\_before\_destroy](#input\_create\_before\_destroy) | Set `true` to enable terraform `create_before_destroy` behavior on the created security group.<br>We recommend setting this `true` on new security groups, but default it to `false` because `true`<br>will cause existing security groups to be replaced.<br>Note that changing this value will always cause the security group to be replaced. | `bool` | `false` | no |
| <a name="input_create_before_destroy"></a> [create\_before\_destroy](#input\_create\_before\_destroy) | Set `true` to enable terraform `create_before_destroy` behavior on the created security group.<br>We only recommend setting this `false` if you are importing an existing security group<br>that you do not want replaced and therefore need full control over its name.<br>Note that changing this value will always cause the security group to be replaced. | `bool` | `true` | no |
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_descriptor_formats"></a> [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.<br>Map of maps. Keys are names of descriptors. Values are maps of the form<br>`{<br> format = string<br> labels = list(string)<br>}`<br>(Type is `any` so the map values can later be enhanced to provide additional options.)<br>`format` is a Terraform format string to be passed to the `format()` function.<br>`labels` is a list of labels, in order, to pass to `format()` function.<br>Label values will be normalized before being passed to `format()` so they will be<br>identical to how they appear in `id`.<br>Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
Expand All @@ -47,6 +54,7 @@
| <a name="input_labels_as_tags"></a> [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.<br>Default is to include all labels.<br>Tags with empty values will not be included in the `tags` output.<br>Set to `[]` to suppress all generated tags.<br>**Notes:**<br> The value of the `name` tag, if included, will be the `id`, not the `name`.<br> Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be<br> changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` | <pre>[<br> "default"<br>]</pre> | no |
| <a name="input_name"></a> [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.<br>This is the only ID element not also included as a `tag`.<br>The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no |
| <a name="input_namespace"></a> [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no |
| <a name="input_preserve_security_group_id"></a> [preserve\_security\_group\_id](#input\_preserve\_security\_group\_id) | When `false` and `security_group_create_before_destroy` is `true`, changes to security group rules<br>cause a new security group to be created with the new rules, and the existing security group is then<br>replaced with the new one, eliminating any service interruption.<br>When `true` or when changing the value (from `false` to `true` or from `true` to `false`),<br>existing security group rules will be deleted before new ones are created, resulting in a service interruption,<br>but preserving the security group itself.<br>**NOTE:** Setting this to `true` does not guarantee the security group will never be replaced,<br>it only keeps changes to the security group rules from triggering a replacement.<br>See the README for further discussion. | `bool` | `false` | no |
| <a name="input_regex_replace_chars"></a> [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.<br>Characters matching the regex will be removed from the ID elements.<br>If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no |
| <a name="input_revoke_rules_on_delete"></a> [revoke\_rules\_on\_delete](#input\_revoke\_rules\_on\_delete) | Instruct Terraform to revoke all of the Security Group's attached ingress and egress rules before deleting<br>the security group itself. This is normally not needed. | `bool` | `false` | no |
| <a name="input_rule_matrix"></a> [rule\_matrix](#input\_rule\_matrix) | A convenient way to apply the same set of rules to a set of subjects. See README for details. | `any` | `[]` | no |
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ module "new_security_group" {
security_group_create_timeout = "5m"
security_group_delete_timeout = "2m"

security_group_name = [format("%s-%s", module.this.id, "new")]
security_group_name = [format("%s-%s", module.this.id, "new-")]

context = module.this.context
}
Expand Down
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.14.0"
required_version = ">= 1.0.0"

required_providers {
aws = {
Expand Down
89 changes: 38 additions & 51 deletions exports/security-group-variables.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# security-group-variables Version: 2
# security-group-variables Version: 3
#
# Copy this file from https://github.com/cloudposse/terraform-aws-security-group/blob/master/exports/security-group-variables.tf
# and EDIT IT TO SUIT YOUR PROJECT. Update the version number above if you update this file from a later version.
Expand All @@ -21,17 +21,17 @@

variable "create_security_group" {
type = bool
default = true
description = "Set `true` to create and configure a new security group. If false, `associated_security_group_ids` must be provided."
default = true
}

variable "associated_security_group_ids" {
type = list(string)
default = []
description = <<-EOT
A list of IDs of Security Groups to associate the created resource with, in addition to the created security group.
These security groups will not be modified and, if `create_security_group` is `false`, must have rules providing the desired access.
EOT
default = []
}

##
Expand All @@ -47,127 +47,113 @@ variable "associated_security_group_ids" {
## - likely to invite count/for_each issues
variable "allowed_security_group_ids" {
type = list(string)
default = []
description = <<-EOT
A list of IDs of Security Groups to allow access to the security group created by this module.
The length of this list must be known at "plan" time.
EOT
default = []
}

variable "allowed_cidr_blocks" {
type = list(string)
default = []
description = <<-EOT
A list of IPv4 CIDRs to allow access to the security group created by this module.
The length of this list must be known at "plan" time.
EOT
default = []
}

variable "allowed_ipv6_cidr_blocks" {
type = list(string)
default = []
description = <<-EOT
A list of IPv6 CIDRs to allow access to the security group created by this module.
The length of this list must be known at "plan" time.
EOT
default = []
}

variable "allowed_ipv6_prefix_list_ids" {
type = list(string)
default = []
description = <<-EOT
A list of IPv6 Prefix Lists IDs to allow access to the security group created by this module.
The length of this list must be known at "plan" time.
EOT
default = []
}
## End of optional allowed_* ###########

variable "security_group_name" {
type = list(string)
default = []
description = <<-EOT
The name to assign to the created security group. Must be unique within the VPC.
If not provided, will be derived from the `null-label.context` passed in.
If `create_before_destroy` is true, will be used as a name prefix.
EOT
default = []
}

variable "security_group_description" {
type = string
default = "Managed by Terraform"
description = <<-EOT
The description to assign to the created Security Group.
Warning: Changing the description causes the security group to be replaced.
EOT
default = "Managed by Terraform"
}

###############################
#
# Decide on a case-by-case basis what the default should be.
# In general, if the resource supports changing security groups without deleting
# the resource or anything it depends on, then default it to `true` and
# note in the release notes and migration documents the option to
# set it to `false` to preserve the existing security group.
# If the resource has to be deleted to change its security group,
# then set the default to `false` and highlight the option to change
# it to `true` in the release notes and migration documents.
#
################################
variable "security_group_create_before_destroy" {
type = bool
#
# Pick `true` or `false` and the associated description.
# Use `true` unless replacing the security group causes the underlying resource
# (e.g. the EC2 instance, not just the security group) to be destroyed and recreated.
#
# Replace "the resource" in the description with the name of the resource, e.g. "EC2 instance".
#

# default = true
# description = <<-EOT
# Set `true` to enable Terraform `create_before_destroy` behavior on the created security group.
# We only recommend setting this `false` if you are upgrading this module and need to keep
# the existing security group from being replaced.
# Note that changing this value will always cause the security group to be replaced.
# EOT

# default = false
# description = <<-EOT
# Set `true` to enable Terraform `create_before_destroy` behavior on the created security group.
# We recommend setting this `true` on new security groups, but default it to `false` because `true`
# will cause existing security groups to be replaced, possibly requiring the resource to be deleted and recreated.
# Note that changing this value will always cause the security group to be replaced.
# EOT
type = bool
description = <<-EOT
Set `true` to enable terraform `create_before_destroy` behavior on the created security group.
We only recommend setting this `false` if you are importing an existing security group
that you do not want replaced and therefore need full control over its name.
Note that changing this value will always cause the security group to be replaced.
EOT
default = true
}

variable "preserve_security_group_id" {
type = bool
description = <<-EOT
When `false` and `security_group_create_before_destroy` is `true`, changes to security group rules
cause a new security group to be created with the new rules, and the existing security group is then
replaced with the new one, eliminating any service interruption.
When `true` or when changing the value (from `false` to `true` or from `true` to `false`),
existing security group rules will be deleted before new ones are created, resulting in a service interruption,
but preserving the security group itself.
**NOTE:** Setting this to `true` does not guarantee the security group will never be replaced,
it only keeps changes to the security group rules from triggering a replacement.
See the [terraform-aws-security-group README](https://github.com/cloudposse/terraform-aws-security-group) for further discussion.
EOT
default = false
}

variable "security_group_create_timeout" {
type = string
default = "10m"
description = "How long to wait for the security group to be created."
default = "10m"
}

variable "security_group_delete_timeout" {
type = string
default = "15m"
description = <<-EOT
How long to retry on `DependencyViolation` errors during security group deletion from
lingering ENIs left by certain AWS services such as Elastic Load Balancing.
EOT
default = "15m"
}

variable "allow_all_egress" {
type = bool
default = true
description = <<-EOT
If `true`, the created security group will allow egress on all ports and protocols to all IP addresses.
If this is false and no egress rules are otherwise specified, then no egress will be allowed.
EOT
default = true
}

variable "additional_security_group_rules" {
type = list(any)
default = []
description = <<-EOT
A list of Security Group rule objects to add to the created security group, in addition to the ones
this module normally creates. (To suppress the module's rules, set `create_security_group` to false
Expand All @@ -177,6 +163,7 @@ variable "additional_security_group_rules" {
For more info see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule
and https://github.com/cloudposse/terraform-aws-security-group.
EOT
default = []
}

#### We do not expose an `additional_security_group_rule_matrix` input for a few reasons:
Expand Down Expand Up @@ -214,21 +201,21 @@ variable "vpc_id" {

variable "inline_rules_enabled" {
type = bool
default = false
description = <<-EOT
NOT RECOMMENDED. Create rules "inline" instead of as separate `aws_security_group_rule` resources.
See [#20046](https://github.com/hashicorp/terraform-provider-aws/issues/20046) for one of several issues with inline rules.
See [this post](https://github.com/hashicorp/terraform-provider-aws/pull/9032#issuecomment-639545250) for details on the difference between inline rules and rule resources.
EOT
default = false
}

variable "revoke_security_group_rules_on_delete" {
type = bool
default = false
description = <<-EOT
Instruct Terraform to revoke all of the Security Group's attached ingress and egress rules before deleting
the security group itself. This is normally not needed.
EOT
default = false
}


Expand Down
Loading

0 comments on commit a7ff89b

Please sign in to comment.