The aws:ec2/defaultNetworkAcl:DefaultNetworkAcl resource, part of the Pulumi AWS provider, adopts a VPC’s default network ACL into Pulumi management, allowing you to define ingress and egress rules declaratively. This guide focuses on three capabilities: adopting the default ACL with standard AWS rules, creating asymmetric traffic patterns, and blocking all traffic to enforce explicit ACL usage.
This resource adopts an existing default ACL rather than creating one. Every VPC has a default ACL that AWS creates automatically. When Pulumi first adopts the ACL, it immediately removes all existing rules, then creates only the rules you specify. The examples are intentionally small. Combine them with your own VPC and subnet configuration.
Adopt the default ACL with AWS default rules
When you create a VPC, AWS provisions a default network ACL with permissive rules that allow all traffic. Teams often adopt this ACL into management to detect drift when rules change outside of infrastructure code.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const mainvpc = new aws.ec2.Vpc("mainvpc", {cidrBlock: "10.1.0.0/16"});
const _default = new aws.ec2.DefaultNetworkAcl("default", {
defaultNetworkAclId: mainvpc.defaultNetworkAclId,
ingress: [{
protocol: "-1",
ruleNo: 100,
action: "allow",
cidrBlock: "0.0.0.0/0",
fromPort: 0,
toPort: 0,
}],
egress: [{
protocol: "-1",
ruleNo: 100,
action: "allow",
cidrBlock: "0.0.0.0/0",
fromPort: 0,
toPort: 0,
}],
});
import pulumi
import pulumi_aws as aws
mainvpc = aws.ec2.Vpc("mainvpc", cidr_block="10.1.0.0/16")
default = aws.ec2.DefaultNetworkAcl("default",
default_network_acl_id=mainvpc.default_network_acl_id,
ingress=[{
"protocol": "-1",
"rule_no": 100,
"action": "allow",
"cidr_block": "0.0.0.0/0",
"from_port": 0,
"to_port": 0,
}],
egress=[{
"protocol": "-1",
"rule_no": 100,
"action": "allow",
"cidr_block": "0.0.0.0/0",
"from_port": 0,
"to_port": 0,
}])
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 {
mainvpc, err := ec2.NewVpc(ctx, "mainvpc", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.1.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewDefaultNetworkAcl(ctx, "default", &ec2.DefaultNetworkAclArgs{
DefaultNetworkAclId: mainvpc.DefaultNetworkAclId,
Ingress: ec2.DefaultNetworkAclIngressArray{
&ec2.DefaultNetworkAclIngressArgs{
Protocol: pulumi.String("-1"),
RuleNo: pulumi.Int(100),
Action: pulumi.String("allow"),
CidrBlock: pulumi.String("0.0.0.0/0"),
FromPort: pulumi.Int(0),
ToPort: pulumi.Int(0),
},
},
Egress: ec2.DefaultNetworkAclEgressArray{
&ec2.DefaultNetworkAclEgressArgs{
Protocol: pulumi.String("-1"),
RuleNo: pulumi.Int(100),
Action: pulumi.String("allow"),
CidrBlock: pulumi.String("0.0.0.0/0"),
FromPort: pulumi.Int(0),
ToPort: pulumi.Int(0),
},
},
})
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 mainvpc = new Aws.Ec2.Vpc("mainvpc", new()
{
CidrBlock = "10.1.0.0/16",
});
var @default = new Aws.Ec2.DefaultNetworkAcl("default", new()
{
DefaultNetworkAclId = mainvpc.DefaultNetworkAclId,
Ingress = new[]
{
new Aws.Ec2.Inputs.DefaultNetworkAclIngressArgs
{
Protocol = "-1",
RuleNo = 100,
Action = "allow",
CidrBlock = "0.0.0.0/0",
FromPort = 0,
ToPort = 0,
},
},
Egress = new[]
{
new Aws.Ec2.Inputs.DefaultNetworkAclEgressArgs
{
Protocol = "-1",
RuleNo = 100,
Action = "allow",
CidrBlock = "0.0.0.0/0",
FromPort = 0,
ToPort = 0,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.DefaultNetworkAcl;
import com.pulumi.aws.ec2.DefaultNetworkAclArgs;
import com.pulumi.aws.ec2.inputs.DefaultNetworkAclIngressArgs;
import com.pulumi.aws.ec2.inputs.DefaultNetworkAclEgressArgs;
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 mainvpc = new Vpc("mainvpc", VpcArgs.builder()
.cidrBlock("10.1.0.0/16")
.build());
var default_ = new DefaultNetworkAcl("default", DefaultNetworkAclArgs.builder()
.defaultNetworkAclId(mainvpc.defaultNetworkAclId())
.ingress(DefaultNetworkAclIngressArgs.builder()
.protocol("-1")
.ruleNo(100)
.action("allow")
.cidrBlock("0.0.0.0/0")
.fromPort(0)
.toPort(0)
.build())
.egress(DefaultNetworkAclEgressArgs.builder()
.protocol("-1")
.ruleNo(100)
.action("allow")
.cidrBlock("0.0.0.0/0")
.fromPort(0)
.toPort(0)
.build())
.build());
}
}
resources:
mainvpc:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.1.0.0/16
default:
type: aws:ec2:DefaultNetworkAcl
properties:
defaultNetworkAclId: ${mainvpc.defaultNetworkAclId}
ingress:
- protocol: -1
ruleNo: 100
action: allow
cidrBlock: 0.0.0.0/0
fromPort: 0
toPort: 0
egress:
- protocol: -1
ruleNo: 100
action: allow
cidrBlock: 0.0.0.0/0
fromPort: 0
toPort: 0
The defaultNetworkAclId property references the VPC’s default ACL ID. The ingress and egress arrays define rules with protocol (-1 means all protocols), ruleNo for evaluation order, action (allow or deny), and CIDR blocks. This configuration preserves AWS default behavior while bringing the ACL under Pulumi management.
Block outbound traffic while allowing inbound
Some security architectures require asymmetric traffic rules, such as allowing inbound connections while preventing resources from initiating outbound requests.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const mainvpc = new aws.ec2.Vpc("mainvpc", {cidrBlock: "10.1.0.0/16"});
const _default = new aws.ec2.DefaultNetworkAcl("default", {
defaultNetworkAclId: mainvpc.defaultNetworkAclId,
ingress: [{
protocol: "-1",
ruleNo: 100,
action: "allow",
cidrBlock: mainvpcAwsDefaultVpc.cidrBlock,
fromPort: 0,
toPort: 0,
}],
});
import pulumi
import pulumi_aws as aws
mainvpc = aws.ec2.Vpc("mainvpc", cidr_block="10.1.0.0/16")
default = aws.ec2.DefaultNetworkAcl("default",
default_network_acl_id=mainvpc.default_network_acl_id,
ingress=[{
"protocol": "-1",
"rule_no": 100,
"action": "allow",
"cidr_block": mainvpc_aws_default_vpc["cidrBlock"],
"from_port": 0,
"to_port": 0,
}])
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 {
mainvpc, err := ec2.NewVpc(ctx, "mainvpc", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.1.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewDefaultNetworkAcl(ctx, "default", &ec2.DefaultNetworkAclArgs{
DefaultNetworkAclId: mainvpc.DefaultNetworkAclId,
Ingress: ec2.DefaultNetworkAclIngressArray{
&ec2.DefaultNetworkAclIngressArgs{
Protocol: pulumi.String("-1"),
RuleNo: pulumi.Int(100),
Action: pulumi.String("allow"),
CidrBlock: pulumi.Any(mainvpcAwsDefaultVpc.CidrBlock),
FromPort: pulumi.Int(0),
ToPort: pulumi.Int(0),
},
},
})
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 mainvpc = new Aws.Ec2.Vpc("mainvpc", new()
{
CidrBlock = "10.1.0.0/16",
});
var @default = new Aws.Ec2.DefaultNetworkAcl("default", new()
{
DefaultNetworkAclId = mainvpc.DefaultNetworkAclId,
Ingress = new[]
{
new Aws.Ec2.Inputs.DefaultNetworkAclIngressArgs
{
Protocol = "-1",
RuleNo = 100,
Action = "allow",
CidrBlock = mainvpcAwsDefaultVpc.CidrBlock,
FromPort = 0,
ToPort = 0,
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.DefaultNetworkAcl;
import com.pulumi.aws.ec2.DefaultNetworkAclArgs;
import com.pulumi.aws.ec2.inputs.DefaultNetworkAclIngressArgs;
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 mainvpc = new Vpc("mainvpc", VpcArgs.builder()
.cidrBlock("10.1.0.0/16")
.build());
var default_ = new DefaultNetworkAcl("default", DefaultNetworkAclArgs.builder()
.defaultNetworkAclId(mainvpc.defaultNetworkAclId())
.ingress(DefaultNetworkAclIngressArgs.builder()
.protocol("-1")
.ruleNo(100)
.action("allow")
.cidrBlock(mainvpcAwsDefaultVpc.cidrBlock())
.fromPort(0)
.toPort(0)
.build())
.build());
}
}
resources:
mainvpc:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.1.0.0/16
default:
type: aws:ec2:DefaultNetworkAcl
properties:
defaultNetworkAclId: ${mainvpc.defaultNetworkAclId}
ingress:
- protocol: -1
ruleNo: 100
action: allow
cidrBlock: ${mainvpcAwsDefaultVpc.cidrBlock}
fromPort: 0
toPort: 0
Omitting the egress property creates an implicit deny-all for outbound traffic. The ingress rule allows traffic from within the VPC’s CIDR block. This creates a one-way traffic pattern where resources can receive connections but cannot initiate them.
Lock down the default ACL to force explicit associations
Teams that want to enforce explicit network ACL assignments can configure the default ACL to deny all traffic, ensuring subnets must be associated with purpose-built ACLs.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const mainvpc = new aws.ec2.Vpc("mainvpc", {cidrBlock: "10.1.0.0/16"});
const _default = new aws.ec2.DefaultNetworkAcl("default", {defaultNetworkAclId: mainvpc.defaultNetworkAclId});
import pulumi
import pulumi_aws as aws
mainvpc = aws.ec2.Vpc("mainvpc", cidr_block="10.1.0.0/16")
default = aws.ec2.DefaultNetworkAcl("default", default_network_acl_id=mainvpc.default_network_acl_id)
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 {
mainvpc, err := ec2.NewVpc(ctx, "mainvpc", &ec2.VpcArgs{
CidrBlock: pulumi.String("10.1.0.0/16"),
})
if err != nil {
return err
}
_, err = ec2.NewDefaultNetworkAcl(ctx, "default", &ec2.DefaultNetworkAclArgs{
DefaultNetworkAclId: mainvpc.DefaultNetworkAclId,
})
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 mainvpc = new Aws.Ec2.Vpc("mainvpc", new()
{
CidrBlock = "10.1.0.0/16",
});
var @default = new Aws.Ec2.DefaultNetworkAcl("default", new()
{
DefaultNetworkAclId = mainvpc.DefaultNetworkAclId,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.ec2.Vpc;
import com.pulumi.aws.ec2.VpcArgs;
import com.pulumi.aws.ec2.DefaultNetworkAcl;
import com.pulumi.aws.ec2.DefaultNetworkAclArgs;
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 mainvpc = new Vpc("mainvpc", VpcArgs.builder()
.cidrBlock("10.1.0.0/16")
.build());
var default_ = new DefaultNetworkAcl("default", DefaultNetworkAclArgs.builder()
.defaultNetworkAclId(mainvpc.defaultNetworkAclId())
.build());
}
}
resources:
mainvpc:
type: aws:ec2:Vpc
properties:
cidrBlock: 10.1.0.0/16
default:
type: aws:ec2:DefaultNetworkAcl
properties:
defaultNetworkAclId: ${mainvpc.defaultNetworkAclId}
Omitting both ingress and egress properties creates an implicit deny-all for all traffic. Any subnet associated with this default ACL will be completely isolated. This forces you to create custom NetworkAcl resources and explicitly associate subnets with them.
Beyond these examples
These snippets focus on specific default network ACL features: adopting the default ACL into management, ingress and egress rule configuration, and implicit deny-all through rule omission. They’re intentionally minimal rather than full network security configurations.
The examples assume pre-existing infrastructure such as a VPC with a default network ACL (created automatically by AWS). They focus on ACL rule configuration rather than provisioning the surrounding network infrastructure.
To keep things focused, common ACL patterns are omitted, including:
- Explicit subnet associations (subnetIds)
- Tags for organization and cost tracking
- IPv6 CIDR blocks (ipv6CidrBlock)
- ICMP type/code configuration for protocol-specific rules
These omissions are intentional: the goal is to illustrate how default ACL adoption and rule management work, not provide drop-in network security modules. See the Default Network ACL resource reference for all available configuration options.
Let's configure AWS VPC Default Network ACLs
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Resource Behavior & Lifecycle
Rule Management
aws.ec2.NetworkAclRule resources. Define all rules inline using the ingress and egress properties.Subnet Associations
subnetIds, or use lifecycle ignoreChanges for subnetIds.Common Configurations
egress property entirely and define only ingress rules. This denies all outbound traffic while permitting the specified inbound traffic.defaultNetworkAclId specified, omitting both ingress and egress properties. This denies all traffic to any subnet in the default network ACL.Using a different cloud?
Explore networking guides for other cloud providers: