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 resource is maintained for backwards compatibility; for new projects, use aws.vpc.SecurityGroupIngressRule and aws.vpc.SecurityGroupEgressRule, which provide better support for multiple CIDR blocks and include tags and descriptions. This guide focuses on three capabilities: CIDR-based traffic rules, VPC endpoint integration, and AWS-managed prefix lists.
Security group rules reference existing security groups and specify traffic sources using CIDR blocks, prefix lists, or other security groups. The examples are intentionally small. Combine them with your own security groups and avoid mixing this resource with inline rules on aws.ec2.SecurityGroup resources.
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, egress for outbound). The fromPort and toPort define the port range, while protocol specifies TCP, UDP, ICMP, or a protocol number. The cidrBlocks and ipv6CidrBlocks properties list the IP ranges that can access this port range.
Route egress traffic to VPC endpoints
Applications accessing AWS services through VPC endpoints need egress rules that reference the endpoint’s prefix list.
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
VPC endpoints expose a prefixListId that represents the service’s IP ranges. The prefixListIds property accepts this ID, allowing outbound traffic to the service without hardcoding IP addresses. Setting protocol to “-1” permits all protocols.
Reference AWS-managed prefix lists for service access
AWS publishes prefix lists for its services, providing stable references that update automatically as 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 looks up AWS-managed prefix lists by name (format: “com.amazonaws.{region}.{service}”). The returned ID goes into prefixListIds, allowing HTTPS egress to S3. The description property documents the rule’s purpose.
Beyond these examples
These snippets focus on specific rule-level features: CIDR-based ingress and egress rules, and VPC endpoint and AWS-managed prefix list integration. They’re intentionally minimal rather than complete 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 VPC infrastructure.
To keep things focused, common rule patterns are omitted, including:
- Security group-to-security group rules (sourceSecurityGroupId)
- Self-referencing rules (self property)
- Rule descriptions for documentation
- Port range variations (ICMP, protocol numbers)
These omissions are intentional: the goal is to illustrate how each rule feature 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 & Compatibility
aws.vpc.SecurityGroupEgressRule and aws.vpc.SecurityGroupIngressRule instead. The aws.ec2.SecurityGroupRule resource struggles with managing multiple CIDR blocks and lacks support for unique IDs, tags, and descriptions.aws.ec2.SecurityGroupRule with aws.vpc.SecurityGroupEgressRule/IngressRule or with inline rules in aws.ec2.SecurityGroup causes rule conflicts, perpetual differences, and overwritten rules. Choose one approach exclusively.Source & Destination Configuration
cidrBlocks, ipv6CidrBlocks, sourceSecurityGroupId, self, or prefixListIds. However, cidrBlocks and ipv6CidrBlocks cannot be used with sourceSecurityGroupId or self. Similarly, sourceSecurityGroupId cannot be used with cidrBlocks, ipv6CidrBlocks, or self.prefixListIds with IDs from VPC endpoints (e.g., myEndpoint.prefixListId) or from the aws.ec2.getPrefixList data source for AWS-managed prefix lists like S3.Protocol & Port Configuration
fromPort and toPort values. This API behavior cannot be controlled by Pulumi and may generate warnings.fromPort for the ICMP type number and toPort for the ICMP code.Immutability & Updates
fromPort, toPort, protocol, type, securityGroupId, sourceSecurityGroupId, cidrBlocks, ipv6CidrBlocks, prefixListIds, and self. Changing any of these requires replacing the rule.Cross-VPC & Advanced Scenarios
Using a different cloud?
Explore networking guides for other cloud providers: