Library / SDK
terraform-aws-modules/terraform-aws-security-group avatar
terraform-aws-modules/terraform-aws-security-group

The AWS security group module that owns the whole rule set

Terraform module to create AWS Security Group resources 🇺🇦

591 stars1,141 forksHCLNOASSERTION

At a glance

What is it?
terraform-aws-security-group creates an EC2 security group in a VPC and manages its rules as separate resources, so rules can be added without replacing the group. It also enforces exclusivity, meaning any rule added by hand in the console is reverted on the next apply, which is the module's most consequential default.
Who is it for?
Adopt terraform-aws-security-group when you want security group rules to live in code, be able to add a rule without replacing the group, and have out-of-band changes reverted. Skip it if your team manages security groups by hand in the console, because the exclusive rules default will fight that.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 78 days ago.
What is it written in?
Mainly HCL, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Rules as separate resources, not inline blocks

The module creates an EC2 security group within a VPC, and the design decision that matters is how it handles rules. The resource list makes it clear: alongside `aws_security_group.this` there are separate `aws_vpc_security_group_ingress_rule.this` and `aws_vpc_security_group_egress_rule.this` resources, plus `aws_vpc_security_group_rules_exclusive.this` and `aws_vpc_security_group_vpc_association.this`. Rules are therefore first-class objects with their own addresses in the state, rather than attributes of the group. That is what makes the common operation of adding one port to an existing group non-destructive, and it is the practical reason to reach for a module rather than writing the `aws_security_group` resource by hand. The usage example takes the shape of a map of rules, keyed by a readable name such as `https` or `self-all`, with each entry carrying ports, protocol, CIDR, description and optional tags. The `create` input controls whether the resources are created at all, and defaults to `true`.

The exclusive rules default will revert manual changes

The most consequential input in the module is `enable_exclusive_rules`, and it defaults to `true`. The documented behaviour is that when it is on, the module enforces that only the rules it declares exist on the security group, and out-of-band rules added through the AWS console or by other Terraform configurations are reverted on the next apply. That is a genuine safety property, because a security group is a security boundary and a rule added by hand is a rule nobody reviewed. It is also a behaviour that will surprise someone who expects the console and the code to coexist. The other inputs in the same family are more conventional. `revoke_rules_on_delete` tells Terraform to revoke all attached ingress and egress rules before deleting a rule, defaulting to `false`. `region` defaults to the region set in the provider configuration. `name` defaults to an empty string and `description` to `null`, both optional.

Referencing the group from itself with a sentinel

Allowing traffic between members of the same security group needs a self reference, and the module handles it with a sentinel string rather than leaving the id to be resolved by hand. Setting `referenced_security_group_id = "self"` on a rule causes the sentinel to be rewritten to the security group's own id at apply time. The usage example shows the pattern inside the ingress rules map, where the `self-all` rule uses `ip_protocol` of `-1` to allow all traffic from members of the group. The reason the sentinel exists is ordering: the group's id is only known after creation, and a module that took a literal id would force every consumer into a two-step apply. A side effect worth knowing is that a self reference ties the group's lifecycle to itself, so a replacement of the group is harder to sequence than a replacement of an ordinary rule.

Calling the module and picking a preset submodule

Using it as a root module means giving it a source, a name, a description and a VPC id, then describing the ingress and egress rules as maps.

hcl
module "security_group" {
  source = "terraform-aws-modules/security-group/aws"

  name        = "example"
  description = "Example security group"
  vpc_id      = "vpc-12345678"

  ingress_rules = {
    https = {
      from_port   = 443
      ip_protocol = "tcp"
      cidr_ipv4   = "10.0.0.0/16"
      description = "HTTPS from internal"
    }
  }

  tags = {
    Environment = "dev"
  }
}

For a group that serves one service, the preset submodule takes a simpler shape, with named CIDR blocks rather than full rule objects.

hcl
module "postgresql_security_group" {
  source = "terraform-aws-modules/security-group/aws//modules/postgresql"

  name        = "postgresql"
  description = "PostgreSQL access"
  vpc_id      = "vpc-12345678"

  ingress_cidr_ipv4 = {
    vpc  = "10.0.0.0/16"
    peer = "172.16.0.0/12"
  }
}

The self-reference sentinel appears in the same rule map shape, so adding a rule that lets the group's own members talk to each other is a one-line change rather than a separate resource.

hcl
ingress_rules = {
  self-all = {
    ip_protocol                  = "-1"
    referenced_security_group_id = "self"
    description                  = "All traffic from members of this SG"
  }
}

The repository also carries a `complete` example, described as a comprehensive example demonstrating the full module surface, which is the one to read if you want to see every input in context rather than the three shown here.

use_name_prefix decides whether a replacement can succeed

The lifecycle note is the part of this module most likely to bite you. The security group resource sets `lifecycle { create_before_destroy = true }` so that replacements happen without dropping traffic. When `use_name_prefix = false`, in other words when you pin a static `name`, any change that forces a replacement will fail, because AWS cannot create a second security group with the same name in the same VPC. The module's own guidance is that you either keep `use_name_prefix = true`, which is the default, or change the `name` along with the replacement. The reason create_before_destroy is there at all is that AWS security group names are unique within a VPC, so the usual Terraform replacement pattern, which creates the new object before destroying the old one, would collide on the name. A prefixed name such as `example-20260806` changes on every replacement and avoids the collision, at the cost of leaving groups with dated names behind, which is why the module also ships the `revoke_rules_on_delete` input for cleaning up.

Preset submodules for single-service groups

Beyond the root module, the repository ships preset submodules under `modules/`, each carrying a curated set of ingress rules for one service, with PostgreSQL, Consul and Cassandra named as examples. The documentation says to use one when a security group serves a single service, and the source address in that case carries a double slash and the submodule path, `terraform-aws-modules/security-group/aws//modules/postgresql`. The preset form takes a simpler shape than the root module, with an `ingress_cidr_ipv4` map of named CIDR blocks such as `vpc` and `peer` rather than the full rule object list. The repository layout explains the rest of the structure: `main.tf`, `variables.tf`, `outputs.tf` and `versions.tf` at the root, a `wrappers/` directory, a `generate/` directory, a `docs/` directory and an `examples/` directory whose `complete` example is described as demonstrating the full module surface.

Versions, requirements and the rules for what gets tested

The requirements table gives the floor: Terraform 1.5.7 or newer and the AWS provider 6.29 or newer, and the module declares no sub-modules of its own. The version history shows a major bump in June 2026 with v6.0.0, preceded by v5.3.1 in October 2025 and v5.3.0 in January 2025, so the 5.x line ran for over a year before the major release. A major version in a Terraform module usually means an input or behaviour change, and the CHANGELOG.md in the repository is where the details live. The documentation is generated into the README by a pre-commit hook and a `.releaserc.json` file drives releases, so the input tables in the README are the source of truth rather than hand-written prose. One input deserves a note because it will look strange in a review: `putin_khuylo` is a boolean input, defaulting to `true`, whose description asks whether you agree that Putin does not respect Ukrainian sovereignty and territorial integrity. It is a political statement encoded as a module argument, and since the module is published to a public registry, that choice is part of the artefact you are depending on.

Editorial conclusion

Adopt terraform-aws-security-group when you want security group rules to live in code, be able to add a rule without replacing the group, and have out-of-band changes reverted. Skip it if your team manages security groups by hand in the console, because the exclusive rules default will fight that. Before you commit, read the create_before_destroy note about use_name_prefix, since a static name turns a replacement into a failure, and check the release history, because v6.0.0 landed in June 2026 after a long run on the 5.x line.

Frequently asked questions

What does terraform-aws-security-group create in AWS?

An EC2 security group within a VPC, plus separate `aws_vpc_security_group_ingress_rule` and `aws_vpc_security_group_egress_rule` resources, an exclusivity resource and a VPC association resource, so rules are managed as their own objects rather than inline attributes of the group.

What does enable_exclusive_rules do in the security group module?

It defaults to `true` and enforces that only the rules declared by the module exist on the security group. Rules added out of band, through the AWS console or by other Terraform configurations, are reverted on the next apply.

How do I allow traffic between members of the same security group?

Set `referenced_security_group_id = "self"` on the rule. The sentinel is rewritten to the security group's own id at apply time, so you do not need a two-step apply to supply the id.

Why does use_name_prefix matter when replacing a security group?

The resource uses `create_before_destroy` so a replacement does not drop traffic, but AWS cannot create a second security group with the same name in the same VPC. With `use_name_prefix = false` and a static name, a forced replacement fails, so either keep the default prefix behaviour or change the name along with the replacement.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. terraform-aws-modules/terraform-aws-security-group on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/terraform-aws-modules-terraform-aws-security-group.svg)](https://hysenlabs.com/projects/terraform-aws-modules-terraform-aws-security-group)

Was this useful?