Resource: aws_security_group

Provides a security group resource.

Example Usage

Basic Usage

resource "aws_security_group" "allow_tls" {
  name        = "allow_tls"
  description = "Allow TLS inbound traffic and all outbound traffic"
  vpc_id      = aws_vpc.main.id

  tags = {
    Name = "allow_tls"
  }
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = aws_vpc.main.cidr_block
  from_port         = 443
  ip_protocol       = "tcp"
  to_port           = 443
}

resource "aws_vpc_security_group_ingress_rule" "allow_tls_ipv6" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv6         = aws_vpc.main.ipv6_cidr_block
  from_port         = 443
  ip_protocol       = "tcp"
  to_port           = 443
}

resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv4" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = "-1" # semantically equivalent to all ports
}

resource "aws_vpc_security_group_egress_rule" "allow_all_traffic_ipv6" {
  security_group_id = aws_security_group.allow_tls.id
  cidr_ipv6         = "::/0"
  ip_protocol       = "-1" # semantically equivalent to all ports
}
resource "aws_security_group" "example" {
  # ... other configuration ...

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

Usage With Prefix List IDs

Prefix Lists are either managed by AWS internally, or created by the customer using a Prefix List resource. Prefix Lists provided by AWS are associated with a prefix list name, or service name, that is linked to a specific region. Prefix list IDs are exported on VPC Endpoints, so you can use this format:

resource "aws_security_group" "example" {
  # ... other configuration ...

  egress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    prefix_list_ids = [aws_vpc_endpoint.my_endpoint.prefix_list_id]
  }
}

resource "aws_vpc_endpoint" "my_endpoint" {
  # ... other configuration ...
}

You can also find a specific Prefix List using the aws_prefix_list data source.

Removing All Ingress and Egress Rules

The ingress and egress arguments are processed in attributes-as-blocks mode. Due to this, removing these arguments from the configuration will not cause Terraform to destroy the managed rules. To subsequently remove all managed ingress and egress rules:

resource "aws_security_group" "example" {
  name   = "sg"
  vpc_id = aws_vpc.example.id

  ingress = []
  egress  = []
}

Recreating a Security Group

A simple security group name change "forces new" the security group--Terraform destroys the security group and creates a new one. (Likewise, description, name_prefix, or vpc_id cannot be changed.) Attempting to recreate the security group leads to a variety of complications depending on how it is used.

Security groups are generally associated with other resources--more than 100 AWS Provider resources reference security groups. Referencing a resource from another resource creates a one-way dependency. For example, if you create an EC2 aws_instance that has a vpc_security_group_ids argument that refers to an aws_security_group resource, the aws_security_group is a dependent of the aws_instance. Because of this, Terraform will create the security group first so that it can then be associated with the EC2 instance.

However, the dependency relationship actually goes both directions causing the _Security Group Deletion Problem_. AWS does not allow you to delete the security group associated with another resource (_e.g._, the aws_instance).

Terraform does not model bi-directional dependencies like this, but, even if it did, simply knowing the dependency situation would not be enough to solve it. For example, some resources must always have an associated security group while others don't need to. In addition, when the aws_security_group resource attempts to recreate, it receives a dependent object error, which does not provide information on whether the dependent object is a security group rule or, for example, an associated EC2 instance. Within Terraform, the associated resource (_e.g._, aws_instance) does not receive an error when the aws_security_group is trying to recreate even though that is where changes to the associated resource would need to take place (_e.g._, removing the security group association).

Despite these sticky problems, below are some ways to improve your experience when you find it necessary to recreate a security group.

create_before_destroy

(This example is one approach to recreating security groups. For more information on the challenges and the _Security Group Deletion Problem_, see the section above.)

Normally, Terraform first deletes the existing security group resource and then creates a new one. When a security group is associated with a resource, the delete won't succeed. You can invert the default behavior using the create_before_destroy meta argument:

resource "aws_security_group" "example" {
  name = "changeable-name"
  # ... other configuration ...

  lifecycle {
    create_before_destroy = true
  }
}

replace_triggered_by

(This example is one approach to recreating security groups. For more information on the challenges and the _Security Group Deletion Problem_, see the section above.)

To replace a resource when a security group changes, use the replace_triggered_by meta argument. Note that in this example, the aws_instance will be destroyed and created again when the aws_security_group changes.

resource "aws_security_group" "example" {
  name = "sg"
  # ... other configuration ...
}

resource "aws_instance" "example" {
  instance_type = "t3.small"
  # ... other configuration ...

  vpc_security_group_ids = [aws_security_group.test.id]

  lifecycle {
    # Reference the security group as a whole or individual attributes like `name`
    replace_triggered_by = [aws_security_group.example]
  }
}

Shorter timeout

(This example is one approach to recreating security groups. For more information on the challenges and the _Security Group Deletion Problem_, see the section above.)

If destroying a security group takes a long time, it may be because Terraform cannot distinguish between a dependent object (_e.g._, a security group rule or EC2 instance) that is _in the process of being deleted_ and one that is not. In other words, it may be waiting for a train that isn't scheduled to arrive. To fail faster, shorten the delete timeout from the default timeout:

resource "aws_security_group" "example" {
  name = "izizavle"
  # ... other configuration ...

  timeouts {
    delete = "2m"
  }
}

Provisioners

(This example is one approach to recreating security groups. For more information on the challenges and the _Security Group Deletion Problem_, see the section above.)

DISCLAIMER: We _HIGHLY_ recommend using one of the above approaches and _NOT_ using local provisioners. Provisioners, like the one shown below, should be considered a last resort since they are _not readable_, _require skills outside standard Terraform configuration_, are _error prone_ and _difficult to maintain_, are not compatible with cloud environments and upgrade tools, require AWS CLI installation, and are subject to AWS CLI and Terraform changes outside the AWS Provider.

data "aws_security_group" "default" {
  name = "default"
  # ... other configuration ...
}

resource "aws_security_group" "example" {
  name = "sg"
  # ... other configuration ...

  # The downstream resource must have at least one SG attached, therefore we
  # attach the default SG of the VPC temporarily and remove it later on
  provisioner "local-exec" {
    when       = destroy
    command    = <<DOC
            ENDPOINT_ID=`aws ec2 describe-vpc-endpoints --filters "Name=tag:Name,Values=${self.tags.workaround1}" --query "VpcEndpoints[0].VpcEndpointId" --output text` &&
            aws ec2 modify-vpc-endpoint --vpc-endpoint-id $${ENDPOINT_ID} --add-security-group-ids ${self.tags.workaround2} --remove-security-group-ids ${self.id}
        DOC
    on_failure = continue
  }

  tags = {
    workaround1 = "tagged-name"
    workaround2 = data.aws_security_group.default.id
  }
}

resource "null_resource" "example" {
  provisioner "local-exec" {
    command    = <<DOC
            aws ec2 modify-vpc-endpoint --vpc-endpoint-id ${aws_vpc_endpoint.example.id} --remove-security-group-ids ${data.aws_security_group.default.id}
        DOC
    on_failure = continue
  }

  triggers = {
    rerun_upon_change_of = join(",", aws_vpc_endpoint.example.security_group_ids)
  }
}
            ENDPOINT_ID=`aws ec2 describe-vpc-endpoints --filters "Name=tag:Name,Values=${self.tags.workaround1}" --query "VpcEndpoints[0].VpcEndpointId" --output text` &&
            aws ec2 modify-vpc-endpoint --vpc-endpoint-id $${ENDPOINT_ID} --add-security-group-ids ${self.tags.workaround2} --remove-security-group-ids ${self.id}
        DOC
    on_failure = continue
  }

  tags = {
    workaround1 = "tagged-name"
    workaround2 = data.aws_security_group.default.id
  }
}

resource "null_resource" "example" {
  provisioner "local-exec" {
    command    = <<DOC
            aws ec2 modify-vpc-endpoint --vpc-endpoint-id ${aws_vpc_endpoint.example.id} --remove-security-group-ids ${data.aws_security_group.default.id}
        DOC
    on_failure = continue
  }

  triggers = {
    rerun_upon_change_of = join(",", aws_vpc_endpoint.example.security_group_ids)
  }
}
            aws ec2 modify-vpc-endpoint --vpc-endpoint-id ${aws_vpc_endpoint.example.id} --remove-security-group-ids ${data.aws_security_group.default.id}
        DOC
    on_failure = continue
  }

  triggers = {
    rerun_upon_change_of = join(",", aws_vpc_endpoint.example.security_group_ids)
  }
}

Argument Reference

This resource supports the following arguments:

ingress

This argument is processed in attribute-as-blocks mode.

The following arguments are required:

The following arguments are optional:

egress

This argument is processed in attribute-as-blocks mode.

The following arguments are required:

The following arguments are optional:

Attribute Reference

This resource exports the following attributes in addition to the arguments above:

Timeouts

Configuration options:

Import

In Terraform v1.5.0 and later, use an import block to import Security Groups using the security group id. For example:

import {
  to = aws_security_group.elb_sg
  id = "sg-903004f8"
}

Using terraform import, import Security Groups using the security group id. For example:

% terraform import aws_security_group.elb_sg sg-903004f8