The aws:ec2/securityGroupRule:SecurityGroupRule resource, part of the Pulumi AWS provider, defines individual ingress or egress rules that control traffic to and from security groups. This guide focuses on three capabilities: CIDR-based traffic rules, VPC endpoint integration, and AWS-managed prefix lists.
This resource is maintained for backwards compatibility. For new projects, consider using aws.vpc.SecurityGroupIngressRule and aws.vpc.SecurityGroupEgressRule, which handle multiple CIDR blocks more reliably and avoid rule conflicts. The examples are intentionally small. Combine them with your own security groups and network infrastructure.
Allow traffic from CIDR blocks on TCP ports
Most rules start by allowing inbound traffic from specific IP ranges, establishing baseline network access for applications.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.ec2.SecurityGroupRule("example", {
type: "ingress",
fromPort: 0,
toPort: 65535,
protocol: aws.ec2.ProtocolType.TCP,
cidrBlocks: [exampleAwsVpc.cidrBlock],
ipv6CidrBlocks: [exampleAwsVpc.ipv6CidrBlock],
securityGroupId: "sg-123456",
});
import pulumi
import pulumi_aws as aws
example = aws.ec2.SecurityGroupRule("example",
type="ingress",
from_port=0,
to_port=65535,
protocol=aws.ec2.ProtocolType.TCP,
cidr_blocks=[example_aws_vpc["cidrBlock"]],
ipv6_cidr_blocks=[example_aws_vpc["ipv6CidrBlock"]],
security_group_id="sg-123456")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := ec2.NewSecurityGroupRule(ctx, "example", &ec2.SecurityGroupRuleArgs{
Type: pulumi.String("ingress"),
FromPort: pulumi.Int(0),
ToPort: pulumi.Int(65535),
Protocol: pulumi.String(ec2.ProtocolTypeTCP),
CidrBlocks: pulumi.StringArray{
exampleAwsVpc.CidrBlock,
},
Ipv6CidrBlocks: pulumi.StringArray{
exampleAwsVpc.Ipv6CidrBlock,
},
SecurityGroupId: pulumi.String("sg-123456"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.Ec2.SecurityGroupRule("example", new()
{
Type = "ingress",
FromPort = 0,
ToPort = 65535,
Protocol = Aws.Ec2.ProtocolType.TCP,
CidrBlocks = new[]
{
exampleAwsVpc.CidrBlock,
},
Ipv6CidrBlocks = new[]
{
exampleAwsVpc.Ipv6CidrBlock,
},
SecurityGroupId = "sg-123456",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.SecurityGroupRule;
import com.pulumi.aws.ec2.SecurityGroupRuleArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var example = new SecurityGroupRule("example", SecurityGroupRuleArgs.builder()
.type("ingress")
.fromPort(0)
.toPort(65535)
.protocol("tcp")
.cidrBlocks(exampleAwsVpc.cidrBlock())
.ipv6CidrBlocks(exampleAwsVpc.ipv6CidrBlock())
.securityGroupId("sg-123456")
.build());
}
}
resources:
example:
type: aws:ec2:SecurityGroupRule
properties:
type: ingress
fromPort: 0
toPort: 65535
protocol: tcp
cidrBlocks:
- ${exampleAwsVpc.cidrBlock}
ipv6CidrBlocks:
- ${exampleAwsVpc.ipv6CidrBlock}
securityGroupId: sg-123456
The type property sets the traffic direction (ingress for inbound). The fromPort and toPort define the port range, while protocol specifies TCP. The cidrBlocks and ipv6CidrBlocks properties list the IP ranges that can connect. The securityGroupId identifies which security group receives this rule.
Route egress traffic to VPC endpoints
Applications connecting to AWS services through VPC endpoints reference the endpoint’s prefix list rather than hardcoding IP ranges.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// ...
const myEndpoint = new aws.ec2.VpcEndpoint("my_endpoint", {});
const allowAll = new aws.ec2.SecurityGroupRule("allow_all", {
type: "egress",
toPort: 0,
protocol: "-1",
prefixListIds: [myEndpoint.prefixListId],
fromPort: 0,
securityGroupId: "sg-123456",
});
import pulumi
import pulumi_aws as aws
# ...
my_endpoint = aws.ec2.VpcEndpoint("my_endpoint")
allow_all = aws.ec2.SecurityGroupRule("allow_all",
type="egress",
to_port=0,
protocol="-1",
prefix_list_ids=[my_endpoint.prefix_list_id],
from_port=0,
security_group_id="sg-123456")
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// ...
myEndpoint, err := ec2.NewVpcEndpoint(ctx, "my_endpoint", nil)
if err != nil {
return err
}
_, err = ec2.NewSecurityGroupRule(ctx, "allow_all", &ec2.SecurityGroupRuleArgs{
Type: pulumi.String("egress"),
ToPort: pulumi.Int(0),
Protocol: pulumi.String("-1"),
PrefixListIds: pulumi.StringArray{
myEndpoint.PrefixListId,
},
FromPort: pulumi.Int(0),
SecurityGroupId: pulumi.String("sg-123456"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
// ...
var myEndpoint = new Aws.Ec2.VpcEndpoint("my_endpoint");
var allowAll = new Aws.Ec2.SecurityGroupRule("allow_all", new()
{
Type = "egress",
ToPort = 0,
Protocol = "-1",
PrefixListIds = new[]
{
myEndpoint.PrefixListId,
},
FromPort = 0,
SecurityGroupId = "sg-123456",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.VpcEndpoint;
import com.pulumi.aws.ec2.SecurityGroupRule;
import com.pulumi.aws.ec2.SecurityGroupRuleArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
// ...
var myEndpoint = new VpcEndpoint("myEndpoint");
var allowAll = new SecurityGroupRule("allowAll", SecurityGroupRuleArgs.builder()
.type("egress")
.toPort(0)
.protocol("-1")
.prefixListIds(myEndpoint.prefixListId())
.fromPort(0)
.securityGroupId("sg-123456")
.build());
}
}
resources:
allowAll:
type: aws:ec2:SecurityGroupRule
name: allow_all
properties:
type: egress
toPort: 0
protocol: '-1'
prefixListIds:
- ${myEndpoint.prefixListId}
fromPort: 0
securityGroupId: sg-123456
# ...
myEndpoint:
type: aws:ec2:VpcEndpoint
name: my_endpoint
When you create a VPC endpoint, AWS assigns it a prefix list ID that represents the service’s IP ranges. The prefixListIds property references this list, allowing traffic to the endpoint. Setting protocol to “-1” permits all protocols, while fromPort and toPort of 0 allow all ports.
Reference AWS service prefix lists by region
AWS publishes managed prefix lists for services like S3, automatically updating as service IP ranges change.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const current = aws.getRegion({});
const s3 = current.then(current => aws.ec2.getPrefixList({
name: `com.amazonaws.${current.region}.s3`,
}));
const s3GatewayEgress = new aws.ec2.SecurityGroupRule("s3_gateway_egress", {
description: "S3 Gateway Egress",
type: "egress",
securityGroupId: "sg-123456",
fromPort: 443,
toPort: 443,
protocol: aws.ec2.ProtocolType.TCP,
prefixListIds: [s3.then(s3 => s3.id)],
});
import pulumi
import pulumi_aws as aws
current = aws.get_region()
s3 = aws.ec2.get_prefix_list(name=f"com.amazonaws.{current.region}.s3")
s3_gateway_egress = aws.ec2.SecurityGroupRule("s3_gateway_egress",
description="S3 Gateway Egress",
type="egress",
security_group_id="sg-123456",
from_port=443,
to_port=443,
protocol=aws.ec2.ProtocolType.TCP,
prefix_list_ids=[s3.id])
package main
import (
"fmt"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ec2"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
current, err := aws.GetRegion(ctx, &aws.GetRegionArgs{}, nil)
if err != nil {
return err
}
s3, err := ec2.GetPrefixList(ctx, &ec2.GetPrefixListArgs{
Name: pulumi.StringRef(fmt.Sprintf("com.amazonaws.%v.s3", current.Region)),
}, nil)
if err != nil {
return err
}
_, err = ec2.NewSecurityGroupRule(ctx, "s3_gateway_egress", &ec2.SecurityGroupRuleArgs{
Description: pulumi.String("S3 Gateway Egress"),
Type: pulumi.String("egress"),
SecurityGroupId: pulumi.String("sg-123456"),
FromPort: pulumi.Int(443),
ToPort: pulumi.Int(443),
Protocol: pulumi.String(ec2.ProtocolTypeTCP),
PrefixListIds: pulumi.StringArray{
pulumi.String(s3.Id),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var current = Aws.GetRegion.Invoke();
var s3 = Aws.Ec2.GetPrefixList.Invoke(new()
{
Name = $"com.amazonaws.{current.Apply(getRegionResult => getRegionResult.Region)}.s3",
});
var s3GatewayEgress = new Aws.Ec2.SecurityGroupRule("s3_gateway_egress", new()
{
Description = "S3 Gateway Egress",
Type = "egress",
SecurityGroupId = "sg-123456",
FromPort = 443,
ToPort = 443,
Protocol = Aws.Ec2.ProtocolType.TCP,
PrefixListIds = new[]
{
s3.Apply(getPrefixListResult => getPrefixListResult.Id),
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.AwsFunctions;
import com.pulumi.aws.inputs.GetRegionArgs;
import com.pulumi.aws.ec2.Ec2Functions;
import com.pulumi.aws.ec2.inputs.GetPrefixListArgs;
import com.pulumi.aws.ec2.SecurityGroupRule;
import com.pulumi.aws.ec2.SecurityGroupRuleArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
final var current = AwsFunctions.getRegion(GetRegionArgs.builder()
.build());
final var s3 = Ec2Functions.getPrefixList(GetPrefixListArgs.builder()
.name(String.format("com.amazonaws.%s.s3", current.region()))
.build());
var s3GatewayEgress = new SecurityGroupRule("s3GatewayEgress", SecurityGroupRuleArgs.builder()
.description("S3 Gateway Egress")
.type("egress")
.securityGroupId("sg-123456")
.fromPort(443)
.toPort(443)
.protocol("tcp")
.prefixListIds(s3.id())
.build());
}
}
resources:
s3GatewayEgress:
type: aws:ec2:SecurityGroupRule
name: s3_gateway_egress
properties:
description: S3 Gateway Egress
type: egress
securityGroupId: sg-123456
fromPort: 443
toPort: 443
protocol: tcp
prefixListIds:
- ${s3.id}
variables:
current:
fn::invoke:
function: aws:getRegion
arguments: {}
s3:
fn::invoke:
function: aws:ec2:getPrefixList
arguments:
name: com.amazonaws.${current.region}.s3
The getPrefixList data source finds AWS-managed prefix lists by name. The name follows the pattern “com.amazonaws.{region}.{service}”. The description property documents the rule’s purpose. This configuration allows HTTPS egress to S3 on port 443.
Beyond these examples
These snippets focus on specific security group rule features: CIDR-based ingress and egress rules, and VPC endpoint and AWS service prefix list integration. They’re intentionally minimal rather than full network security configurations.
The examples reference pre-existing infrastructure such as security groups (all examples reference existing group IDs), and VPC endpoints (for prefix list examples). They focus on configuring individual rules rather than provisioning security groups or VPCs.
To keep things focused, common rule patterns are omitted, including:
- Security group self-referencing (self property)
- Cross-security-group rules (sourceSecurityGroupId)
- Rule descriptions for documentation
- ICMP protocol configuration
These omissions are intentional: the goal is to illustrate how each rule type is wired, not provide drop-in security modules. See the Security Group Rule resource reference for all available configuration options.
Let's configure AWS Security Group Rules
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Selection & Best Practices
aws.vpc.SecurityGroupEgressRule and aws.vpc.SecurityGroupIngressRule instead, with one CIDR block per rule.aws.ec2.SecurityGroupRule alongside aws.vpc.SecurityGroupEgressRule, aws.vpc.SecurityGroupIngressRule, or inline rules in aws.ec2.SecurityGroup causes rule conflicts, perpetual differences, and overwritten rules. Choose one approach and stick with it.Configuration Requirements
cidrBlocks, ipv6CidrBlocks, prefixListIds, sourceSecurityGroupId, or self. All are marked optional, but at least one is required to define the traffic source.cidrBlocks and ipv6CidrBlocks cannot be specified with sourceSecurityGroupId or self. These source options are mutually exclusive.type, fromPort, toPort, protocol, securityGroupId, sourceSecurityGroupId, cidrBlocks, ipv6CidrBlocks, prefixListIds, and self. Changing any of these requires replacing the rule.type to ingress for inbound traffic rules or egress for outbound traffic rules.Protocol & Port Behavior
fromPort and toPort values. This API behavior can’t be controlled by Pulumi and may generate warnings.Advanced Features
prefixListIds property. You can reference VPC endpoint prefix lists directly (e.g., myEndpoint.prefixListId) or use the aws.ec2.getPrefixList data source to find AWS-managed prefix lists like S3.Import Behavior
Using a different cloud?
Explore networking guides for other cloud providers: