AWS.EC2 reference
AuthorizeSecurityGroupIngress
Section titled “AuthorizeSecurityGroupIngress”Source:
src/AWS/EC2/AuthorizeSecurityGroupIngress.ts
Runtime binding for the AuthorizeSecurityGroupIngress operation scoped to
the bound SecurityGroup (IAM action
ec2:AuthorizeSecurityGroupIngress on the security group ARN).
Adds an inbound rule to the group at runtime — the classic dynamic
IP-allowlisting Lambda that opens a port for an operator’s current address.
Pair with RevokeSecurityGroupIngress to remove the rule afterwards.
Provide the implementation with
Effect.provide(AWS.EC2.AuthorizeSecurityGroupIngressHttp).
AuthorizeSecurityGroupIngress: Dynamic Security Group Rules
Section titled “AuthorizeSecurityGroupIngress: Dynamic Security Group Rules”// init — bind the operation to the security groupconst authorizeIngress = yield* AWS.EC2.AuthorizeSecurityGroupIngress(group);
// runtime — open port 22 for the caller's addressyield* authorizeIngress({ IpProtocol: "tcp", FromPort: 22, ToPort: 22, CidrIp: "203.0.113.7/32",});ClientVpnAuthorizationRule
Section titled “ClientVpnAuthorizationRule”Source:
src/AWS/EC2/ClientVpnAuthorizationRule.ts
Authorizes Client VPN clients to access a destination network. Authorization does not create a route: configure a target association and any required routes separately. Specify either accessGroupId or authorizeAllGroups: true.
Every property is immutable, including description. Description-only changes revoke the old rule before creating its replacement. This resource manages the declared endpoint/CIDR/group identity, including an existing rule discovered without cached state. Rules have no independent ownership markers. Revoking an all-groups rule leaves group-specific rules untouched.
Readiness waits default to 30 minutes. Set AWS_CLIENT_VPN_TIMEOUT to a
positive finite duration, such as 45 minutes, to override this deadline.
ClientVpnAuthorizationRule: Authorizing All Clients
Section titled “ClientVpnAuthorizationRule: Authorizing All Clients”const rule = yield* AWS.EC2.ClientVpnAuthorizationRule("VpnVpcAccess", { clientVpnEndpointId: endpoint.clientVpnEndpointId, targetNetworkCidr: "10.0.0.0/16", authorizeAllGroups: true, description: "Access to the VPC",});ClientVpnAuthorizationRule: Authorizing One Group
Section titled “ClientVpnAuthorizationRule: Authorizing One Group”const rule = yield* AWS.EC2.ClientVpnAuthorizationRule("VpnAdminAccess", { clientVpnEndpointId: endpoint.clientVpnEndpointId, targetNetworkCidr: "10.0.1.0/24", accessGroupId: "S-1-5-21-123456789-123456789-123456789-1234",});ClientVpnEndpoint
Section titled “ClientVpnEndpoint”Source:
src/AWS/EC2/ClientVpnEndpoint.ts
An AWS Client VPN endpoint for authenticated remote access to a VPC. Create target network associations, authorization rules, and routes separately. An endpoint is usable only after a target network has finished associating. Client address range, authentication, transport, address families, and VPC changes replace the endpoint. Other settings update in place; omitted optional settings restore the documented defaults rather than leaving drift unmanaged.
Readiness waits default to 30 minutes. Set AWS_CLIENT_VPN_TIMEOUT to a
positive finite duration, such as 45 minutes, to override this deadline.
ClientVpnEndpoint: Creating an Endpoint
Section titled “ClientVpnEndpoint: Creating an Endpoint”const endpoint = yield* AWS.EC2.ClientVpnEndpoint("Vpn", { clientCidrBlock: "172.20.0.0/22", serverCertificateArn: certificate.certificateArn, authenticationOptions: [{ type: "certificate-authentication", mutualAuthentication: { clientRootCertificateChainArn: certificate.certificateArn, }, }], splitTunnel: true, vpcId: vpc.vpcId,});ClientVpnEndpoint: Logging Connections
Section titled “ClientVpnEndpoint: Logging Connections”const endpoint = yield* AWS.EC2.ClientVpnEndpoint("LoggedVpn", { clientCidrBlock: "172.20.0.0/22", serverCertificateArn: certificate.certificateArn, authenticationOptions: [{ type: "directory-service-authentication", activeDirectory: { directoryId: "d-0123456789" }, }], connectionLogOptions: { enabled: true, cloudwatchLogGroup: logGroup.logGroupName, },});ClientVpnRoute
Section titled “ClientVpnRoute”Source:
src/AWS/EC2/ClientVpnRoute.ts
Adds a destination route through a Client VPN target subnet. The target must already be associated; reference the association’s subnetId output, not the subnet resource directly, to preserve deployment and teardown ordering. Authorization rules are configured separately and are also required for access.
All property changes require replacement. Description-only changes delete the old route first because AWS has no modify-route API. AWS-created local routes cannot be managed or deleted with this resource. This resource manages the declared endpoint/destination/subnet identity, including an existing manual route discovered without cached state. Routes have no independent ownership markers.
Readiness waits default to 30 minutes. Set AWS_CLIENT_VPN_TIMEOUT to a
positive finite duration, such as 45 minutes, to override this deadline.
ClientVpnRoute: Routing Client Traffic
Section titled “ClientVpnRoute: Routing Client Traffic”const target = yield* AWS.EC2.ClientVpnTargetNetworkAssociation("VpnTarget", { clientVpnEndpointId: endpoint.clientVpnEndpointId, subnetId: publicSubnet.subnetId,});const route = yield* AWS.EC2.ClientVpnRoute("VpnInternetRoute", { clientVpnEndpointId: target.clientVpnEndpointId, targetVpcSubnetId: target.subnetId, destinationCidrBlock: "0.0.0.0/0", description: "Internet egress",});The subnet’s VPC routing must provide the required egress path. For multiple target associations, add the same destination through each target subnet so clients have consistent access regardless of which association serves them.
ClientVpnTargetNetworkAssociation
Section titled “ClientVpnTargetNetworkAssociation”Source:
src/AWS/EC2/ClientVpnTargetNetworkAssociation.ts
Associates a VPC subnet with a Client VPN endpoint and waits for it to become associated. AWS automatically adds the VPC’s local route; do not manage that automatic route with ClientVpnRoute. Security groups are endpoint-wide settings. Association and disassociation can take several minutes. The provider waits for AWS to finish, fails on association errors, and supports cancellation.
All property changes replace the association. Replacement within the same endpoint deletes first because AWS permits only one subnet per Availability Zone. Removing the last association disconnects clients. This resource manages the declared endpoint/subnet pair, including an existing association discovered without cached state. Associations have no independent ownership markers.
Readiness waits default to 30 minutes. Set AWS_CLIENT_VPN_TIMEOUT to a
positive finite duration, such as 45 minutes, to override this deadline.
ClientVpnTargetNetworkAssociation: Associating a Target Network
Section titled “ClientVpnTargetNetworkAssociation: Associating a Target Network”const target = yield* AWS.EC2.ClientVpnTargetNetworkAssociation("VpnTarget", { clientVpnEndpointId: endpoint.clientVpnEndpointId, subnetId: privateSubnet.subnetId,});const route = yield* AWS.EC2.ClientVpnRoute("VpnInternet", { clientVpnEndpointId: target.clientVpnEndpointId, targetVpcSubnetId: target.subnetId, destinationCidrBlock: "0.0.0.0/0",});CreateSnapshot
Section titled “CreateSnapshot”Source:
src/AWS/EC2/CreateSnapshot.ts
Runtime binding for the CreateSnapshot operation scoped to the bound
Volume (IAM actions ec2:CreateSnapshot + ec2:CreateTags on the
volume ARN and the region-wide snapshot wildcard — snapshot creation
authorizes against both the source volume and the new snapshot).
Creates a point-in-time EBS snapshot of the volume — e.g. a Lambda that
takes an application-consistent backup before a risky migration. The
snapshot is created pending and completes asynchronously. Provide the
implementation with Effect.provide(AWS.EC2.CreateSnapshotHttp).
CreateSnapshot: Volume Backups
Section titled “CreateSnapshot: Volume Backups”// init — bind the operation to the volumeconst createSnapshot = yield* AWS.EC2.CreateSnapshot(volume);
// runtime — take a point-in-time backupconst snapshot = yield* createSnapshot({ Description: "pre-migration backup",});console.log(snapshot.SnapshotId, snapshot.State);DefaultSecurityGroup
Section titled “DefaultSecurityGroup”Source:
src/AWS/EC2/DefaultSecurityGroup.ts
Declaratively manages the rules of the AWS-created default security group
in one VPC. AWS creates this group named default whenever it creates a
VPC; Alchemy looks it up by VPC ID and name, never creates it, and never
deletes it.
Inline rules use the same defaults as SecurityGroup: omitted or undefined
ingress means no inline inbound rules, and omitted or undefined egress
means IPv4 allow-all outbound. Passing [] means no inline rules in that
direction, not unmanaged rules. Defaults apply on initial management,
property removal, adoption, and drift repair; AWS’s initial self-ingress
rule is removed, not restored.
Current standalone SecurityGroupRule declarations in the same stack and
stage retain ownership of their persisted physical rule IDs. Other rules
are removed even if they carry Alchemy tags. This resource manages rules,
not ownership of the AWS-created group; do not also manage this group with
a SecurityGroup resource or a second DefaultSecurityGroup manager.
Removing this Alchemy resource leaves both the default group and its last applied rules unchanged. Deleting its VPC removes the group as part of AWS’s VPC lifecycle.
DefaultSecurityGroup: Default Inline Rules
Section titled “DefaultSecurityGroup: Default Inline Rules”Remove AWS self-ingress and allow outbound IPv4
const vpc = yield* AWS.EC2.Vpc("Vpc", { cidrBlock: "10.0.0.0/16" });const group = yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId,});Explicit undefined has the same meaning as omission
yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId, ingress: undefined, egress: undefined,});DefaultSecurityGroup: Closing the Default Security Group
Section titled “DefaultSecurityGroup: Closing the Default Security Group”const vpc = yield* AWS.EC2.Vpc("Vpc", { cidrBlock: "10.0.0.0/16" });yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId, ingress: [], egress: [],});DefaultSecurityGroup: Updating Rules
Section titled “DefaultSecurityGroup: Updating Rules”yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId, ingress: [{ ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "10.0.0.0/16", description: "Internal HTTPS", }], egress: [],});Unchanged deployments repair missing rules and remove undeclared rules. Updates leave unrelated rule IDs untouched; description changes update in place, and removing a description clears it. Equivalent protocol names, canonical CIDRs, rule order, and duplicate rules do not cause rule churn. Duplicates with conflicting descriptions are rejected before any writes. A rule with several source fields expands into one AWS rule per source.
DefaultSecurityGroup: Restoring Default Inline Rules
Section titled “DefaultSecurityGroup: Restoring Default Inline Rules”yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId, ingress: [], egress: [],});This still denies inline inbound access but restores IPv4 allow-all outbound. Removing these properties is different from removing the manager itself: deleting the manager leaves its last-applied rules, with no baseline restore.
DefaultSecurityGroup: Composing Standalone Rules
Section titled “DefaultSecurityGroup: Composing Standalone Rules”const group = yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: vpc.vpcId, ingress: [], egress: [],});yield* AWS.EC2.SecurityGroupRule("HttpsEgress", { group: group, type: "egress", ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "10.0.0.0/16",});Pass the whole manager as group so rule creation and updates wait for its
inline reconciliation. Only group.groupId is consumed by the rule provider;
unrelated manager attributes do not trigger rule updates. The stable ID is
still available for replacement planning. The ID-only groupId form cannot
enforce this ordering when the manager updates without changing its ID.
Standalone ingress composes the same way. Omitting egress instead would
retain the default allow-all rule alongside this standalone rule. A current
declaration and its persisted physical ID establish ownership, not cloud
tags. Removing a declaration ends that protection; its provider handles
deletion. Cross-stack or cross-stage rule ownership is unsupported.
Inline and standalone rules must have distinct identities. If a standalone
rule owns IPv4 allow-all egress, set egress: [] to disable the inline default.
DefaultSecurityGroup: Changing VPCs
Section titled “DefaultSecurityGroup: Changing VPCs”yield* AWS.EC2.DefaultSecurityGroup("DefaultSecurityGroup", { vpcId: otherVpc.vpcId, ingress: [], egress: [],});Changing vpcId replaces the Alchemy resource, not either AWS-owned group,
including when the destination VPC is created or replaced in the same deploy.
The previous group’s last-applied rules remain unchanged. Keep the old VPC
declared until replacement finishes, then remove it separately if desired.
DescribeInstance
Section titled “DescribeInstance”Source:
src/AWS/EC2/DescribeInstance.ts
Runtime binding for the DescribeInstances operation scoped to the bound
Instance (IAM action ec2:DescribeInstances; Describe* actions
do not support resource-level permissions, so the grant is on *).
Returns the bound instance’s live description — state, addresses, block
device mappings — e.g. a Lambda that reports whether a dev box is running.
Provide the implementation with
Effect.provide(AWS.EC2.DescribeInstanceHttp).
DescribeInstance: Observing Instances
Section titled “DescribeInstance: Observing Instances”// init — bind the operation to the instanceconst describeInstance = yield* AWS.EC2.DescribeInstance(instance);
// runtime — read the live descriptionconst live = yield* describeInstance();console.log(live?.State?.Name, live?.PrivateIpAddress);DescribeInstanceStatus
Section titled “DescribeInstanceStatus”Source:
src/AWS/EC2/DescribeInstanceStatus.ts
Runtime binding for the DescribeInstanceStatus operation scoped to the
bound Instance (IAM action ec2:DescribeInstanceStatus;
Describe* actions do not support resource-level permissions, so the grant
is on *).
Reads the instance’s system/instance status checks and scheduled events —
e.g. a Lambda that alerts on failed reachability checks. Pass
IncludeAllInstances: true to also see the status while the instance is
stopped. Provide the implementation with
Effect.provide(AWS.EC2.DescribeInstanceStatusHttp).
DescribeInstanceStatus: Observing Instances
Section titled “DescribeInstanceStatus: Observing Instances”// init — bind the operation to the instanceconst describeStatus = yield* AWS.EC2.DescribeInstanceStatus(instance);
// runtime — read status checks (even while stopped)const result = yield* describeStatus({ IncludeAllInstances: true });console.log(result.InstanceStatuses?.[0]?.InstanceStatus?.Status);DhcpOptions
Section titled “DhcpOptions”Source:
src/AWS/EC2/DhcpOptions.ts
A DHCP options set configures the DHCP parameters (domain name, DNS servers, NTP servers, NetBIOS settings) that a VPC hands out to the instances launched inside it. Attach a custom set to a VPC to override the AWS defaults — for example to point instances at your own DNS or an internal search domain.
A DHCP options set is immutable: AWS provides no edit API, so changing any
DHCP parameter replaces the set. Setting vpcId associates the set with a
VPC; clearing it (or deleting the resource) re-associates the VPC with the
account’s default options set, since a set must be disassociated from every
VPC before it can be deleted.
DhcpOptions: Creating a DHCP Options Set
Section titled “DhcpOptions: Creating a DHCP Options Set”Custom DNS and Search Domain
const dhcp = yield* AWS.EC2.DhcpOptions("CorpDhcp", { domainName: "corp.internal", domainNameServers: ["10.0.0.2", "AmazonProvidedDNS"], vpcId: myVpc.vpcId,});Creates the options set and associates it with the VPC in one step.
Instances launched into the VPC receive the corp.internal search domain and
the listed DNS servers.
NTP and NetBIOS Configuration
const dhcp = yield* AWS.EC2.DhcpOptions("Dhcp", { ntpServers: ["169.254.169.123"], netbiosNameServers: ["10.0.0.5"], netbiosNodeType: "2",});Creates an unassociated options set that you can associate later by setting
vpcId.
EgressOnlyInternetGateway
Section titled “EgressOnlyInternetGateway”Source:
src/AWS/EC2/EgressOnlyInternetGateway.ts
An egress-only internet gateway is the IPv6 counterpart to a NAT gateway: it lets instances in a VPC initiate outbound IPv6 traffic to the internet while preventing the internet from initiating inbound connections to them. Use it to give private, IPv6-addressed resources outbound-only internet access.
Unlike a NAT gateway it is free, has no bandwidth charges, and does not
require an Elastic IP — but it works for IPv6 only. It always belongs to a
VPC (vpcId is required); the gateway must be paired with an IPv6
Route to actually carry traffic.
EgressOnlyInternetGateway: Creating an Egress-Only Internet Gateway
Section titled “EgressOnlyInternetGateway: Creating an Egress-Only Internet Gateway”The gateway is created and attached to vpcId in a single step. Because the
attachment is intrinsic, changing vpcId replaces the gateway rather than
moving it.
Basic Egress-Only Internet Gateway
const egressOnlyIgw = yield* AWS.EC2.EgressOnlyInternetGateway("EgressOnlyIgw", { vpcId: myVpc.vpcId,});Creates the gateway in the VPC. The resulting
egressOnlyInternetGatewayId (prefixed eigw-) is referenced from a
route’s egressOnlyInternetGatewayId target.
Egress-Only Internet Gateway with Tags
const egressOnlyIgw = yield* AWS.EC2.EgressOnlyInternetGateway("EgressOnlyIgw", { vpcId: myVpc.vpcId, tags: { Name: "production-eigw" },});The tags map is merged with the alchemy auto-tags and can be updated in
place without replacing the gateway.
EgressOnlyInternetGateway: Routing IPv6 Egress Traffic
Section titled “EgressOnlyInternetGateway: Routing IPv6 Egress Traffic”A gateway alone does nothing until a private route table sends IPv6 traffic
to it. Pair it with a ::/0 Route so private, IPv6-addressed
instances can reach the internet outbound-only.
const egressOnlyIgw = yield* AWS.EC2.EgressOnlyInternetGateway("EgressOnlyIgw", { vpcId: myVpc.vpcId,});
const ipv6EgressRoute = yield* AWS.EC2.Route("Ipv6EgressRoute", { routeTableId: privateRouteTable.routeTableId, destinationIpv6CidrBlock: "::/0", egressOnlyInternetGatewayId: egressOnlyIgw.egressOnlyInternetGatewayId,});Instances in subnets associated with privateRouteTable can now make
outbound IPv6 connections (updates, API calls) while remaining unreachable
from the public internet.
Source:
src/AWS/EC2/EIP.ts
An Elastic IP address — a static, public IPv4 address allocated to your AWS account that you can attach to instances, network interfaces, or NAT gateways.
Allocating an EIP reserves the address; you then reference its
allocationId from the resource that should use it (for example a public
NatGateway). The address is released back to AWS when the resource is
destroyed. The pool-related properties (publicIpv4Pool,
networkBorderGroup, customerOwnedIpv4Pool) are immutable and replace the
address when changed.
EIP: Allocating Elastic IPs
Section titled “EIP: Allocating Elastic IPs”By default an Elastic IP is allocated for use within a VPC (domain: "vpc"),
which is the only domain available to modern accounts.
const eip = yield* AWS.EC2.EIP("MyEip", { domain: "vpc", tags: { Name: "app-eip" },});This reserves a standard, Amazon-owned public IPv4 address scoped to your VPC;
domain defaults to "vpc", so it can be omitted, and tags help you find
the address in the console and on the bill.
EIP: Bring-Your-Own-IP and Address Pools
Section titled “EIP: Bring-Your-Own-IP and Address Pools”If you have onboarded an address range to AWS (BYOIP) or use Outposts, you can draw the address from a specific pool instead of Amazon’s general pool.
Allocate from a Public IPv4 (BYOIP) Pool
const eip = yield* AWS.EC2.EIP("ByoipEip", { publicIpv4Pool: "ipv4pool-ec2-0abcdef1234567890", networkBorderGroup: "us-east-1",});publicIpv4Pool selects an address from a pool you own rather than a random
Amazon address, and networkBorderGroup restricts which zone group AWS
advertises it from (useful for Local and Wavelength Zones).
Allocate from a Customer-Owned Pool (Outposts)
const eip = yield* AWS.EC2.EIP("CoIpEip", { customerOwnedIpv4Pool: "ipv4pool-coip-0abcdef1234567890",});customerOwnedIpv4Pool pulls a customer-owned IP (CoIP) from an
Outposts-associated pool, for workloads that must use your own on-premises
address space.
EIP: Using an Elastic IP
Section titled “EIP: Using an Elastic IP”const eip = yield* AWS.EC2.EIP("NatEip", {});
const natGateway = yield* AWS.EC2.NatGateway("NatGateway", { subnetId: publicSubnet.subnetId, allocationId: eip.allocationId,});Downstream resources consume the reserved address through its allocationId;
here the EIP becomes the fixed public IP of a NAT gateway.
FlowLog
Section titled “FlowLog”Source:
src/AWS/EC2/FlowLog.ts
A flow log captures information about the IP traffic going to and from a VPC, subnet, or network interface, and publishes it to CloudWatch Logs, S3, or a Kinesis Data Firehose delivery stream. Use it for network monitoring, traffic analysis, and troubleshooting security-group / NACL rules.
A flow log is immutable: every property except tags is fixed at creation,
so changing the monitored resource, destination, or traffic type replaces the
flow log. For CloudWatch Logs delivery you must supply a logGroupName and a
deliverLogsPermissionArn — an IAM role that EC2’s vpc-flow-logs service
principal can assume to write to the group.
FlowLog: Creating a Flow Log
Section titled “FlowLog: Creating a Flow Log”VPC Flow Log to CloudWatch Logs
const logGroup = yield* AWS.Logs.LogGroup("FlowLogs", {});
const role = yield* AWS.IAM.Role("FlowLogRole", { assumeRolePolicyDocument: { Version: "2012-10-17", Statement: [{ Effect: "Allow", Principal: { Service: "vpc-flow-logs.amazonaws.com" }, Action: "sts:AssumeRole", }], }, inlinePolicies: { deliver: { Version: "2012-10-17", Statement: [{ Effect: "Allow", Action: [ "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams", ], Resource: "*", }], }, },});
const flowLog = yield* AWS.EC2.FlowLog("VpcFlowLog", { resourceType: "VPC", resourceId: myVpc.vpcId, logGroupName: logGroup.logGroupName, deliverLogsPermissionArn: role.roleArn,});Captures all traffic for the VPC and delivers it to the CloudWatch Logs group via the delivery role.
S3 Flow Log for Rejected Traffic
const flowLog = yield* AWS.EC2.FlowLog("RejectedTraffic", { resourceType: "Subnet", resourceId: mySubnet.subnetId, trafficType: "REJECT", logDestinationType: "s3", logDestination: bucket.bucketArn,});Delivers only rejected-traffic records for a subnet directly to an S3 bucket (no IAM role required for S3 delivery).
GetAmi
Section titled “GetAmi”Source:
src/AWS/EC2/GetAmi.ts
The DescribeImages-backed AMI lookup (IAM action ec2:DescribeImages;
Describe* actions do not support resource-level permissions, so the
runtime grant is on *). Returns the newest available image matching the
filters, or undefined when nothing matches.
As a data source, invoke it at plan time via getAmi — the
result is an Output resolved during plan/deploy and inert inside
deployed bundles. As a runtime binding, bind it inside a Function to
look images up at runtime with the IAM grant attached automatically.
Provide the implementation with
Effect.provide(AWS.EC2.GetAmiHttp) (already registered by
AWS.providers() for plan-time use).
GetAmi: Looking Up Images
Section titled “GetAmi: Looking Up Images”Plan-time lookup (data source)
const instance = yield* AWS.EC2.Instance("web", { imageId: AWS.EC2.getAmi({ owners: ["amazon"], name: ["al2023-ami-2023.*"], }).ImageId.as<string>(), instanceType: "t3.micro", subnetId: subnet.subnetId,});Runtime lookup inside a Function
// init — bind the operationconst getAmi = yield* AWS.EC2.GetAmi({ owners: ["amazon"], name: ["al2023-ami-2023.*"],});
// runtime — read the newest matching imageconst latest = yield* getAmi();console.log(latest?.ImageId, latest?.CreationDate);GetConsoleOutput
Section titled “GetConsoleOutput”Source:
src/AWS/EC2/GetConsoleOutput.ts
Runtime binding for the GetConsoleOutput operation scoped to the bound
Instance (IAM action ec2:GetConsoleOutput on the instance ARN).
Fetches the instance’s serial console output (base64-encoded) — e.g. a
diagnostics Lambda that captures boot logs when a host fails its health
checks. Pass Latest: true for the most recent output on supported
instance types. Provide the implementation with
Effect.provide(AWS.EC2.GetConsoleOutputHttp).
GetConsoleOutput: Diagnostics
Section titled “GetConsoleOutput: Diagnostics”// init — bind the operation to the instanceconst getConsoleOutput = yield* AWS.EC2.GetConsoleOutput(instance);
// runtime — fetch the (base64-encoded) boot logconst result = yield* getConsoleOutput({ Latest: true });const log = Buffer.from(result.Output ?? "", "base64").toString("utf8");GetPasswordData
Section titled “GetPasswordData”Source:
src/AWS/EC2/GetPasswordData.ts
Runtime binding for the GetPasswordData operation scoped to the bound
Instance (IAM action ec2:GetPasswordData on the instance ARN).
Retrieves the encrypted Windows administrator password for the instance.
The PasswordData field is sensitive and surfaces as
Redacted.Redacted<string> — decrypt it with the launch key pair’s private
key. Linux instances return an empty value. Provide the implementation with
Effect.provide(AWS.EC2.GetPasswordDataHttp).
GetPasswordData: Diagnostics
Section titled “GetPasswordData: Diagnostics”// init — bind the operation to the instanceconst getPasswordData = yield* AWS.EC2.GetPasswordData(instance);
// runtime — the ciphertext is Redacted; unwrap explicitly to decryptconst result = yield* getPasswordData();const ciphertext = result.PasswordData; // Redacted<string>Instance
Section titled “Instance”Source:
src/AWS/EC2/Instance.ts
An EC2 instance that can either act as a low-level compute primitive or run a bundled long-lived Effect program directly on the machine.
Instance: Launching Instances
Section titled “Instance: Launching Instances”const instance = yield* AWS.EC2.Instance("AppInstance", { imageId: AWS.EC2.amazonLinux2023(), instanceType: "t3.micro", subnetId: subnet.subnetId,});Instance: Hosting Processes
Section titled “Instance: Hosting Processes”const api = yield* Effect.gen(function* () { yield* Http.serve( HttpServerResponse.json({ ok: true }), );
return { main: import.meta.url, imageId: AWS.EC2.amazonLinux2023(), instanceType: "t3.small", subnetId: subnet.subnetId, securityGroupIds: [securityGroup.groupId], associatePublicIpAddress: true, port: 3000, };}).pipe( Effect.provide(AWS.EC2.HttpServer), AWS.EC2.Instance("ApiInstance"),);Instance: Bundling & Tree-shaking
Section titled “Instance: Bundling & Tree-shaking”main is bundled with rolldown at deploy time. Unused code is
tree-shaken. effect, alchemy, and @distilled.cloud are marked
pure so unused parts prune more aggressively. Your app is not
marked pure.
Mark additional packages as pure
Only list packages with no top-level side effects.
{ main: import.meta.url, build: { pure: { packages: ["my-lib", "@my-scope/*"] }, },}Turn it off
{ main: import.meta.url, build: { pure: false },}InternetGateway
Section titled “InternetGateway”Source:
src/AWS/EC2/InternetGateway.ts
An internet gateway provides a target for internet-routable traffic in a VPC, enabling bidirectional IPv4 and IPv6 connectivity between resources in your VPC and the public internet. A VPC can have at most one internet gateway attached at a time.
The only inputs are the optional vpcId to attach to and tags. Attaching a
gateway is not enough on its own to make a subnet public — you also need a
0.0.0.0/0 Route pointing at the gateway and a
RouteTableAssociation binding the subnet to that route table.
InternetGateway: Creating an Internet Gateway
Section titled “InternetGateway: Creating an Internet Gateway”Pass vpcId to create and attach the gateway in one step, or omit it to
create a standalone gateway and attach it later by setting the prop. Updating
vpcId moves the gateway between VPCs (detach then attach) without
recreating it.
Internet Gateway Attached to a VPC
const internetGateway = yield* AWS.EC2.InternetGateway("InternetGateway", { vpcId: myVpc.vpcId,});Creates the gateway and attaches it to the VPC immediately. The resulting
internetGatewayId (prefixed igw-) is what you reference from a route’s
gatewayId.
Detached Internet Gateway
const internetGateway = yield* AWS.EC2.InternetGateway("InternetGateway", {});Omitting vpcId creates an unattached gateway. This is occasionally useful
when the VPC is provisioned separately; add the vpcId prop later to attach
it.
Internet Gateway with Tags
const internetGateway = yield* AWS.EC2.InternetGateway("InternetGateway", { vpcId: myVpc.vpcId, tags: { Name: "production-igw" },});The tags map is merged with the alchemy auto-tags and can be changed in
place. A Name tag makes the gateway easy to identify in the AWS console.
InternetGateway: Enabling Public Internet Access
Section titled “InternetGateway: Enabling Public Internet Access”An internet gateway only carries traffic once a route table sends traffic to it and a subnet is associated with that table. The full pattern below makes a subnet public.
const internetGateway = yield* AWS.EC2.InternetGateway("InternetGateway", { vpcId: myVpc.vpcId,});
const publicRouteTable = yield* AWS.EC2.RouteTable("PublicRouteTable", { vpcId: myVpc.vpcId,});
const internetRoute = yield* AWS.EC2.Route("InternetRoute", { routeTableId: publicRouteTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", gatewayId: internetGateway.internetGatewayId,});With the default route in place, any subnet associated with
publicRouteTable can send and receive internet traffic. Add an analogous
route with destinationIpv6CidrBlock: "::/0" to enable IPv6.
KeyPair
Section titled “KeyPair”Source:
src/AWS/EC2/KeyPair.ts
An EC2 key pair used to grant SSH access to instances launched with its
keyName.
By default Alchemy asks EC2 to generate the key pair and captures the
private key (returned only once, at create time) as a secret in state. Pass
KeyPairProps.publicKeyMaterial to import your own public key instead,
in which case no private key is stored.
KeyPair: Creating a Key Pair
Section titled “KeyPair: Creating a Key Pair”Generated key pair
const keyPair = yield* AWS.EC2.KeyPair("DeployKey", { keyType: "ed25519",});// keyPair.keyName -> pass to AWS.EC2.Instance({ keyName })// keyPair.privateKey -> Redacted<string> (the PEM private key)Imported public key
const keyPair = yield* AWS.EC2.KeyPair("ImportedKey", { publicKeyMaterial: "ssh-ed25519 AAAAC3Nz... user@host",});NatGateway
Section titled “NatGateway”Source:
src/AWS/EC2/NatGateway.ts
A NAT gateway that lets instances in a private subnet reach the internet (and other AWS services) while preventing unsolicited inbound connections.
The gateway lives in the subnet given by subnetId, and its
connectivityType decides how it connects: a "public" gateway must sit in a
public subnet and requires an Elastic IP via allocationId, while a
"private" gateway has no public address and is used for VPC-to-VPC routing.
A NAT gateway only carries traffic once a Route sends 0.0.0.0/0 from the
private subnet’s route table to it. Core properties (subnetId,
connectivityType, allocationId) are immutable, so changing them replaces
the gateway.
NatGateway: Public NAT Gateways
Section titled “NatGateway: Public NAT Gateways”Public gateways translate private addresses to a stable public IP, so they must be placed in a public subnet (one with a route to an internet gateway) and given an Elastic IP allocation.
const eip = yield* AWS.EC2.EIP("NatEip", {});
const natGateway = yield* AWS.EC2.NatGateway("NatGateway", { subnetId: publicSubnet.subnetId, allocationId: eip.allocationId, connectivityType: "public", tags: { Name: "production-nat" },});Allocating the EIP first and passing its allocationId gives the gateway a
fixed public IP. connectivityType defaults to "public", so it can be
omitted; this is the standard way to give private instances outbound internet
access.
NatGateway: Private NAT Gateways
Section titled “NatGateway: Private NAT Gateways”Private gateways have no public IP and route traffic between VPCs or to on-premises networks without exposing it to the internet.
Private NAT Gateway with a Fixed Private IP
const natGateway = yield* AWS.EC2.NatGateway("PrivateNat", { subnetId: privateSubnet.subnetId, connectivityType: "private", privateIpAddress: "10.0.10.10",});Omitting allocationId and setting connectivityType: "private" creates a
gateway with no public address; privateIpAddress pins it to a specific
address in the subnet instead of letting AWS choose one automatically.
Private NAT Gateway with Secondary Addresses
const natGateway = yield* AWS.EC2.NatGateway("ScaledNat", { subnetId: privateSubnet.subnetId, connectivityType: "private", secondaryPrivateIpAddressCount: 3,});Secondary private addresses — via secondaryPrivateIpAddressCount,
secondaryPrivateIpAddresses, or secondaryAllocationIds — raise the number
of simultaneous connections a private gateway can sustain to busy
destinations, which is only valid for private gateways.
NatGateway: Routing Private Traffic
Section titled “NatGateway: Routing Private Traffic”const natRoute = yield* AWS.EC2.Route("NatRoute", { routeTableId: privateRouteTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", natGatewayId: natGateway.natGatewayId,});Without a route the gateway is inert; this entry sends all outbound traffic from the private subnet’s route table through the gateway so private instances can reach the internet.
Network
Section titled “Network”Source:
src/AWS/EC2/Network.ts
Creates a production-shaped VPC network from the low-level EC2 primitives.
Network is the ergonomic entry point for users who want a ready-to-use VPC
layout without manually creating route tables, internet gateways, NAT
gateways, and subnet associations by hand.
The helper still returns the underlying canonical resources so callers can
keep composing with raw AWS.EC2.* APIs when they need more control.
Network: Examples
Section titled “Network: Examples”Minimal network
const network = yield* AWS.EC2.Network("AppNetwork", { cidrBlock: "10.42.0.0/16",});ECS-ready network with shared NAT
const network = yield* AWS.EC2.Network("AppNetwork", { cidrBlock: "10.42.0.0/16", availabilityZones: 2, nat: "single", gatewayEndpoints: ["s3"],});
yield* AWS.ECS.Service("ApiService", { cluster, task: apiTask, vpcId: network.vpcId, subnets: network.publicSubnetIds, assignPublicIp: true,});NetworkAcl
Section titled “NetworkAcl”Source:
src/AWS/EC2/NetworkAcl.ts
A network ACL — a stateless firewall that controls inbound and outbound traffic at the subnet level, evaluated as an ordered list of numbered allow/deny rules.
Unlike security groups (which are stateful and attach to interfaces), a
network ACL is associated with subnets and evaluates return traffic
independently, so you typically pair each inbound rule with a matching
ephemeral-port outbound rule. The ACL itself only takes vpcId and tags;
the actual rules live in NetworkAclEntry resources and subnet attachments
in NetworkAclAssociation resources. Changing vpcId replaces the ACL.
NetworkAcl: Creating Network ACLs
Section titled “NetworkAcl: Creating Network ACLs”const acl = yield* AWS.EC2.NetworkAcl("PrivateNetworkAcl", { vpcId: vpc.vpcId, tags: { Name: "private-nacl" },});This creates an empty custom ACL in the VPC — it starts with only the implicit default-deny rules, so until you add entries it blocks all traffic on any subnet you associate with it.
NetworkAcl: Composing Rules and Associations
Section titled “NetworkAcl: Composing Rules and Associations”A network ACL is only useful once you attach rules and point subnets at it.
The typical pattern is one NetworkAcl, several NetworkAclEntry rules, and
one NetworkAclAssociation per subnet.
const acl = yield* AWS.EC2.NetworkAcl("PrivateNetworkAcl", { vpcId: vpc.vpcId,});
const allowVpc = yield* AWS.EC2.NetworkAclEntry("AllowVpc", { networkAclId: acl.networkAclId, ruleNumber: 100, protocol: "-1", ruleAction: "allow", egress: false, cidrBlock: "10.0.0.0/16",});
const association = yield* AWS.EC2.NetworkAclAssociation("SubnetAssoc", { networkAclId: acl.networkAclId, subnetId: privateSubnet.subnetId,});The entry allows all traffic from within the VPC CIDR and the association
makes the subnet use this ACL instead of the VPC default. Build up the full
rule set by adding more NetworkAclEntry resources with increasing
ruleNumbers.
NetworkAclAssociation
Section titled “NetworkAclAssociation”Source:
src/AWS/EC2/NetworkAclAssociation.ts
Associates a subnet with a NetworkAcl, replacing whichever ACL the subnet
currently uses (every subnet is always associated with exactly one network
ACL — the VPC’s default until you point it at a custom one).
Changing subnetId replaces the association, while changing only
networkAclId re-points the same subnet at a different ACL in place. On
delete, the subnet is reverted to the VPC’s default network ACL so it is never
left without one.
NetworkAclAssociation: Associating Subnets
Section titled “NetworkAclAssociation: Associating Subnets”A subnet starts out attached to the VPC’s default ACL; this resource moves it
onto a custom ACL so the rules you defined with NetworkAclEntry take effect
for that subnet.
const association = yield* AWS.EC2.NetworkAclAssociation("PrivateSubnetNaclAssoc", { networkAclId: privateNetworkAcl.networkAclId, subnetId: privateSubnet.subnetId,});This detaches the subnet from the default ACL and attaches it to your custom ACL; destroying the association automatically reverts the subnet to the default ACL, which is the safe way to “remove” a custom ACL from a subnet.
NetworkAclEntry
Section titled “NetworkAclEntry”Source:
src/AWS/EC2/NetworkAclEntry.ts
A single rule in a NetworkAcl — a numbered, stateless allow/deny entry that
matches traffic by protocol, CIDR (IPv4 or IPv6), and, for TCP/UDP, a port
range.
Each entry is identified by its (networkAclId, ruleNumber, egress) triple,
and changing any of those three replaces the entry. Rules are evaluated from
the lowest ruleNumber upward and the first match wins, so leave gaps between
numbers to make room for future rules. Because NACLs are stateless, always add
a matching ephemeral-port rule for return traffic.
NetworkAclEntry: Inbound Rules
Section titled “NetworkAclEntry: Inbound Rules”Inbound rules (egress: false) match traffic entering the subnet. A common
pattern is to allow trusted source ranges plus the ephemeral ports needed for
return traffic.
Allow Inbound Traffic from the VPC CIDR
const allowVpc = yield* AWS.EC2.NetworkAclEntry("AllowVpc", { networkAclId: acl.networkAclId, ruleNumber: 100, protocol: "-1", ruleAction: "allow", egress: false, cidrBlock: "10.0.0.0/16",});protocol: "-1" matches all protocols and cidrBlock scopes the rule to the
VPC’s IPv4 range; the low ruleNumber (100) makes it take precedence over
higher-numbered rules.
Allow Inbound Ephemeral Ports (NAT Return Traffic)
const allowEphemeral = yield* AWS.EC2.NetworkAclEntry("AllowEphemeral", { networkAclId: acl.networkAclId, ruleNumber: 200, protocol: "6", ruleAction: "allow", egress: false, cidrBlock: "0.0.0.0/0", portRange: { from: 1024, to: 65535 },});Because the ACL is stateless, responses to outbound requests arrive on
ephemeral ports and need their own inbound rule; protocol: "6" is TCP and
portRange restricts the match to the ephemeral port range.
Deny a Specific IPv6 Range
const denyRange = yield* AWS.EC2.NetworkAclEntry("DenyBadActor", { networkAclId: acl.networkAclId, ruleNumber: 50, protocol: "-1", ruleAction: "deny", egress: false, ipv6CidrBlock: "2001:db8:1234::/48",});ruleAction: "deny" with a very low ruleNumber blocks an IPv6 range before
any allow rule can match it; use ipv6CidrBlock instead of cidrBlock to
target IPv6 traffic.
NetworkAclEntry: Outbound Rules
Section titled “NetworkAclEntry: Outbound Rules”Outbound rules (egress: true) match traffic leaving the subnet and are
numbered in their own sequence, independent of the inbound rules.
const allowEgress = yield* AWS.EC2.NetworkAclEntry("AllowEgress", { networkAclId: acl.networkAclId, ruleNumber: 100, protocol: "-1", ruleAction: "allow", egress: true, cidrBlock: "0.0.0.0/0",});Setting egress: true makes this an outbound rule; allowing all protocols to
0.0.0.0/0 is typical when you want the subnet to initiate connections freely.
NetworkAclEntry: ICMP Rules
Section titled “NetworkAclEntry: ICMP Rules”const allowPing = yield* AWS.EC2.NetworkAclEntry("AllowPing", { networkAclId: acl.networkAclId, ruleNumber: 300, protocol: "1", ruleAction: "allow", egress: false, cidrBlock: "10.0.0.0/16", icmpTypeCode: { type: 8, code: -1 },});ICMP (protocol: "1") has no ports, so icmpTypeCode selects the message
type instead — type 8 is echo request and code: -1 matches all codes.
NetworkInterface
Section titled “NetworkInterface”Source:
src/AWS/EC2/NetworkInterface.ts
An Elastic Network Interface (ENI) — a virtual network card in a VPC subnet
with its own private IPs, MAC address, and security groups. Attach one to an
instance via a NetworkInterfaceAttachment for stable-IP and
multi-homing patterns.
Changing subnetId or the primary privateIpAddress replaces the interface.
description, securityGroupIds, and sourceDestCheck are applied in place.
NetworkInterface: Creating a Network Interface
Section titled “NetworkInterface: Creating a Network Interface”const eni = yield* AWS.EC2.NetworkInterface("AppEni", { subnetId: subnet.subnetId, description: "stable IP for the app server", securityGroupIds: [securityGroup.groupId],});The interface gets a private IP from the subnet’s range. Its IP survives instance replacement — detach it from a failed instance and attach it to a new one to keep the same address.
NetworkInterface: Fixed Private IP
Section titled “NetworkInterface: Fixed Private IP”const eni = yield* AWS.EC2.NetworkInterface("FixedIpEni", { subnetId: subnet.subnetId, privateIpAddress: "10.0.1.50", securityGroupIds: [securityGroup.groupId],});Pinning privateIpAddress gives the interface a predictable address —
useful for appliances and services other resources reference by IP.
NetworkInterface: Forwarding Appliances
Section titled “NetworkInterface: Forwarding Appliances”const eni = yield* AWS.EC2.NetworkInterface("NatEni", { subnetId: subnet.subnetId, sourceDestCheck: false, securityGroupIds: [securityGroup.groupId],});Disable sourceDestCheck when the interface belongs to a NAT instance,
firewall, or router that forwards packets not addressed to itself.
NetworkInterfaceAttachment
Section titled “NetworkInterfaceAttachment”Source:
src/AWS/EC2/NetworkInterfaceAttachment.ts
Attaches an NetworkInterface (ENI) to an EC2 Instance at a
device index. The interface and instance must be in the same Availability
Zone. On delete the interface is detached (force-detached as a fallback)
before the resource is removed.
This is an existence-style resource — its identity is the
networkInterfaceId/instanceId/deviceIndex triple. Changing any of them
replaces the attachment.
NetworkInterfaceAttachment: Attaching a Network Interface
Section titled “NetworkInterfaceAttachment: Attaching a Network Interface”const attachment = yield* AWS.EC2.NetworkInterfaceAttachment("SecondaryEni", { networkInterfaceId: eni.networkInterfaceId, instanceId: instance.instanceId, deviceIndex: 1,});Device index 0 is the instance’s primary interface, so secondary interfaces use index 1 and up. The ENI’s IPs and security groups now apply to the instance on that interface.
PrefixList
Section titled “PrefixList”Source:
src/AWS/EC2/PrefixList.ts
A managed prefix list is a named, reusable set of CIDR blocks that you reference by ID from security group rules and route tables. Instead of duplicating the same IP ranges across many rules, you maintain them in one place and every rule that references the list picks up changes automatically.
The list is versioned: each entry modification bumps version, and AWS
limits a list to maxEntries CIDRs (you provision headroom up front and can
only grow it, never shrink it, in place). addressFamily fixes whether the
list holds IPv4 or IPv6 CIDRs and is immutable.
PrefixList: Creating a Prefix List
Section titled “PrefixList: Creating a Prefix List”Basic IPv4 Prefix List
const corpNetworks = yield* AWS.EC2.PrefixList("CorpNetworks", { maxEntries: 10, entries: [ { cidr: "10.0.0.0/16", description: "vpc-a" }, { cidr: "10.1.0.0/16", description: "vpc-b" }, ],});Creates a prefix list with two IPv4 CIDRs. The resulting prefixListId
(prefixed pl-) can be referenced from security group rules and routes.
IPv6 Prefix List
const ipv6List = yield* AWS.EC2.PrefixList("Ipv6List", { addressFamily: "IPv6", maxEntries: 5, entries: [{ cidr: "2001:db8::/32" }],});addressFamily: "IPv6" makes the list accept IPv6 CIDRs. Because the family
is intrinsic to the list, changing it later replaces the resource.
PrefixList: Referencing a Prefix List from a Security Group Rule
Section titled “PrefixList: Referencing a Prefix List from a Security Group Rule”const corpNetworks = yield* AWS.EC2.PrefixList("CorpNetworks", { maxEntries: 10, entries: [{ cidr: "10.0.0.0/16" }],});
const rule = yield* AWS.EC2.SecurityGroupRule("AllowCorp", { groupId: sg.groupId, type: "ingress", ipProtocol: "tcp", fromPort: 443, toPort: 443, prefixListId: corpNetworks.prefixListId,});The rule allows HTTPS from every CIDR in the list. Editing the list’s
entries updates what the rule permits without touching the rule itself.
RebootInstance
Section titled “RebootInstance”Source:
src/AWS/EC2/RebootInstance.ts
Runtime binding for the RebootInstances operation scoped to the bound
Instance (IAM action ec2:RebootInstances on the instance ARN).
Requests an asynchronous reboot of the instance — e.g. a remediation Lambda
that bounces a wedged host after a failed health check. Provide the
implementation with Effect.provide(AWS.EC2.RebootInstanceHttp).
RebootInstance: Instance Lifecycle Control
Section titled “RebootInstance: Instance Lifecycle Control”// init — bind the operation to the instanceconst rebootInstance = yield* AWS.EC2.RebootInstance(instance);
// runtime — request the reboot (asynchronous)yield* rebootInstance();RevokeSecurityGroupIngress
Section titled “RevokeSecurityGroupIngress”Source:
src/AWS/EC2/RevokeSecurityGroupIngress.ts
Runtime binding for the RevokeSecurityGroupIngress operation scoped to
the bound SecurityGroup (IAM action
ec2:RevokeSecurityGroupIngress on the security group ARN).
Removes an inbound rule from the group at runtime — the cleanup half of the
dynamic IP-allowlisting pattern (see
AuthorizeSecurityGroupIngress). Provide the implementation with
Effect.provide(AWS.EC2.RevokeSecurityGroupIngressHttp).
RevokeSecurityGroupIngress: Dynamic Security Group Rules
Section titled “RevokeSecurityGroupIngress: Dynamic Security Group Rules”// init — bind the operation to the security groupconst revokeIngress = yield* AWS.EC2.RevokeSecurityGroupIngress(group);
// runtime — close port 22 for the address againyield* revokeIngress({ IpProtocol: "tcp", FromPort: 22, ToPort: 22, CidrIp: "203.0.113.7/32",});Source:
src/AWS/EC2/Route.ts
A single route entry inside a RouteTable. A route maps a destination
to exactly one target, telling the VPC where to send packets whose address
falls within the destination range.
Every route has two halves:
- Destination — exactly one of
destinationCidrBlock(IPv4),destinationIpv6CidrBlock(IPv6), ordestinationPrefixListId(a managed prefix list, e.g. for an AWS service). - Target — exactly one of
gatewayId(internet/virtual private gateway),natGatewayId,instanceId(NAT instance),networkInterfaceId,vpcPeeringConnectionId,transitGatewayId,localGatewayId(Outposts),carrierGatewayId(Wavelength),egressOnlyInternetGatewayId(IPv6),coreNetworkArn(Cloud WAN), orvpcEndpointId(Gateway Load Balancer).
Changing the routeTableId or the destination replaces the route, whereas
changing only the target is applied in place via ReplaceRoute.
Route: IPv4 Routing
Section titled “Route: IPv4 Routing”Use an IPv4 destinationCidrBlock — most commonly 0.0.0.0/0 for the
default route, or a narrower CIDR to route specific traffic.
Default Route to an Internet Gateway
const internetRoute = yield* AWS.EC2.Route("InternetRoute", { routeTableId: publicRouteTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", gatewayId: internetGateway.internetGatewayId,});Sends all outbound IPv4 traffic to the internet gateway, which is what makes a subnet “public”. Attach this route table to any subnet that needs inbound and outbound internet connectivity.
Default Route to a NAT Gateway
const natRoute = yield* AWS.EC2.Route("NatRoute", { routeTableId: privateRouteTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", natGatewayId: natGateway.natGatewayId,});Lets private subnets reach the internet for outbound traffic (package updates, API calls) while blocking unsolicited inbound connections. The NAT gateway itself lives in a public subnet.
Route to a VPC Peering Connection
const peeringRoute = yield* AWS.EC2.Route("PeeringRoute", { routeTableId: routeTable.routeTableId, destinationCidrBlock: "10.1.0.0/16", vpcPeeringConnectionId: "pcx-0abc1234",});Routes traffic destined for the peer VPC’s CIDR across a VPC peering
connection. Use a narrow destination matching the remote VPC rather than
0.0.0.0/0 so only cross-VPC traffic is affected.
Route to a Transit Gateway
const transitRoute = yield* AWS.EC2.Route("TransitRoute", { routeTableId: routeTable.routeTableId, destinationCidrBlock: "172.16.0.0/12", transitGatewayId: "tgw-0abc1234",});Hands traffic to a transit gateway, the hub used to connect many VPCs and on-premises networks. The destination CIDR should cover the address space reachable through the transit gateway.
Route to a Network Interface or NAT Instance
const applianceRoute = yield* AWS.EC2.Route("ApplianceRoute", { routeTableId: routeTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", networkInterfaceId: "eni-0abc1234",});Forwards traffic to a specific elastic network interface — for example a
firewall or NAT instance appliance. Use instanceId instead when targeting
a NAT instance that has exactly one network interface attached.
Route: IPv6 Routing
Section titled “Route: IPv6 Routing”IPv6 routes use destinationIpv6CidrBlock (e.g. ::/0 for the IPv6 default
route). For outbound-only IPv6 access from private subnets, target an
EgressOnlyInternetGateway.
IPv6 Egress Route to an Egress-Only Internet Gateway
const ipv6EgressRoute = yield* AWS.EC2.Route("Ipv6EgressRoute", { routeTableId: privateRouteTable.routeTableId, destinationIpv6CidrBlock: "::/0", egressOnlyInternetGatewayId: egressOnlyIgw.egressOnlyInternetGatewayId,});Gives IPv6-addressed instances outbound internet access while blocking inbound connections — the IPv6 equivalent of routing IPv4 through a NAT gateway.
IPv6 Internet Route to an Internet Gateway
const ipv6InternetRoute = yield* AWS.EC2.Route("Ipv6InternetRoute", { routeTableId: publicRouteTable.routeTableId, destinationIpv6CidrBlock: "::/0", gatewayId: internetGateway.internetGatewayId,});Provides full bidirectional IPv6 connectivity for a public subnet, since an internet gateway (unlike an egress-only gateway) allows inbound IPv6 traffic.
Route: Routing to AWS Services via Prefix Lists
Section titled “Route: Routing to AWS Services via Prefix Lists”Instead of a raw CIDR, a route can match a managed prefix list — useful for AWS service ranges (e.g. an S3 gateway endpoint) where the underlying CIDRs change over time.
const prefixListRoute = yield* AWS.EC2.Route("S3PrefixRoute", { routeTableId: privateRouteTable.routeTableId, destinationPrefixListId: "pl-0abc1234", vpcEndpointId: "vpce-0abc1234",});Routes traffic for every CIDR in the prefix list to a Gateway Load Balancer VPC endpoint. AWS keeps the prefix list current, so you don’t have to update the route when the service’s address ranges change.
RouteTable
Section titled “RouteTable”Source:
src/AWS/EC2/RouteTable.ts
A VPC route table holds a set of routes that determine where network
traffic from associated subnets (or gateways) is directed. Create one
route table per routing domain — typically a “public” table whose default
route points at an InternetGateway, and one or more “private” tables
whose default route points at a NAT gateway.
A route table is little more than a container: it owns a vpcId and tags,
while the actual routing behaviour is supplied by separate Route
resources and applied to subnets by RouteTableAssociation resources.
RouteTable: Creating a Route Table
Section titled “RouteTable: Creating a Route Table”The only required input is the vpcId the table belongs to. Changing
vpcId later replaces the route table, since a table cannot move between
VPCs.
Basic Route Table
const routeTable = yield* AWS.EC2.RouteTable("PublicRouteTable", { vpcId: myVpc.vpcId,});Creates an empty route table in the given VPC. It starts with only the
implicit local route (managed by AWS) until you add your own
Route resources.
Route Table with Tags
const routeTable = yield* AWS.EC2.RouteTable("PrivateRouteTable", { vpcId: myVpc.vpcId, tags: { Name: "private-rt", Tier: "private" },});The tags map is merged with the alchemy auto-tags (alchemy::stack,
alchemy::stage, alchemy::id) and can be updated in place without
replacing the table. Use the Name tag to label the table in the AWS
console.
RouteTable: Building a Public Routing Domain
Section titled “RouteTable: Building a Public Routing Domain”A route table only directs traffic once you attach routes to it and
associate it with subnets. The pattern below wires a public subnet to the
internet: an InternetGateway, a default Route pointing at it,
and a RouteTableAssociation binding the subnet to the table.
const internetGateway = yield* AWS.EC2.InternetGateway("InternetGateway", { vpcId: myVpc.vpcId,});
const publicRouteTable = yield* AWS.EC2.RouteTable("PublicRouteTable", { vpcId: myVpc.vpcId,});
const internetRoute = yield* AWS.EC2.Route("InternetRoute", { routeTableId: publicRouteTable.routeTableId, destinationCidrBlock: "0.0.0.0/0", gatewayId: internetGateway.internetGatewayId,});
const association = yield* AWS.EC2.RouteTableAssociation("PublicSubnetAssociation", { routeTableId: publicRouteTable.routeTableId, subnetId: publicSubnet.subnetId,});Any subnet associated with this table now reaches the public internet via
the 0.0.0.0/0 route. Multiple subnets can share the same route table by
declaring additional associations — a common way to give every public
subnet in a VPC identical routing.
RouteTableAssociation
Section titled “RouteTableAssociation”Source:
src/AWS/EC2/RouteTableAssociation.ts
Associates a RouteTable with a subnet (or a gateway), making that
route table govern traffic for the associated resource. A subnet can be
associated with exactly one route table at a time; multiple subnets may share
the same route table.
Provide exactly one of subnetId or gatewayId. Changing the subnet or
gateway replaces the association, whereas pointing an existing association at
a different route table is applied in place via
ReplaceRouteTableAssociation.
RouteTableAssociation: Associating Subnets
Section titled “RouteTableAssociation: Associating Subnets”Associating a subnet overrides the VPC’s main route table for that subnet. This is how you make a subnet “public” (associate it with a table that has an internet-gateway route) or “private” (associate it with a NAT-gateway table).
Associate a Subnet with a Route Table
const association = yield* AWS.EC2.RouteTableAssociation("PublicSubnetAssociation", { routeTableId: publicRouteTable.routeTableId, subnetId: publicSubnet.subnetId,});Binds a single subnet to the route table so its instances follow that
table’s routes. The returned associationId (prefixed rtbassoc-) can be
used to track or replace the association.
Share One Route Table Across Multiple Subnets
const subnet1Association = yield* AWS.EC2.RouteTableAssociation("PublicSubnet1Association", { routeTableId: publicRouteTable.routeTableId, subnetId: publicSubnet1.subnetId,});
const subnet2Association = yield* AWS.EC2.RouteTableAssociation("PublicSubnet2Association", { routeTableId: publicRouteTable.routeTableId, subnetId: publicSubnet2.subnetId,});Declaring multiple associations against the same routeTableId gives every
listed subnet identical routing — a concise way to apply one public (or
private) routing policy across all subnets in a tier.
RouteTableAssociation: Associating Gateways (Edge Routing)
Section titled “RouteTableAssociation: Associating Gateways (Edge Routing)”Instead of a subnet, an association can target an internet gateway or
virtual private gateway via gatewayId. This “gateway route table
association” enables edge routing, where inbound traffic is inspected or
redirected (e.g. to a firewall appliance) as it enters the VPC.
const edgeAssociation = yield* AWS.EC2.RouteTableAssociation("EdgeAssociation", { routeTableId: ingressRouteTable.routeTableId, gatewayId: internetGateway.internetGatewayId,});Attaches the route table at the gateway rather than at a subnet, so traffic arriving from the internet is steered by this table — typically toward an inspection appliance before reaching its destination subnet.
SecurityGroup
Section titled “SecurityGroup”Source:
src/AWS/EC2/SecurityGroup.ts
An EC2 security group — a stateful virtual firewall that controls inbound
(ingress) and outbound (egress) traffic for resources in a VPC. Rules can
allow traffic from CIDR ranges, IPv6 ranges, managed prefix lists, or other
security groups. Because it is stateful, return traffic for an allowed
connection is permitted automatically regardless of the opposite-direction
rules.
If no egress rules are specified, all outbound traffic is allowed by
default. Changing the vpcId or groupName replaces the security group.
Inline rules are authoritative: undeclared rules are removed even if they
carry Alchemy tags. Standalone SecurityGroupRule resources declared in the
same stack and stage retain ownership of their persisted physical rule IDs;
their own providers manage their updates and deletion. Cloud tags alone do
not establish ownership.
SecurityGroup: Creating a Security Group
Section titled “SecurityGroup: Creating a Security Group”Every security group belongs to a VPC. groupName and description are
optional — alchemy generates a deterministic name and a default description
when they are omitted. Both the VPC and the name are immutable, so changing
either replaces the group.
Empty Security Group
const sg = yield* AWS.EC2.SecurityGroup("AppSg", { vpcId: vpc.vpcId,});With no rules, this group denies all inbound traffic and (since no egress is
given) allows all outbound. It’s a useful starting point you attach rules to
later, or a target other groups can reference.
Named group with a description
const sg = yield* AWS.EC2.SecurityGroup("AppSg", { vpcId: vpc.vpcId, groupName: "app-tier", description: "Application tier security group",});Set an explicit groupName when you need a stable, human-readable identifier
(for example to reference the group by name elsewhere). The description is
shown in the EC2 console and cannot be changed after creation.
SecurityGroup: Ingress Rules
Section titled “SecurityGroup: Ingress Rules”Inbound rules are declared inline via ingress. Each rule specifies an
ipProtocol (tcp, udp, icmp, or -1 for all), an optional port range
(fromPort/toPort), and a source — most commonly an IPv4 cidrIpv4.
const webSg = yield* AWS.EC2.SecurityGroup("WebSecurityGroup", { vpcId: vpc.vpcId, description: "Web tier security group", ingress: [ { ipProtocol: "tcp", fromPort: 80, toPort: 80, cidrIpv4: "0.0.0.0/0", description: "Allow HTTP", }, { ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "0.0.0.0/0", description: "Allow HTTPS", }, ], tags: { Name: "web-sg" },});Two rules open the standard web ports to the whole internet (0.0.0.0/0).
Setting fromPort equal to toPort opens a single port; widen the range to
open a contiguous span.
SecurityGroup: Egress Rules
Section titled “SecurityGroup: Egress Rules”Outbound traffic is governed by egress. If you omit it entirely, the group
enforces the default “allow all outbound” rule. Supplying egress replaces
that default with exactly the rules you list — so you must re-add an
allow-all rule if you still want unrestricted outbound.
const lockedSg = yield* AWS.EC2.SecurityGroup("LockedSg", { vpcId: vpc.vpcId, description: "Outbound restricted to HTTPS", egress: [ { ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "0.0.0.0/0", description: "Allow outbound HTTPS", }, ],});This locks egress down to port 443 only — useful for instances that should only call out to HTTPS APIs. Any other outbound traffic (DNS, NTP, etc.) would need explicit rules added here.
SecurityGroup: Updating Inline Rules
Section titled “SecurityGroup: Updating Inline Rules”Changing a rule description updates the existing physical rule in place. Adding or removing a rule leaves unrelated rule IDs unchanged. Redeploying unchanged code repairs missing rules and removes undeclared rules.
const sg = yield* AWS.EC2.SecurityGroup("AppSg", { vpcId: vpc.vpcId, ingress: [{ ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "10.0.0.0/16", description: "HTTPS", description: "Internal HTTPS", }],});Removing description resets it to an empty description without replacing
the rule.
SecurityGroup: Restoring Default Rules
Section titled “SecurityGroup: Restoring Default Rules”Omitting ingress restores no inbound rules. Omitting egress restores the
default IPv4 allow-all outbound rule; egress: [] disables outbound traffic.
These defaults apply on creation, property removal, and drift repair.
const sg = yield* AWS.EC2.SecurityGroup("AppSg", { vpcId: vpc.vpcId, egress: [{ ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "0.0.0.0/0", }],});SecurityGroup: Composing Standalone Rules
Section titled “SecurityGroup: Composing Standalone Rules”Standalone rules must be declared in the same stack and stage as this group.
Pass the whole resource as group to order creation and updates after inline
reconciliation. The ID-only groupId form remains supported, but a stable ID
alone does not order concurrent inline updates. Ownership is verified against
each current declaration’s persisted physical rule ID.
Removing a declaration ends that ownership; tags alone do not protect rules.
Cross-stack or cross-stage rule ownership is unsupported: this group removes
rules declared elsewhere when reconciling its authoritative configuration.
Inline and standalone rules must have distinct identities. If a standalone
rule owns IPv4 allow-all egress, set egress: [] to disable the inline default.
yield* AWS.EC2.SecurityGroupRule("HttpsIngress", { group: sg, type: "ingress", ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "10.0.0.0/16",});SecurityGroup: Referencing Other Groups
Section titled “SecurityGroup: Referencing Other Groups”Instead of a CIDR, a rule’s source can be another security group via
referencedGroupId. This is the idiomatic way to express tier-to-tier trust
(“the database accepts connections from anything in the app tier”) without
pinning IP addresses.
const dbSg = yield* AWS.EC2.SecurityGroup("DbSecurityGroup", { vpcId: vpc.vpcId, description: "Database tier security group", ingress: [ { ipProtocol: "tcp", fromPort: 5432, toPort: 5432, referencedGroupId: webSg.groupId, description: "Allow PostgreSQL from web tier", }, ], tags: { Name: "db-sg" },});Only instances in webSg can reach PostgreSQL on this group, regardless of
their IPs. As the web tier scales up and down, the rule keeps working without
any change.
SecurityGroup: IPv6, Prefix Lists & ICMP
Section titled “SecurityGroup: IPv6, Prefix Lists & ICMP”Beyond IPv4 CIDRs, a rule source can be an IPv6 range (cidrIpv6) or a managed
prefix list (prefixListId). For ICMP, set ipProtocol: "icmp" and use
fromPort/toPort as the ICMP type and code (-1 for all).
const sg = yield* AWS.EC2.SecurityGroup("EdgeSg", { vpcId: vpc.vpcId, description: "Edge security group", ingress: [ { ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv6: "::/0", description: "Allow HTTPS over IPv6", }, { ipProtocol: "tcp", fromPort: 22, toPort: 22, prefixListId: "pl-0123456789abcdef0", description: "Allow SSH from corporate prefix list", }, { ipProtocol: "icmp", fromPort: -1, toPort: -1, cidrIpv4: "10.0.0.0/16", description: "Allow all ICMP from within the VPC", }, ],});Prefix lists let you reference a centrally-maintained set of CIDRs (e.g. your
corporate egress IPs) by ID, so the rule updates automatically as the list
changes. The ICMP rule with type/code -1 permits ping and other ICMP within
the VPC.
SecurityGroupRule
Section titled “SecurityGroupRule”Source:
src/AWS/EC2/SecurityGroupRule.ts
A single ingress or egress rule attached to an existing security group, managed as a standalone resource. Use this when you want to manage a security group’s rules independently of its inline rules, or to add rules to a group not managed by an Alchemy SecurityGroup resource.
Declare the group and rule in the same stack and stage, passing the whole
resource as group to order rule operations after inline reconciliation.
groupId remains supported for ID-only callers, but its stable value alone
does not order concurrent inline updates. Specify exactly one input form;
switching forms with the same physical ID does not replace the rule.
The group recognizes the current declaration and its persisted physical
rule ID, not cloud tags. Rules in another stack or stage are not protected
from the group’s reconciliation;
cross-stack ownership is unsupported.
Changes to protocol, ports, source, or type props replace the rule.
External edits to protocol, ports, source, description, and tags are repaired
on unchanged deployment. Description and tag prop changes update in place.
SecurityGroupRule: Ingress vs Egress
Section titled “SecurityGroupRule: Ingress vs Egress”The type field decides the direction: "ingress" for inbound rules and
"egress" for outbound. Everything else (protocol, ports, source) is shared
between the two directions.
Inbound HTTPS from anywhere
const httpsRule = yield* AWS.EC2.SecurityGroupRule("HttpsIngress", { group: sg, type: "ingress", ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv4: "0.0.0.0/0", description: "Allow HTTPS",});Opens TCP 443 inbound from the entire internet on the target group. A single
port is expressed by setting fromPort and toPort to the same value.
Outbound to a database port
const egressRule = yield* AWS.EC2.SecurityGroupRule("DbEgress", { group: sg, type: "egress", ipProtocol: "tcp", fromPort: 5432, toPort: 5432, cidrIpv4: "10.0.0.0/16", description: "Allow PostgreSQL to the VPC",});This rule allows outbound PostgreSQL within the VPC CIDR. Set egress: []
on the parent group to remove its default allow-all-outbound rule; adding a
standalone egress rule does not remove other rules.
SecurityGroupRule: Rule Sources
Section titled “SecurityGroupRule: Rule Sources”A rule’s source (for ingress) or destination (for egress) is exactly one of:
an IPv4 CIDR (cidrIpv4), an IPv6 CIDR (cidrIpv6), another security group
(referencedGroupId), or a managed prefix list (prefixListId).
Allow traffic from another security group
const dbFromWeb = yield* AWS.EC2.SecurityGroupRule("DbFromWeb", { group: dbSg, type: "ingress", ipProtocol: "tcp", fromPort: 5432, toPort: 5432, referencedGroupId: webSg.groupId, description: "Allow PostgreSQL from web tier",});Referencing webSg rather than a CIDR means any instance in the web tier can
reach the database, even as the tier’s IPs change. This is the preferred way
to wire trust between tiers.
Allow an IPv6 range
const ipv6Rule = yield* AWS.EC2.SecurityGroupRule("HttpsIpv6", { group: sg, type: "ingress", ipProtocol: "tcp", fromPort: 443, toPort: 443, cidrIpv6: "::/0", description: "Allow HTTPS over IPv6",});Use cidrIpv6 for dual-stack workloads; ::/0 is the IPv6 equivalent of
0.0.0.0/0. IPv4 and IPv6 are separate rules — you’d pair this with a
cidrIpv4 rule to cover both.
Allow from a managed prefix list
const sshRule = yield* AWS.EC2.SecurityGroupRule("SshFromCorp", { group: sg, type: "ingress", ipProtocol: "tcp", fromPort: 22, toPort: 22, prefixListId: "pl-0123456789abcdef0", description: "Allow SSH from the corporate prefix list",});A prefixListId references a centrally-managed set of CIDRs by ID, so the
rule’s effective ranges update automatically whenever the prefix list does.
SecurityGroupRule: Protocols & Ports
Section titled “SecurityGroupRule: Protocols & Ports”ipProtocol accepts tcp, udp, icmp/icmpv6, a protocol number, or -1
for all protocols. For ICMP, fromPort is the ICMP type and toPort is the
ICMP code, with -1 meaning “all”.
Allow all traffic from a trusted CIDR
const allRule = yield* AWS.EC2.SecurityGroupRule("AllFromVpc", { group: sg, type: "ingress", ipProtocol: "-1", cidrIpv4: "10.0.0.0/16", description: "Allow all protocols from within the VPC",});With ipProtocol: "-1", ports are ignored and every protocol is permitted —
appropriate only for fully trusted sources such as your own VPC CIDR.
Allow ICMP echo (ping)
const icmpRule = yield* AWS.EC2.SecurityGroupRule("AllowPing", { group: sg, type: "ingress", ipProtocol: "icmp", fromPort: 8, toPort: 0, cidrIpv4: "10.0.0.0/16", description: "Allow ICMP echo request",});For ICMP the port fields carry the type and code: type 8 / code 0 is an
echo request (ping). Use fromPort: -1, toPort: -1 to allow every ICMP
type/code instead.
Snapshot
Section titled “Snapshot”Source:
src/AWS/EC2/Snapshot.ts
A point-in-time backup of an EBS Volume, stored in S3. Snapshots are
incremental and immutable — you create new Volumes from them via
snapshotId.
A snapshot is immutable once created; changing the source volumeId replaces
it. Creation is asynchronous — the resource waits for the snapshot to reach
the completed state before returning.
Snapshot: Creating a Snapshot
Section titled “Snapshot: Creating a Snapshot”const snapshot = yield* AWS.EC2.Snapshot("DailyBackup", { volumeId: volume.volumeId, description: "nightly backup of the data volume",});The snapshot captures the volume’s state at creation time. Because snapshots are incremental, only blocks changed since the previous snapshot of the same volume are stored.
Snapshot: Restoring from a Snapshot
Section titled “Snapshot: Restoring from a Snapshot”const restored = yield* AWS.EC2.Volume("Restored", { availabilityZone: "us-east-1a", snapshotId: snapshot.snapshotId,});Pass a snapshot’s snapshotId to Volume to provision a new volume
pre-populated with the snapshot’s data — the standard backup/restore and
clone-across-AZ pattern.
StartInstance
Section titled “StartInstance”Source:
src/AWS/EC2/StartInstance.ts
Runtime binding for the StartInstances operation scoped to the bound
Instance (IAM action ec2:StartInstances on the instance ARN).
Starts the stopped instance — the classic scheduled Lambda that powers dev
boxes on in the morning. Starting an already-running instance succeeds
without effect. Provide the implementation with
Effect.provide(AWS.EC2.StartInstanceHttp).
StartInstance: Instance Lifecycle Control
Section titled “StartInstance: Instance Lifecycle Control”// init — bind the operation to the instanceconst startInstance = yield* AWS.EC2.StartInstance(instance);
// runtime — power the instance onconst result = yield* startInstance();console.log(result.StartingInstances?.[0]?.CurrentState?.Name);StopInstance
Section titled “StopInstance”Source:
src/AWS/EC2/StopInstance.ts
Runtime binding for the StopInstances operation scoped to the bound
Instance (IAM action ec2:StopInstances on the instance ARN).
Stops the running instance — the other half of the scheduled
start/stop-Lambda pattern that powers dev fleets off overnight. Pass
Hibernate: true for hibernation-enabled instances. Provide the
implementation with Effect.provide(AWS.EC2.StopInstanceHttp).
StopInstance: Instance Lifecycle Control
Section titled “StopInstance: Instance Lifecycle Control”// init — bind the operation to the instanceconst stopInstance = yield* AWS.EC2.StopInstance(instance);
// runtime — power the instance offconst result = yield* stopInstance();console.log(result.StoppingInstances?.[0]?.CurrentState?.Name);Subnet
Section titled “Subnet”Source:
src/AWS/EC2/Subnet.ts
A subnet within a VPC — a range of IP addresses bound to a single
Availability Zone where you place instances and other resources. Create
public subnets (with mapPublicIpOnLaunch) for internet-facing resources
and private subnets for internal ones.
Changing the vpcId, cidrBlock, availability zone, or an IPAM/IPv6 pool
replaces the subnet.
Subnet: Creating a Subnet
Section titled “Subnet: Creating a Subnet”A subnet carves a smaller CIDR range out of its parent VPC’s block. The
cidrBlock must be a subset of the VPC CIDR and must not overlap any sibling
subnet. You can also let AWS IPAM allocate the range via ipv4IpamPoolId +
ipv4NetmaskLength.
const subnet = yield* AWS.EC2.Subnet("TestSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24",});The minimal subnet: a /24 (256 addresses) inside the VPC. Without an
explicit availabilityZone, AWS picks one for you.
Subnet: Availability Zone Placement
Section titled “Subnet: Availability Zone Placement”Each subnet lives in exactly one AZ. Pin it with availabilityZone (the zone
name, e.g. us-east-1a) or availabilityZoneId (the stable zone ID, e.g.
use1-az1) to spread tiers across zones for high availability.
const subnet = yield* AWS.EC2.Subnet("Az1Subnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", availabilityZone: "us-east-1a",});Pinning the AZ lets you place a matching subnet in us-east-1b and run
resources redundantly across zones. Use availabilityZoneId instead when you
need the physical zone to line up across different AWS accounts.
Subnet: Public IP Assignment
Section titled “Subnet: Public IP Assignment”const publicSubnet = yield* AWS.EC2.Subnet("PublicSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", availabilityZone: "us-east-1a", mapPublicIpOnLaunch: true, tags: { Name: "public-1a", Tier: "public" },});mapPublicIpOnLaunch: true makes this a “public” subnet — instances launched
here automatically get a public IPv4 address. Combine it with an internet
gateway route so those instances can reach the internet.
Subnet: IPv6 Subnets
Section titled “Subnet: IPv6 Subnets”For dual-stack VPCs, give the subnet an IPv6 cidrBlock, auto-assign IPv6
addresses on launch with assignIpv6AddressOnCreation, and optionally enable
enableDns64 so the Amazon DNS resolver synthesizes IPv6 addresses for
IPv4-only destinations (NAT64).
const subnet = yield* AWS.EC2.Subnet("Ipv6Subnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", ipv6CidrBlock: "2600:1f18:abcd:1234::/64", assignIpv6AddressOnCreation: true, enableDns64: true,});Instances launched here receive an IPv6 address automatically, and enableDns64
lets them reach IPv4-only services through a NAT gateway. The IPv6 /64 must
come from the parent VPC’s IPv6 block.
Subnet: DNS Hostname Options
Section titled “Subnet: DNS Hostname Options”Control what hostnames instances receive on launch. hostnameType chooses
between IP-based names (ip-name) and resource-based names (resource-name),
and the enableResourceNameDnsARecordOnLaunch /
enableResourceNameDnsAAAARecordOnLaunch flags register A / AAAA records for
resource-name hosts.
const subnet = yield* AWS.EC2.Subnet("ResourceNameSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", hostnameType: "resource-name", enableResourceNameDnsARecordOnLaunch: true, enableResourceNameDnsAAAARecordOnLaunch: true,});Resource-name hostnames are derived from the instance ID rather than its IP, so they stay stable across stop/start. Enabling the A/AAAA records makes those names resolvable over IPv4 and IPv6.
Subnet: Public & Private Tiers
Section titled “Subnet: Public & Private Tiers”const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", enableDnsSupport: true, enableDnsHostnames: true,});
const publicSubnet = yield* AWS.EC2.Subnet("PublicSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", availabilityZone: "us-east-1a", mapPublicIpOnLaunch: true,});
const privateSubnet = yield* AWS.EC2.Subnet("PrivateSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.10.0/24", availabilityZone: "us-east-1a",});The canonical two-tier pattern: a public subnet (auto public IPs, routed to an internet gateway) for load balancers and a private subnet (no public IPs) for application and database instances. Both share the same AZ here, but in production you’d replicate the pair across AZs.
Volume
Section titled “Volume”Source:
src/AWS/EC2/Volume.ts
An Elastic Block Store (EBS) volume — durable block storage you attach to an
EC2 instance via a VolumeAttachment. Volumes live in a single
Availability Zone and persist independently of any instance.
Changing availabilityZone, encrypted, kmsKeyId, snapshotId, or
shrinking size replaces the volume. Growing size and changing iops,
throughput, or volumeType are applied in place via modifyVolume (note
AWS enforces a 6-hour cooldown between volume modifications).
Volume: Creating a Volume
Section titled “Volume: Creating a Volume”const volume = yield* AWS.EC2.Volume("DataVolume", { availabilityZone: "us-east-1a", size: 20, volumeType: "gp3",});The minimal volume: a 20 GiB general-purpose gp3 volume in one AZ. It must
be in the same AZ as the instance you attach it to.
Volume: Provisioned Performance
Section titled “Volume: Provisioned Performance”const fast = yield* AWS.EC2.Volume("FastVolume", { availabilityZone: "us-east-1a", size: 100, volumeType: "gp3", iops: 6000, throughput: 250,});gp3 decouples IOPS and throughput from size, so you can provision up to
16,000 IOPS and 1,000 MiB/s independently. Use io2 for the highest
durability and IOPS ceilings.
Volume: Encryption
Section titled “Volume: Encryption”const secure = yield* AWS.EC2.Volume("SecureVolume", { availabilityZone: "us-east-1a", size: 20, encrypted: true, kmsKeyId: "alias/my-app-key",});Setting kmsKeyId implies encryption. Omit it while setting encrypted: true to use the account’s default EBS KMS key.
Volume: Creating from a Snapshot
Section titled “Volume: Creating from a Snapshot”const restored = yield* AWS.EC2.Volume("RestoredVolume", { availabilityZone: "us-east-1a", snapshotId: snapshot.snapshotId,});When you create a volume from a snapshot, size defaults to the snapshot’s
size and can only be grown, never shrunk.
VolumeAttachment
Section titled “VolumeAttachment”Source:
src/AWS/EC2/VolumeAttachment.ts
Attaches an EBS Volume to an EC2 Instance at a device name.
The volume and instance must be in the same Availability Zone. On delete the
volume is detached (and force-detached as a fallback) before the resource is
removed.
This is an existence-style resource — its identity is the
volumeId/instanceId/device triple. Changing any of them replaces the
attachment.
VolumeAttachment: Attaching a Volume
Section titled “VolumeAttachment: Attaching a Volume”const attachment = yield* AWS.EC2.VolumeAttachment("DataAttachment", { volumeId: volume.volumeId, instanceId: instance.instanceId, device: "/dev/sdf",});The volume appears to the instance as a block device at device. On modern
Linux AMIs the kernel may rename /dev/sdf to /dev/xvdf — check
lsblk inside the instance. The volume and instance must share an AZ.
Source:
src/AWS/EC2/Vpc.ts
An Amazon VPC (Virtual Private Cloud) — an isolated virtual network that is the root of any custom AWS networking topology. Subnets, route tables, gateways, security groups, and instances are all created inside a VPC.
Changing the cidrBlock, instanceTenancy, or an IPAM/IPv6 pool replaces
the VPC.
Vpc: Creating a VPC
Section titled “Vpc: Creating a VPC”A VPC is defined by a private IPv4 address range (cidrBlock). Pick a block
from the RFC 1918 private space (e.g. 10.0.0.0/16) that is large enough to
subdivide into subnets across your Availability Zones.
Basic VPC
const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16",});A /16 gives you 65,536 addresses to carve into subnets — enough headroom
for a multi-AZ, multi-tier network. This is the minimal config every other
networking resource builds on.
Allocating IPv4 from an IPAM pool
const vpc = yield* AWS.EC2.Vpc("MyVpc", { ipv4IpamPoolId: "ipam-pool-0123456789abcdef0", ipv4NetmaskLength: 16,});Instead of hard-coding cidrBlock, let AWS IPAM hand out a non-overlapping
range of the requested size. Use this when an organization centrally manages
address space to avoid CIDR collisions between accounts.
Vpc: DNS Resolution
Section titled “Vpc: DNS Resolution”Two independent toggles control DNS behavior inside the VPC. enableDnsSupport
lets instances resolve names via the Amazon DNS server; enableDnsHostnames
additionally assigns public DNS hostnames to instances with public IPs.
const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", enableDnsSupport: true, enableDnsHostnames: true,});Enable both when instances need public DNS names or when you rely on private hosted zones and VPC endpoints, which require DNS resolution to function.
Vpc: Instance Tenancy
Section titled “Vpc: Instance Tenancy”const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", instanceTenancy: "dedicated",});Forcing "dedicated" tenancy ensures every instance launched in the VPC runs
on single-tenant hardware — required by some compliance regimes, but more
expensive than the "default" shared tenancy. This property cannot be
changed after creation without replacing the VPC.
Vpc: IPv6 Addressing
Section titled “Vpc: IPv6 Addressing”A VPC can carry an IPv6 /56 block alongside its IPv4 range. The block can
come from Amazon’s pool, an IPAM pool, or your own BYOIP pool
(ipv6CidrBlock + ipv6Pool, optionally scoped to a
ipv6CidrBlockNetworkBorderGroup).
Amazon-provided IPv6 block
const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", amazonProvidedIpv6CidrBlock: true,});Requests an Amazon-assigned IPv6 /56, the simplest way to make a VPC
dual-stack. Pair it with IPv6-enabled subnets and an egress-only internet
gateway for outbound-only IPv6 connectivity.
IPv6 from an IPAM pool
const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", ipv6IpamPoolId: "ipam-pool-0fedcba9876543210", ipv6NetmaskLength: 56,});Draws the IPv6 block from a centrally-managed IPAM pool instead of Amazon’s pool, giving you deterministic, organization-governed IPv6 ranges.
Vpc: Composing a Network
Section titled “Vpc: Composing a Network”const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", enableDnsSupport: true, enableDnsHostnames: true,});
const subnet = yield* AWS.EC2.Subnet("PublicSubnet", { vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", availabilityZone: "us-east-1a", mapPublicIpOnLaunch: true,});Passing vpc.vpcId into a Subnet is how you build out a topology — the
subnet’s CIDR must fall within the VPC’s cidrBlock. Add route tables,
gateways, and security groups the same way.
Vpc: Tagging
Section titled “Vpc: Tagging”const vpc = yield* AWS.EC2.Vpc("MyVpc", { cidrBlock: "10.0.0.0/16", tags: { Name: "production-vpc", Environment: "production", },});User tags are merged with alchemy’s auto-tags (alchemy::stack,
alchemy::stage, alchemy::id), which brand the VPC as managed by your
stack. The Name tag is what surfaces in the EC2 console.
VpcEndpoint
Section titled “VpcEndpoint”Source:
src/AWS/EC2/VpcEndpoint.ts
A VPC endpoint that connects your VPC privately to an AWS service (or a service behind a Gateway Load Balancer) without traversing the public internet, a NAT gateway, or an internet gateway.
The vpcEndpointType selects how the connection is realized:
"Gateway"— for S3 and DynamoDB; traffic is directed by adding routes to the route tables inrouteTableIds(no hourly cost)."Interface"— for most other AWS services; provisions elastic network interfaces insubnetIds, guarded bysecurityGroupIds, and optionally resolves the service’s public DNS name privately viaprivateDnsEnabled."GatewayLoadBalancer"— routes traffic through a third-party appliance fleet fronted by a Gateway Load Balancer.
Changing vpcId, serviceName, or vpcEndpointType replaces the endpoint;
route tables, subnets, security groups, DNS, and the policy update in place.
VpcEndpoint: Gateway Endpoints
Section titled “VpcEndpoint: Gateway Endpoints”Gateway endpoints target S3 and DynamoDB and work by injecting a prefix-list route into each route table you list, so requests to the service stay on the AWS network.
const s3Endpoint = yield* AWS.EC2.VpcEndpoint("S3Endpoint", { vpcId: vpc.vpcId, serviceName: "com.amazonaws.us-east-1.s3", vpcEndpointType: "Gateway", routeTableIds: [privateRouteTable.routeTableId], tags: { Name: "s3-endpoint" },});Listing the private subnets’ route tables in routeTableIds lets those
subnets reach S3 directly, removing NAT data-processing charges for S3 traffic
and keeping it off the public internet.
VpcEndpoint: Interface Endpoints
Section titled “VpcEndpoint: Interface Endpoints”Interface endpoints place an ENI in each chosen subnet and are reached over private IPs; enabling private DNS lets existing SDK calls resolve to the endpoint transparently.
const secretsEndpoint = yield* AWS.EC2.VpcEndpoint("SecretsEndpoint", { vpcId: vpc.vpcId, serviceName: "com.amazonaws.us-east-1.secretsmanager", vpcEndpointType: "Interface", subnetIds: [privateSubnet.subnetId], securityGroupIds: [endpointSecurityGroup.groupId], privateDnsEnabled: true, ipAddressType: "ipv4", dnsOptions: { dnsRecordIpType: "ipv4", },});The endpoint gets an interface in each subnetIds entry, securityGroupIds
controls who may reach those interfaces, and privateDnsEnabled: true makes
the service’s default DNS name resolve to the endpoint; ipAddressType and
dnsOptions tune the IP family used for the interfaces and their DNS records.
VpcEndpoint: Restricting Access with a Policy
Section titled “VpcEndpoint: Restricting Access with a Policy”const s3Endpoint = yield* AWS.EC2.VpcEndpoint("RestrictedS3Endpoint", { vpcId: vpc.vpcId, serviceName: "com.amazonaws.us-east-1.s3", vpcEndpointType: "Gateway", routeTableIds: [privateRouteTable.routeTableId], policyDocument: JSON.stringify({ Version: "2012-10-17", Statement: [ { Effect: "Allow", Principal: "*", Action: ["s3:GetObject"], Resource: ["arn:aws:s3:::my-bucket/*"], }, ], }),});policyDocument attaches an endpoint policy (JSON) that constrains which
service actions and resources can be reached through the endpoint; omit it to
allow full access to the service.
VpcPeeringConnection
Section titled “VpcPeeringConnection”Source:
src/AWS/EC2/VpcPeeringConnection.ts
A VPC peering connection links two VPCs so resources in each can communicate using private IP addresses, as if they were on the same network. The two VPCs can be in the same account or different accounts, and the same Region or different Regions. Their CIDR blocks must not overlap.
A peering connection is a two-sided handshake: a requester VPC creates the
request and an accepter VPC accepts it. For same-account, same-Region
peering alchemy accepts the request for you automatically (autoAccept
defaults to true); for cross-account or cross-Region peering the connection
is left in pending-acceptance for the peer to accept out of band. Once
active, add Routes on both sides pointing the peer CIDR at the
connection to actually carry traffic.
VpcPeeringConnection: Creating a Peering Connection
Section titled “VpcPeeringConnection: Creating a Peering Connection”Same-Account Peering (auto-accepted)
const vpcA = yield* AWS.EC2.Vpc("VpcA", { cidrBlock: "10.0.0.0/16" });const vpcB = yield* AWS.EC2.Vpc("VpcB", { cidrBlock: "10.1.0.0/16" });
const peering = yield* AWS.EC2.VpcPeeringConnection("Peering", { vpcId: vpcA.vpcId, peerVpcId: vpcB.vpcId,});Because both VPCs are in the same account and Region, the request is accepted
automatically and the connection reaches the active state.
Cross-Account Peering (accepted out of band)
const peering = yield* AWS.EC2.VpcPeeringConnection("Peering", { vpcId: myVpc.vpcId, peerVpcId: "vpc-0abc123", peerOwnerId: "123456789012",});With a different peerOwnerId the connection stays in pending-acceptance
until the peer account accepts it.
VpcPeeringConnection: Routing Traffic Across the Peering
Section titled “VpcPeeringConnection: Routing Traffic Across the Peering”const peering = yield* AWS.EC2.VpcPeeringConnection("Peering", { vpcId: vpcA.vpcId, peerVpcId: vpcB.vpcId,});
const routeAtoB = yield* AWS.EC2.Route("RouteAtoB", { routeTableId: vpcARouteTable.routeTableId, destinationCidrBlock: "10.1.0.0/16", vpcPeeringConnectionId: peering.vpcPeeringConnectionId,});Each side needs a route pointing the other VPC’s CIDR at the peering connection; only then can instances reach each other over private IPs.