# AWS with Terraform Tutorial: AWS Security Groups (9)

> How to configure and use the Terraform aws_security_group and aws_security_group_rule resources to create AWS security groups and secure the infrastructure.

- Source: https://www.itwonderlab.com/aws-terraform-tutorial-aws-security-groups/
- Published: 2024-01-26
- Updated: 2026-10-05
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## How to Create, and Manage AWS Security Groups with Terraform

**How to configure and use the Terraform `aws_security_group` and `aws_security_group_rule` resource blocks to create and manage AWS Security Groups and secure the infrastructure.**

Welcome to our tutorial series where we dive into cloud infrastructure deployment using **[Terraform or OpenTofu on AWS](https://www.itwonderlab.com/tag/aws-terraform-essential-guide/)**. In this series, the fundamentals are shown, guiding you through the process of minimizing resource usage and simplifying the deployment complexities associated with cloud infrastructure.

This comprehensive **OpenTofu and Terraform tutorial** guides you step-by-step through creating infrastructure in AWS using Terraform.

![Terraform Basics, AWS Basics, Terraform AWS Provider, AWS VPC, AWS Subnets, AWS Internet Gateway, AWS NAT Gateway, AWS Routing Tables, AWS Security Groups, AWS Key Pairs, AWS AMIs, AWS EC2 Instances, AWS RDS, AWS Route 53 (DNS), AWS Auto Scaling, AWS Load Balancers, Terraform AWS & Ansible, Terraform Modules, Terraform Backends, Terraform Tools, Terraform CI/CD.](https://www.itwonderlab.com/media/tutorials/AWS-Terraform-Essentials/ITWL-Tutorials-AWS-Terraform-Essentials-Steps.svg)

Infrastructure as Code (IaC) helps maintain consistency, enables version control, enhances collaboration among teams, allows for easier replication of environments, streamlines the deployment and management of infrastructure boosting efficiency, and reducing errors in managing complex systems.

### Prerequisites

Read the previous sections of the tutorial, listed in the [series index](#series) at the end of this page.

### AWS Security Groups

An [AWS Security Group](https://www.itwonderlab.com/aws-security-groups/) acts as a virtual firewall for AWS instances and services controlling inbound and outbound traffic. It is a set of rules that filter traffic based on protocols, ports, and origins or destinations.

Security Groups are composed of Security Rules that define the inbound or outbound flows. In AWS, Security Groups define what is authorized, by default everything else is denied.

The diagram shows the elements of a Security Group:

![AWS Terraform Essentials Security Groups Rules](https://www.itwonderlab.com/media/tutorials/AWS-Terraform-Essentials/ITWL-Tutorials-AWS-Terraform-Essentials-Security-Groups-Rules.svg)

#### Ingress or Inbound Rules

Ingress Rules, also called Inbound Rules apply to traffic that enters the instance, e.g. traffic generated on the Internet that reaches an instance (instance being the destination).

Components of an Inbound or Ingress Security Rule are:

##### Type

All types of traffic, TCP, UDP, or ICMP (e.g. ping)

##### Port or Port Range

The destination port can be a single port or a range. A single port is specified with its number (e.g. 80) and a range with a start port number, a dash, and the end port number (e.g. 1024-1040). The port number is only required for TCP and UDP protocols.

##### Source

The origin is the instance or network generating the traffic, it can be specified in many different ways:

-   IPv4 CIDR notation (e.g. 75.2.98.97/32)
-   IPv6 CIDR notation (e.g. 2001:db6::/32)
-   IPv4 Anywhere (0.0.0.0/0)
-   IPv6 Anywhere (::/0)
-   An AWS Prefix (a set of one or more CIDR blocks). See [Group CIDR blocks using managed prefix lists](https://docs.aws.amazon.com/vpc/latest/userguide/managed-prefix-lists.html).
-   A Security Group: This is the recommended way to specify sources inside the VPC. The source is any instance that has a group with this name attached.

##### Destination

Specifies the resource the incoming network traffic is trying to reach. The destination in an inbound rule is not specified as it is **derived from the instances that have the Security Group attached**. E.g. If the security group is attached to `instance-web-server-01` and `instance-web-server-02`, then the destination becomes any of those instances.

##### Description

A text explaining intended use that acts as documentation

##### Tags

A list of tags and values classifying the rule.

#### Egress or Outbound Rules

Egress Rules, also called Outbound Rules apply to traffic that leaves the instance, e.g. traffic generated in the instance that is sent to another instance or destination (e.g. from an Instance to the Internet).

When you create a new Security Group in a VPC, AWS sets up an allow all egress rule by default. However, when using Terraform, this default rule is removed.

Components of an Egress or Outbound Security Rule are:

##### Type

All types of traffic, TCP, UDP, or ICMP (e.g. ping)

##### Port or Port Range

The destination port can be a single port or a range. A single port is specified with its number (e.g. 80) and a range with a start port number, a dash, and the end port number (e.g. 1024-1040). The port number is only required for TCP and UDP protocols.

##### Source

Specifies the resource that is generating the outgoing network traffic. The source in an outbound rule is not specified as it is **derived from the instances that have the Security Group attached**. E.g. If the security group is attached to instance-web-server-01, then the source becomes this instance (eg. this instance is the source for traffic going to a database instance in a private subnet).

##### Destination

The destination is the instance or network where the traffic is going, it can be specified in many different ways:

-   IPv4 CIDR notation (e.g. 75.2.98.97/32)
-   IPv6 CIDR notation (e.g. 2001:db6::/32)
-   IPv4 Anywhere (0.0.0.0/0)
-   IPv6 Anywhere (::/0)
-   An AWS Prefix (a set of one or more CIDR blocks). See [Group CIDR blocks using managed prefix lists](https://docs.aws.amazon.com/vpc/latest/userguide/managed-prefix-lists.html).
-   A Security Group: This is the recommended way to specify sources inside the VPC. The source is any instance that has a group with this name attached.

##### Description

A text explaining intended use that acts as documentation

##### Tags

A list of tags and values classifying the rule.

### Connectivity Diagrams

Security should be taken as a priority when designing any Infrastructure. A secure instance will have by default all the incoming traffic denied and only the needed ports open and if possible only to traffic originating from known sources. E.g. A database server that stores data for a web application should have all inbound traffic denied except the one originating from the application that uses the data (a web server probably).

A diagram with the services, listening ports, and intended connections helps design a secure infrastructure.

The diagram shows two instances that will be added in the next sections and their connectivity requirements:

![AWS Terraform Essentials Security Groups Design 2](https://www.itwonderlab.com/media/tutorials/AWS-Terraform-Essentials/ITWL-Tutorials-AWS-Terraform-Essentials-Security-Groups-Design_2-1.svg)

#### EC2 Instance: ditwl-ec-front-end-001

A front-end instance, located in the `ditwl-sn-za-pro-pub-00` Public Subnet. Instances in public subnets are usually exposed to the Internet, this one will expose port 80 and run an HTTP server.

Connectivity requirements:

-   Outbound access (from the instance) to the Internet: the instance should be able to access the Internet to download updates.
-   Inbound access (to the instance) from the Internet to the Instance TCP port 80.
-   Outbound access (from the instance) to the TCP port 8080 on the back-end server `ditwl-ec-back-end-123` instance.

#### EC2 Instance: ditwl-ec-back-end-123

A back-end instance, located in the `ditwl-sn-za-pro-pri-02` Private Subnet. Instances in private subnets are not exposed to the Internet, this one will expose port 8080 and run an HTTP server (e.g. an API).

Connectivity requirements:

-   Outbound access (from the instance) to the Internet: the instance should be able to access the Internet to download updates. It will use the NAT Gateway, although it doesn’t have an impact on the Security Rules.
-   Inbound access from the instance `ditwl-ec-front-end-001` to the TCP port 8080.

#### Security Groups Needed

To have a secure infrastructure, each group of common services (eg. cluster of web servers running the same application) requires a specific security group, additionally a security group with common rules for all the instances can be added to open ssh and observability access from selected sources.

![AWS Terraform Essentials Security Groups](https://www.itwonderlab.com/media/tutorials/AWS-Terraform-Essentials/ITWL-Tutorials-AWS-Terraform-Essentials-Security-Groups.svg)

##### Security Group ditwl-sg-base-ec2

This is a security group with common rules that should be applied to all instances. It allows inbound connectivity to port TCP 22 from the Internet and also ICMP.

The recommended security practice is to limit SSH and ICMP access to a set of known IP addresses corresponding to the management network.

See [How to programmatically use your public Internet IP address in Terraform?](https://www.itwonderlab.com/use-your-public-internet-ip-address-terraform/) for a better approach.

###### Ingress Security Rule ditwl-sr-internet-to-ec2-ssh

Allows inbound connectivity to port TCP 22 from the Internet. Allowing SSH from any address is a security risk as the instances could be compromised. The recommended practice is to limit SSH access to a set of known IP addresses corresponding to the management network or use EC2 Instance Connect or [AWS Systems Manager Session Manager](https://www.itwonderlab.com/aws-systems-manager/), which need no inbound rules.

###### Ingress ditwl-sr-internet-to-ec2-icmp

Allows inbound ICMP from the Internet, that again is not a recommended practice and should be limited to a set of known IP addresses corresponding to the management network.

###### ditwl-sr-all-outbund

Allows all outbound connectivity from the instance, including access to the Internet.

##### Security Group ditwl-sg-front-end

This security group defines the access rules for the Front End Instance.

###### Ingress ditwl-sr-internet-to-front-end-http

Allow access from the Internet to TCP port 80 (for a web server).

##### Security Group ditwl-sg-back-end

This security group defines the access rules for the Back-End Instance, allowing access only from the Front-End instance.

###### Ingress ditwl-sr-front-end-to-api

Allow access from instances having the `ditwl-sg-front-end` security group assigned to TCP port 8080 (for a web server offering an API).

### How to create an AWS Security Group with Terraform

Previous sections of this [AWS with Terraform guide](https://www.itwonderlab.com/tag/aws-terraform-essential-guide/) have covered basic information about Terraform and AWS and have configured and used the AWS Terraform provider to create a VPC, four subnets, one Internet Gateway, two NAT Gateways, and three Routing Tables.

It is time to create Security Groups that will allow traffic between different resources and to the Internet.

Keep adding Terraform blocks to the `terraform-aws-tutorial.tf` created in previous sections.

#### Terraform Code for Security Groups

Security Group creation is done with `aws_security_group` and `aws_security_group_rule` Terraform Blocks.

Security Groups need to be assigned to Instances using the Instance creation block `aws_instance` property `vpc_security_group_ids`. See the EC2 Instance creation section for the assignment.

Add the following block to the file `terraform-aws-tutorial.tf`.

For Security Group `ditwl-sg-base-ec2`:

```yaml
# Create a "base" Security Group for EC2 instances
resource "aws_security_group" "ditwl-sg-base-ec2" {
  name        = "ditwl-sg-base-ec2"
  vpc_id      = aws_vpc.ditlw-vpc.id
  description = "Base security Group for EC2 instances"
}

# DANGEROUS!!
# Allow access from the Internet to port 22 (SSH) in the Public EC2 instances
resource "aws_security_group_rule" "ditwl-sr-internet-to-ec2-ssh" {
  security_group_id = aws_security_group.ditwl-sg-base-ec2.id
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"] # Internet
  description       = "Allow access from the Internet to port 22 (SSH)"
}

# Allow access from the Internet for ICMP protocol (e.g. ping) to the EC2 instances
resource "aws_security_group_rule" "ditwl-sr-internet-to-ec2-icmp" {
  security_group_id = aws_security_group.ditwl-sg-base-ec2.id
  type              = "ingress"
  from_port         = -1
  to_port           = -1
  protocol          = "icmp"
  cidr_blocks       = ["0.0.0.0/0"] # Internet
  description       = "Allow access from the Internet for ICMP protocol"
}

# Allow all outbound traffic to the Internet
resource "aws_security_group_rule" "ditwl-sr-all-outbund" {
  security_group_id = aws_security_group.ditwl-sg-base-ec2.id
  type              = "egress"
  from_port         = "0"
  to_port           = "0"
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  description       = "Allow all outbound traffic to Internet"
}
```

For Security Group `ditwl-sg-front-end`:

```yaml
# Create a Security Group for the Front end Server
resource "aws_security_group" "ditwl-sg-front-end" {
  name        = "ditwl-sg-front-end"
  vpc_id      = aws_vpc.ditlw-vpc.id
  description = "Front end Server Security"
}

# Allow access from the Internet to port 80 HTTP in the EC2 instances
resource "aws_security_group_rule" "ditwl-sr-internet-to-front-end-http" {
  security_group_id = aws_security_group.ditwl-sg-front-end.id
  type              = "ingress"
  from_port         = 80
  to_port           = 80
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"] # Internet
  description       = "Access from the Internet to port 80 in the EC2 instances"
}
```

For Security Group `ditwl-sg-back-end`:

```hcl
# Create a Security Group for the Back-end Server
resource "aws_security_group" "ditwl-sg-back-end" {
  name        = "ditwl-sg-back-end"
  vpc_id      = aws_vpc.ditlw-vpc.id
  description = "Back-end Server Security"
}

# Allow access from the front-end to port 8080 in the back-end API
resource "aws_security_group_rule" "ditwl-sr-front-end-to-api" {
  security_group_id        = aws_security_group.ditwl-sg-back-end.id
  type                     = "ingress"
  from_port                = 8080
  to_port                  = 8080
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.ditwl-sg-front-end.id
  description              = "Allow access from the front-end to port 8080 in the back-end API"
}
```

### Run the Terraform Plan

Open a command line shell in the directory where the file `terraform-aws-tutorial.tf` is located, and run the Terraform or OpenTofu `plan` and `apply` commands.

#### Plan using OpenTofu

Run `tofu plan` to generate and review the execution plan. Check each line and value to make sure that it corresponds to the desired change.

Terraform will refresh the state comparing it with the Cloud resources and produce a plan for the resources that need to be created, updated, or destroyed.

In the past section of the tutorial, some resources were commented out to reduce the infrastructure cost during development, as the security Groups don’t have any dependency yet with the commented code, there is no need to uncomment.

Run the Terraform plan:

#### Apply the changes using OpenTofu

Run `tofu apply` to generate and **apply** the execution plan. OpenTofu will generate a new plan and ask for confirmation before applying the changes. Review again the changes and answer yes or no to apply the changes in AWS.

Review the result, it should be `12 (resource) added, 0 changed, 0 destroyed`.

### Security Groups Cost

There is no cost for creating or having Security Groups in AWS. See also [AWS Security Groups’ Best Practices](https://www.itwonderlab.com/aws-security-groups-best-practices/).

### Common Questions About AWS Security Groups

#### Can the source be an IP address for instance inbound?

It can be, although it is not recommended as the common practice is to use DHCP for private IP address assignment. If the instance IP address changes after a reboot then all security rules will need to be updated and if the old IP address gets assigned to another instance it can create a security risk.

#### How does Security Group differ from Network ACL?

A security group is stateful and operates at the instance (network interface) level; a [network ACL](https://www.itwonderlab.com/aws-network-acl/) is stateless and works at the subnet level.

#### What’s the default rule for a new Security Group?

All inbound traffic is denied and, in the console, all outbound traffic is allowed (a security group created with Terraform has no outbound rules until they are defined).

#### Can I assign multiple Security Groups to an instance?

Yes. By default you can associate up to five security groups with a network interface (the quota can be increased), and the rules of all of them are combined.

#### Do Security Groups support deny rules?

No, security groups only have allow rules: whatever is not allowed is denied. To deny a specific source explicitly, use a [network ACL](https://www.itwonderlab.com/aws-network-acl/).

### Next Steps

Continue with [AWS Key Pairs](https://www.itwonderlab.com/aws-terraform-tutorial-aws-key-pairs/), or see the [full series index](#series).
