alchemy 0.60.2 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/bin/alchemy.js +1480 -1498
- package/bin/commands/init.ts +94 -25
- package/bin/services/execute-alchemy.ts +32 -23
- package/lib/alchemy.d.ts +2 -2
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +1 -0
- package/lib/alchemy.js.map +1 -1
- package/lib/aws/client-props.d.ts +149 -0
- package/lib/aws/client-props.d.ts.map +1 -0
- package/lib/aws/client-props.js +1 -0
- package/lib/aws/client-props.js.map +1 -0
- package/lib/aws/credentials.d.ts +68 -5
- package/lib/aws/credentials.d.ts.map +1 -1
- package/lib/aws/credentials.js +148 -0
- package/lib/aws/credentials.js.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.d.ts +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.js +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +68 -40
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway.js +68 -40
- package/lib/aws/ec2/internet-gateway.js.map +1 -1
- package/lib/aws/ec2/nat-gateway.d.ts +99 -18
- package/lib/aws/ec2/nat-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/nat-gateway.js +113 -37
- package/lib/aws/ec2/nat-gateway.js.map +1 -1
- package/lib/aws/ec2/route-table-association.d.ts +1 -1
- package/lib/aws/ec2/route-table-association.js +1 -1
- package/lib/aws/ec2/route-table.d.ts +64 -1
- package/lib/aws/ec2/route-table.d.ts.map +1 -1
- package/lib/aws/ec2/route-table.js +64 -1
- package/lib/aws/ec2/route-table.js.map +1 -1
- package/lib/aws/ec2/route.d.ts +60 -1
- package/lib/aws/ec2/route.d.ts.map +1 -1
- package/lib/aws/ec2/route.js +60 -1
- package/lib/aws/ec2/route.js.map +1 -1
- package/lib/aws/ec2/security-group.d.ts +96 -3
- package/lib/aws/ec2/security-group.d.ts.map +1 -1
- package/lib/aws/ec2/security-group.js +96 -3
- package/lib/aws/ec2/security-group.js.map +1 -1
- package/lib/aws/ec2/subnet.d.ts +61 -3
- package/lib/aws/ec2/subnet.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.js +61 -3
- package/lib/aws/ec2/subnet.js.map +1 -1
- package/lib/aws/ec2/utils.d.ts +10 -2
- package/lib/aws/ec2/utils.d.ts.map +1 -1
- package/lib/aws/ec2/utils.js +68 -7
- package/lib/aws/ec2/utils.js.map +1 -1
- package/lib/aws/ec2/vpc.d.ts +50 -2
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.js +50 -2
- package/lib/aws/ec2/vpc.js.map +1 -1
- package/lib/aws/index.d.ts +2 -0
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +2 -0
- package/lib/aws/index.js.map +1 -1
- package/lib/cloudflare/api-response.js +2 -2
- package/lib/cloudflare/api-response.js.map +1 -1
- package/lib/cloudflare/api.d.ts +19 -0
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +4 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/assets.d.ts.map +1 -1
- package/lib/cloudflare/assets.js +3 -4
- package/lib/cloudflare/assets.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +5 -3
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +28 -20
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-compat-warning.js +7 -7
- package/lib/cloudflare/bundle/plugin-compat-warning.js.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-migrations.js +3 -2
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/index.d.ts +2 -1
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -1
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +2 -2
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +16 -3
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +3 -0
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/oauth.js +3 -3
- package/lib/cloudflare/oauth.js.map +1 -1
- package/lib/cloudflare/quick-tunnel.d.ts +17 -0
- package/lib/cloudflare/quick-tunnel.d.ts.map +1 -0
- package/lib/cloudflare/quick-tunnel.js +26 -0
- package/lib/cloudflare/quick-tunnel.js.map +1 -0
- package/lib/cloudflare/sveltekit/plugin.d.ts +2 -2
- package/lib/cloudflare/sveltekit/plugin.d.ts.map +1 -1
- package/lib/cloudflare/sveltekit/plugin.js +5 -4
- package/lib/cloudflare/sveltekit/plugin.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +26 -97
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/worker-bundle.js +11 -9
- package/lib/cloudflare/worker-bundle.js.map +1 -1
- package/lib/cloudflare/worker-tail.d.ts.map +1 -1
- package/lib/cloudflare/worker-tail.js +6 -6
- package/lib/cloudflare/worker-tail.js.map +1 -1
- package/lib/resource.d.ts +1 -0
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +13 -2
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +38 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +44 -19
- package/lib/scope.js.map +1 -1
- package/lib/state/cloudflare-state-store.d.ts.map +1 -1
- package/lib/state/cloudflare-state-store.js +2 -1
- package/lib/state/cloudflare-state-store.js.map +1 -1
- package/lib/util/idempotent-spawn.d.ts +24 -0
- package/lib/util/idempotent-spawn.d.ts.map +1 -0
- package/lib/util/idempotent-spawn.js +237 -0
- package/lib/util/idempotent-spawn.js.map +1 -0
- package/lib/util/index.d.ts +2 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +2 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/safe-fetch.d.ts +24 -0
- package/lib/util/safe-fetch.d.ts.map +1 -1
- package/lib/util/safe-fetch.js +72 -0
- package/lib/util/safe-fetch.js.map +1 -1
- package/package.json +9 -5
- package/src/alchemy.ts +3 -2
- package/src/aws/client-props.ts +156 -0
- package/src/aws/credentials.ts +165 -4
- package/src/aws/ec2/internet-gateway-attachment.ts +59 -4
- package/src/aws/ec2/internet-gateway.ts +70 -41
- package/src/aws/ec2/nat-gateway.ts +123 -33
- package/src/aws/ec2/route-table-association.ts +1 -1
- package/src/aws/ec2/route-table.ts +66 -2
- package/src/aws/ec2/route.ts +62 -2
- package/src/aws/ec2/security-group.ts +98 -4
- package/src/aws/ec2/subnet.ts +63 -4
- package/src/aws/ec2/utils.ts +73 -7
- package/src/aws/ec2/vpc.ts +52 -3
- package/src/aws/index.ts +2 -0
- package/src/cloudflare/api-response.ts +2 -2
- package/src/cloudflare/api.ts +23 -0
- package/src/cloudflare/assets.ts +3 -4
- package/src/cloudflare/bucket.ts +39 -29
- package/src/cloudflare/bundle/plugin-compat-warning.ts +7 -7
- package/src/cloudflare/cloudflare-env-proxy.ts +2 -2
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-migrations.ts +4 -5
- package/src/cloudflare/index.ts +2 -1
- package/src/cloudflare/kv-namespace.ts +26 -27
- package/src/cloudflare/miniflare/build-worker-options.ts +16 -3
- package/src/cloudflare/miniflare/miniflare-controller.ts +4 -0
- package/src/cloudflare/oauth.ts +3 -3
- package/src/cloudflare/quick-tunnel.ts +30 -0
- package/src/cloudflare/sveltekit/plugin.ts +5 -5
- package/src/cloudflare/website.ts +30 -111
- package/src/cloudflare/worker-bundle.ts +11 -9
- package/src/cloudflare/worker-tail.ts +6 -8
- package/src/resource.ts +19 -2
- package/src/scope.ts +111 -20
- package/src/state/cloudflare-state-store.ts +2 -1
- package/src/util/idempotent-spawn.ts +303 -0
- package/src/util/index.ts +1 -0
- package/src/util/safe-fetch.ts +114 -0
- package/templates/rwsdk/package.json +1 -1
package/src/aws/ec2/route.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type TimeoutConfig,
|
|
10
10
|
waitForResourceState,
|
|
11
11
|
} from "../../util/timeout.ts";
|
|
12
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
12
13
|
import { retry } from "../retry.ts";
|
|
13
14
|
import type { InternetGateway } from "./internet-gateway.ts";
|
|
14
15
|
import type { NatGateway } from "./nat-gateway.ts";
|
|
@@ -18,7 +19,7 @@ import { callEC2Api, createEC2Client } from "./utils.ts";
|
|
|
18
19
|
/**
|
|
19
20
|
* Properties for creating or updating a Route
|
|
20
21
|
*/
|
|
21
|
-
export interface RouteProps {
|
|
22
|
+
export interface RouteProps extends AwsClientProps {
|
|
22
23
|
/**
|
|
23
24
|
* The route table to add the route to
|
|
24
25
|
*/
|
|
@@ -74,6 +75,9 @@ export interface Route extends Resource<"aws::Route">, RouteProps {
|
|
|
74
75
|
* Creates and manages individual routes within route tables, directing traffic
|
|
75
76
|
* to various targets like Internet Gateways, NAT Gateways, or instances.
|
|
76
77
|
*
|
|
78
|
+
* Supports AWS credential overrides at the resource level, allowing you to deploy Routes
|
|
79
|
+
* to different AWS accounts or regions than the default scope configuration.
|
|
80
|
+
*
|
|
77
81
|
* @example
|
|
78
82
|
* // Create a default route to Internet Gateway
|
|
79
83
|
* const publicRoute = await Route("public-default-route", {
|
|
@@ -91,6 +95,39 @@ export interface Route extends Resource<"aws::Route">, RouteProps {
|
|
|
91
95
|
* });
|
|
92
96
|
*
|
|
93
97
|
* @example
|
|
98
|
+
* // Create Route with AWS credential overrides
|
|
99
|
+
* const crossAccountRoute = await Route("cross-account-route", {
|
|
100
|
+
* routeTable: mainRouteTable,
|
|
101
|
+
* destinationCidrBlock: "0.0.0.0/0",
|
|
102
|
+
* target: { internetGateway: mainIgw },
|
|
103
|
+
* // Override AWS credentials for this specific resource
|
|
104
|
+
* region: "us-east-1",
|
|
105
|
+
* profile: "production-account",
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // Create Route in different region with role assumption
|
|
110
|
+
* const multiRegionRoute = await Route("multi-region-route", {
|
|
111
|
+
* routeTable: euRouteTable,
|
|
112
|
+
* destinationCidrBlock: "0.0.0.0/0",
|
|
113
|
+
* target: { internetGateway: euIgw },
|
|
114
|
+
* region: "eu-west-1",
|
|
115
|
+
* roleArn: "arn:aws:iam::123456789012:role/CrossRegionRole",
|
|
116
|
+
* roleSessionName: "route-deployment",
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Create Route with explicit credentials
|
|
121
|
+
* const explicitCredsRoute = await Route("explicit-creds-route", {
|
|
122
|
+
* routeTable: testRouteTable,
|
|
123
|
+
* destinationCidrBlock: "0.0.0.0/0",
|
|
124
|
+
* target: { internetGateway: testIgw },
|
|
125
|
+
* accessKeyId: alchemy.secret("AKIAIOSFODNN7EXAMPLE"),
|
|
126
|
+
* secretAccessKey: alchemy.secret("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
|
|
127
|
+
* region: "us-west-2",
|
|
128
|
+
* });
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
94
131
|
* // Create a route to a specific instance
|
|
95
132
|
* const instanceRoute = await Route("instance-route", {
|
|
96
133
|
* routeTable: "rtb-12345678",
|
|
@@ -133,6 +170,28 @@ export interface Route extends Resource<"aws::Route">, RouteProps {
|
|
|
133
170
|
* delayMs: 2000
|
|
134
171
|
* }
|
|
135
172
|
* });
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* // Multi-account deployment with scope-level and resource-level overrides
|
|
176
|
+
* await alchemy.run("production", {
|
|
177
|
+
* aws: { region: "us-west-2", profile: "main-account" }
|
|
178
|
+
* }, async () => {
|
|
179
|
+
* // This Route uses scope credentials (main-account, us-west-2)
|
|
180
|
+
* const mainRoute = await Route("main-route", {
|
|
181
|
+
* routeTable: mainRouteTable,
|
|
182
|
+
* destinationCidrBlock: "0.0.0.0/0",
|
|
183
|
+
* target: { internetGateway: mainIgw }
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* // This Route overrides to use different account
|
|
187
|
+
* const crossAccountRoute = await Route("cross-account-route", {
|
|
188
|
+
* routeTable: crossRouteTable,
|
|
189
|
+
* destinationCidrBlock: "0.0.0.0/0",
|
|
190
|
+
* target: { internetGateway: crossIgw },
|
|
191
|
+
* profile: "secondary-account",
|
|
192
|
+
* region: "us-east-1", // Also override region
|
|
193
|
+
* });
|
|
194
|
+
* });
|
|
136
195
|
*/
|
|
137
196
|
export const Route = Resource(
|
|
138
197
|
"aws::Route",
|
|
@@ -141,7 +200,8 @@ export const Route = Resource(
|
|
|
141
200
|
_id: string,
|
|
142
201
|
props: RouteProps,
|
|
143
202
|
): Promise<Route> {
|
|
144
|
-
|
|
203
|
+
// Create EC2 client with credential resolution handled internally
|
|
204
|
+
const client = await createEC2Client(props);
|
|
145
205
|
const _timeoutConfig = mergeTimeoutConfig(ROUTE_TIMEOUT, props.timeout);
|
|
146
206
|
const routeTableId =
|
|
147
207
|
typeof props.routeTable === "string"
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type TimeoutConfig,
|
|
9
9
|
waitForResourceState,
|
|
10
10
|
} from "../../util/timeout.ts";
|
|
11
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
11
12
|
import { callEC2Api, createEC2Client } from "./utils.ts";
|
|
12
13
|
import type { Vpc } from "./vpc.ts";
|
|
13
14
|
|
|
@@ -22,7 +23,7 @@ export const SECURITY_GROUP_TIMEOUT: TimeoutConfig = {
|
|
|
22
23
|
/**
|
|
23
24
|
* Properties for creating or updating a Security Group
|
|
24
25
|
*/
|
|
25
|
-
export interface SecurityGroupProps {
|
|
26
|
+
export interface SecurityGroupProps extends AwsClientProps {
|
|
26
27
|
/**
|
|
27
28
|
* The VPC to create the security group in.
|
|
28
29
|
*/
|
|
@@ -79,8 +80,12 @@ export interface SecurityGroup
|
|
|
79
80
|
* instances and other AWS resources. Rules are managed separately using the
|
|
80
81
|
* `SecurityGroupRule` resource.
|
|
81
82
|
*
|
|
83
|
+
* Supports AWS credential overrides at the resource level, allowing you to deploy Security Groups
|
|
84
|
+
* to different AWS accounts or regions than the default scope configuration.
|
|
85
|
+
*
|
|
82
86
|
* @example
|
|
83
|
-
*
|
|
87
|
+
* ```typescript
|
|
88
|
+
* // Create a basic web server security group
|
|
84
89
|
* const webSecurityGroup = await SecurityGroup("web-sg", {
|
|
85
90
|
* vpc: mainVpc,
|
|
86
91
|
* groupName: "web-server-sg",
|
|
@@ -90,11 +95,72 @@ export interface SecurityGroup
|
|
|
90
95
|
* Environment: "production"
|
|
91
96
|
* }
|
|
92
97
|
* });
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Create Security Group with AWS credential overrides
|
|
103
|
+
* const crossAccountSg = await SecurityGroup("cross-account-sg", {
|
|
104
|
+
* vpc: mainVpc,
|
|
105
|
+
* groupName: "cross-account-security-group",
|
|
106
|
+
* description: "Security group in different account",
|
|
107
|
+
* // Override AWS credentials for this specific resource
|
|
108
|
+
* region: "us-east-1",
|
|
109
|
+
* profile: "production-account",
|
|
110
|
+
* tags: {
|
|
111
|
+
* Name: "cross-account-sg",
|
|
112
|
+
* Environment: "production"
|
|
113
|
+
* }
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
93
116
|
*
|
|
94
117
|
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Create Security Group in different region with role assumption
|
|
120
|
+
* const multiRegionSg = await SecurityGroup("multi-region-sg", {
|
|
121
|
+
* vpc: euVpc,
|
|
122
|
+
* groupName: "eu-security-group",
|
|
123
|
+
* description: "Security group in EU region",
|
|
124
|
+
* region: "eu-west-1",
|
|
125
|
+
* roleArn: "arn:aws:iam::123456789012:role/CrossRegionRole",
|
|
126
|
+
* roleSessionName: "security-group-deployment",
|
|
127
|
+
* tags: {
|
|
128
|
+
* Name: "eu-security-group",
|
|
129
|
+
* Region: "europe"
|
|
130
|
+
* }
|
|
131
|
+
* });
|
|
132
|
+
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* // Create Security Group with explicit credentials
|
|
137
|
+
* const explicitCredsSg = await SecurityGroup("explicit-creds-sg", {
|
|
138
|
+
* vpc: testVpc,
|
|
139
|
+
* groupName: "test-security-group",
|
|
140
|
+
* description: "Security group with explicit credentials",
|
|
141
|
+
* accessKeyId: alchemy.secret("AKIAIOSFODNN7EXAMPLE"),
|
|
142
|
+
* secretAccessKey: alchemy.secret("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"),
|
|
143
|
+
* region: "us-west-2",
|
|
144
|
+
* tags: {
|
|
145
|
+
* Name: "explicit-credentials-sg",
|
|
146
|
+
* Purpose: "testing"
|
|
147
|
+
* }
|
|
148
|
+
* });
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* // Create Security Group with rules (rules managed separately)
|
|
154
|
+
* const webSg = await SecurityGroup("web-sg", {
|
|
155
|
+
* vpc: mainVpc,
|
|
156
|
+
* groupName: "web-server-sg",
|
|
157
|
+
* description: "Security group for web servers",
|
|
158
|
+
* tags: { Name: "web-server-security-group" }
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
95
161
|
* // Separately, define a rule to allow HTTP traffic
|
|
96
162
|
* const httpRule = await SecurityGroupRule("web-sg-http-rule", {
|
|
97
|
-
* securityGroup:
|
|
163
|
+
* securityGroup: webSg,
|
|
98
164
|
* type: "ingress",
|
|
99
165
|
* protocol: "tcp",
|
|
100
166
|
* fromPort: 80,
|
|
@@ -102,6 +168,33 @@ export interface SecurityGroup
|
|
|
102
168
|
* cidrBlocks: ["0.0.0.0/0"],
|
|
103
169
|
* description: "Allow HTTP access from anywhere"
|
|
104
170
|
* });
|
|
171
|
+
* ```
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* // Multi-account deployment with scope-level and resource-level overrides
|
|
176
|
+
* await alchemy.run("production", {
|
|
177
|
+
* aws: { region: "us-west-2", profile: "main-account" }
|
|
178
|
+
* }, async () => {
|
|
179
|
+
* // This SG uses scope credentials (main-account, us-west-2)
|
|
180
|
+
* const mainSg = await SecurityGroup("main-sg", {
|
|
181
|
+
* vpc: mainVpc,
|
|
182
|
+
* groupName: "main-account-sg",
|
|
183
|
+
* description: "Security group in main account",
|
|
184
|
+
* tags: { Name: "main-account-sg" }
|
|
185
|
+
* });
|
|
186
|
+
*
|
|
187
|
+
* // This SG overrides to use different account
|
|
188
|
+
* const crossAccountSg = await SecurityGroup("cross-account-sg", {
|
|
189
|
+
* vpc: crossVpc,
|
|
190
|
+
* groupName: "secondary-account-sg",
|
|
191
|
+
* description: "Security group in secondary account",
|
|
192
|
+
* profile: "secondary-account",
|
|
193
|
+
* region: "us-east-1", // Also override region
|
|
194
|
+
* tags: { Name: "secondary-account-sg" }
|
|
195
|
+
* });
|
|
196
|
+
* });
|
|
197
|
+
* ```
|
|
105
198
|
*/
|
|
106
199
|
export const SecurityGroup = Resource(
|
|
107
200
|
"aws::SecurityGroup",
|
|
@@ -110,7 +203,8 @@ export const SecurityGroup = Resource(
|
|
|
110
203
|
_id: string,
|
|
111
204
|
props: SecurityGroupProps,
|
|
112
205
|
): Promise<SecurityGroup> {
|
|
113
|
-
|
|
206
|
+
// Create EC2 client with credential resolution handled internally
|
|
207
|
+
const client = await createEC2Client(props);
|
|
114
208
|
const vpcId = typeof props.vpc === "string" ? props.vpc : props.vpc.vpcId;
|
|
115
209
|
const timeoutConfig = mergeTimeoutConfig(
|
|
116
210
|
SECURITY_GROUP_TIMEOUT,
|
package/src/aws/ec2/subnet.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type TimeoutConfig,
|
|
10
10
|
waitForResourceState,
|
|
11
11
|
} from "../../util/timeout.ts";
|
|
12
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
12
13
|
import { retry } from "../retry.ts";
|
|
13
14
|
import { callEC2Api, createEC2Client } from "./utils.ts";
|
|
14
15
|
import type { Vpc } from "./vpc.ts";
|
|
@@ -16,7 +17,7 @@ import type { Vpc } from "./vpc.ts";
|
|
|
16
17
|
/**
|
|
17
18
|
* Properties for creating or updating a Subnet
|
|
18
19
|
*/
|
|
19
|
-
export interface SubnetProps {
|
|
20
|
+
export interface SubnetProps extends AwsClientProps {
|
|
20
21
|
/**
|
|
21
22
|
* The VPC to create the subnet in
|
|
22
23
|
*/
|
|
@@ -164,14 +165,15 @@ async function waitForSubnetDeleted(
|
|
|
164
165
|
* AWS Subnet Resource
|
|
165
166
|
*
|
|
166
167
|
* Creates and manages subnets within a VPC. Subnets are used to segment
|
|
167
|
-
* your VPC into smaller networks for organizing resources.
|
|
168
|
+
* your VPC into smaller networks for organizing resources. Supports AWS
|
|
169
|
+
* credential overrides for multi-account and cross-region deployments.
|
|
168
170
|
*
|
|
169
171
|
* @example
|
|
170
172
|
* // Create a public subnet for web servers
|
|
171
173
|
* const publicSubnet = await Subnet("public-subnet", {
|
|
172
174
|
* vpc: myVpc,
|
|
173
175
|
* cidrBlock: "10.0.1.0/24",
|
|
174
|
-
* availabilityZone:
|
|
176
|
+
* availabilityZone: `${process.env.AWS_REGION}a` || "us-east-1a",
|
|
175
177
|
* mapPublicIpOnLaunch: true,
|
|
176
178
|
* tags: {
|
|
177
179
|
* Name: "public-subnet",
|
|
@@ -203,6 +205,62 @@ async function waitForSubnetDeleted(
|
|
|
203
205
|
* delayMs: 500
|
|
204
206
|
* }
|
|
205
207
|
* });
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* // Create a subnet with AWS credential overrides for cross-account deployment
|
|
211
|
+
* const crossAccountSubnet = await Subnet("cross-account-subnet", {
|
|
212
|
+
* vpc: myVpc,
|
|
213
|
+
* cidrBlock: "10.1.1.0/24",
|
|
214
|
+
* availabilityZone: "us-east-1a",
|
|
215
|
+
* region: "us-east-1",
|
|
216
|
+
* profile: "production-account",
|
|
217
|
+
* mapPublicIpOnLaunch: true,
|
|
218
|
+
* tags: {
|
|
219
|
+
* Name: "cross-account-subnet",
|
|
220
|
+
* Account: "production"
|
|
221
|
+
* }
|
|
222
|
+
* });
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* // Create a subnet in a different region with explicit credentials
|
|
226
|
+
* const multiRegionSubnet = await Subnet("multi-region-subnet", {
|
|
227
|
+
* vpc: "vpc-12345678", // VPC ID from different region
|
|
228
|
+
* cidrBlock: "10.2.1.0/24",
|
|
229
|
+
* availabilityZone: "eu-west-1a",
|
|
230
|
+
* region: "eu-west-1",
|
|
231
|
+
* accessKeyId: alchemy.secret("AKIA..."),
|
|
232
|
+
* secretAccessKey: alchemy.secret("..."),
|
|
233
|
+
* mapPublicIpOnLaunch: false,
|
|
234
|
+
* tags: {
|
|
235
|
+
* Name: "eu-subnet",
|
|
236
|
+
* Region: "europe"
|
|
237
|
+
* }
|
|
238
|
+
* });
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* // Using scope-level credentials with resource-level overrides
|
|
242
|
+
* await alchemy.run("multi-account", {
|
|
243
|
+
* aws: {
|
|
244
|
+
* region: "us-west-2",
|
|
245
|
+
* profile: "staging"
|
|
246
|
+
* }
|
|
247
|
+
* }, async () => {
|
|
248
|
+
* // This subnet uses staging credentials from scope
|
|
249
|
+
* const stagingSubnet = await Subnet("staging-subnet", {
|
|
250
|
+
* vpc: stagingVpc,
|
|
251
|
+
* cidrBlock: "10.0.1.0/24",
|
|
252
|
+
* availabilityZone: "us-west-2a"
|
|
253
|
+
* });
|
|
254
|
+
*
|
|
255
|
+
* // This subnet overrides to use production credentials
|
|
256
|
+
* const prodSubnet = await Subnet("prod-subnet", {
|
|
257
|
+
* vpc: prodVpc,
|
|
258
|
+
* cidrBlock: "10.1.1.0/24",
|
|
259
|
+
* availabilityZone: "us-east-1a",
|
|
260
|
+
* profile: "production",
|
|
261
|
+
* region: "us-east-1"
|
|
262
|
+
* });
|
|
263
|
+
* });
|
|
206
264
|
*/
|
|
207
265
|
export const Subnet = Resource(
|
|
208
266
|
"aws::Subnet",
|
|
@@ -211,7 +269,8 @@ export const Subnet = Resource(
|
|
|
211
269
|
_id: string,
|
|
212
270
|
props: SubnetProps,
|
|
213
271
|
): Promise<Subnet> {
|
|
214
|
-
|
|
272
|
+
// Create EC2 client with credential resolution handled internally
|
|
273
|
+
const client = await createEC2Client(props);
|
|
215
274
|
const vpcId = typeof props.vpc === "string" ? props.vpc : props.vpc.vpcId;
|
|
216
275
|
|
|
217
276
|
// Validate timeout config early, but use default for deletion if invalid
|
package/src/aws/ec2/utils.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
|
|
2
2
|
import { AwsClient } from "aws4fetch";
|
|
3
|
+
import { alchemy } from "../../alchemy.ts";
|
|
4
|
+
import { Secret } from "../../secret.ts";
|
|
3
5
|
import { logger } from "../../util/logger.ts";
|
|
4
6
|
import { flattenParams } from "../../util/params.ts";
|
|
7
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
8
|
+
import { resolveAwsCredentials } from "../credentials.ts";
|
|
5
9
|
import { getRegion } from "../utils.ts";
|
|
6
10
|
|
|
7
11
|
/**
|
|
@@ -9,19 +13,81 @@ import { getRegion } from "../utils.ts";
|
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
|
-
* Create an AWS EC2 client
|
|
16
|
+
* Create an AWS EC2 client with credential resolution from props
|
|
17
|
+
*
|
|
18
|
+
* This function handles the complete credential resolution process internally,
|
|
19
|
+
* merging global, scope, and resource-level credentials according to the
|
|
20
|
+
* established precedence hierarchy.
|
|
21
|
+
*
|
|
22
|
+
* @param props - AWS client properties that may include credential overrides
|
|
23
|
+
* @returns Promise<AwsClient> - Configured AWS client for EC2 operations
|
|
13
24
|
*/
|
|
14
|
-
export async function createEC2Client(
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
export async function createEC2Client(
|
|
26
|
+
props?: AwsClientProps,
|
|
27
|
+
): Promise<AwsClient> {
|
|
28
|
+
// Resolve credentials from all sources (global, scope, resource)
|
|
29
|
+
const credentials = await resolveAwsCredentials(props);
|
|
30
|
+
let awsCredentials: AwsClientProps;
|
|
31
|
+
let region: string;
|
|
32
|
+
|
|
33
|
+
if (credentials && (credentials.accessKeyId || credentials.secretAccessKey)) {
|
|
34
|
+
// Use provided credentials directly (unwrap secrets for AWS SDK)
|
|
35
|
+
awsCredentials = {
|
|
36
|
+
accessKeyId: credentials.accessKeyId,
|
|
37
|
+
secretAccessKey: credentials.secretAccessKey,
|
|
38
|
+
sessionToken: credentials.sessionToken,
|
|
39
|
+
};
|
|
40
|
+
} else if (credentials?.profile) {
|
|
41
|
+
// Use profile-based credentials if profile is specified
|
|
42
|
+
try {
|
|
43
|
+
const profileCredentials = await fromNodeProviderChain({
|
|
44
|
+
profile: credentials.profile,
|
|
45
|
+
})();
|
|
46
|
+
awsCredentials = {
|
|
47
|
+
accessKeyId: alchemy.secret(profileCredentials.accessKeyId),
|
|
48
|
+
secretAccessKey: alchemy.secret(profileCredentials.secretAccessKey),
|
|
49
|
+
sessionToken: alchemy.secret(profileCredentials.sessionToken),
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.log(
|
|
53
|
+
`Error loading credentials from profile ${credentials.profile}:`,
|
|
54
|
+
error,
|
|
55
|
+
);
|
|
56
|
+
// Fall back to default credentials
|
|
57
|
+
awsCredentials = await loadAwsPropsFromCredentialChain();
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
// Fall back to AWS SDK credential chain
|
|
61
|
+
awsCredentials = await loadAwsPropsFromCredentialChain();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Use region from resolved credentials or fall back to getRegion()
|
|
65
|
+
if (credentials?.region) {
|
|
66
|
+
region = credentials.region;
|
|
67
|
+
} else {
|
|
68
|
+
region = await getRegion();
|
|
69
|
+
}
|
|
17
70
|
|
|
18
71
|
return new AwsClient({
|
|
19
|
-
...
|
|
72
|
+
...awsCredentials,
|
|
73
|
+
accessKeyId: Secret.unwrap(awsCredentials.accessKeyId)!,
|
|
74
|
+
secretAccessKey: Secret.unwrap(awsCredentials.secretAccessKey)!,
|
|
75
|
+
sessionToken: Secret.unwrap(awsCredentials.sessionToken) || undefined,
|
|
20
76
|
service: "ec2",
|
|
21
77
|
region,
|
|
22
78
|
});
|
|
23
79
|
}
|
|
24
80
|
|
|
81
|
+
async function loadAwsPropsFromCredentialChain(): Promise<AwsClientProps> {
|
|
82
|
+
const defaultCredentialsRaw = await fromNodeProviderChain()();
|
|
83
|
+
return {
|
|
84
|
+
...defaultCredentialsRaw,
|
|
85
|
+
accessKeyId: alchemy.secret(defaultCredentialsRaw.accessKeyId),
|
|
86
|
+
secretAccessKey: alchemy.secret(defaultCredentialsRaw.secretAccessKey),
|
|
87
|
+
sessionToken: alchemy.secret(defaultCredentialsRaw.sessionToken),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
25
91
|
/**
|
|
26
92
|
* Make an EC2 API call with custom response parser
|
|
27
93
|
*/
|
|
@@ -34,8 +100,8 @@ export async function callEC2Api<T>(
|
|
|
34
100
|
// Try the API call, and retry once with fresh credentials on auth failure
|
|
35
101
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
36
102
|
try {
|
|
37
|
-
|
|
38
|
-
const url = `https://ec2.${region}.amazonaws.com/`;
|
|
103
|
+
// Use the client's region instead of the global region
|
|
104
|
+
const url = `https://ec2.${client.region}.amazonaws.com/`;
|
|
39
105
|
|
|
40
106
|
const body = new URLSearchParams({
|
|
41
107
|
Action: action,
|
package/src/aws/ec2/vpc.ts
CHANGED
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
type TimeoutConfig,
|
|
10
10
|
waitForResourceState,
|
|
11
11
|
} from "../../util/timeout.ts";
|
|
12
|
+
import type { AwsClientProps } from "../client-props.ts";
|
|
12
13
|
import { callEC2Api, createEC2Client } from "./utils.ts";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Properties for creating or updating a VPC
|
|
16
17
|
*/
|
|
17
|
-
export interface VpcProps {
|
|
18
|
+
export interface VpcProps extends AwsClientProps {
|
|
18
19
|
/**
|
|
19
20
|
* The IPv4 network range for the VPC, in CIDR notation
|
|
20
21
|
* @example "10.0.0.0/16"
|
|
@@ -148,7 +149,8 @@ export interface Vpc extends Resource<"aws::Vpc">, VpcProps {
|
|
|
148
149
|
* AWS VPC (Virtual Private Cloud) Resource
|
|
149
150
|
*
|
|
150
151
|
* Creates and manages Amazon VPC instances with configurable CIDR blocks,
|
|
151
|
-
* DNS settings, and instance tenancy options.
|
|
152
|
+
* DNS settings, and instance tenancy options. Supports AWS credential overrides
|
|
153
|
+
* for multi-account and cross-region deployments.
|
|
152
154
|
*
|
|
153
155
|
* @example
|
|
154
156
|
* // Create a basic VPC with default settings
|
|
@@ -188,6 +190,52 @@ export interface Vpc extends Resource<"aws::Vpc">, VpcProps {
|
|
|
188
190
|
* Type: "isolated"
|
|
189
191
|
* }
|
|
190
192
|
* });
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* // Create a VPC with AWS credential overrides for cross-account deployment
|
|
196
|
+
* const crossAccountVpc = await Vpc("cross-account-vpc", {
|
|
197
|
+
* cidrBlock: "10.1.0.0/16",
|
|
198
|
+
* region: "us-east-1",
|
|
199
|
+
* profile: "production-account",
|
|
200
|
+
* tags: {
|
|
201
|
+
* Name: "cross-account-vpc",
|
|
202
|
+
* Account: "production"
|
|
203
|
+
* }
|
|
204
|
+
* });
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* // Create a VPC in a different region with explicit credentials
|
|
208
|
+
* const multiRegionVpc = await Vpc("multi-region-vpc", {
|
|
209
|
+
* cidrBlock: "10.2.0.0/16",
|
|
210
|
+
* region: "eu-west-1",
|
|
211
|
+
* accessKeyId: alchemy.secret("AKIA..."),
|
|
212
|
+
* secretAccessKey: alchemy.secret("..."),
|
|
213
|
+
* tags: {
|
|
214
|
+
* Name: "eu-vpc",
|
|
215
|
+
* Region: "europe"
|
|
216
|
+
* }
|
|
217
|
+
* });
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* // Using scope-level credentials with resource-level overrides
|
|
221
|
+
* await alchemy.run("multi-account", {
|
|
222
|
+
* aws: {
|
|
223
|
+
* region: "us-west-2",
|
|
224
|
+
* profile: "staging"
|
|
225
|
+
* }
|
|
226
|
+
* }, async () => {
|
|
227
|
+
* // This VPC uses staging credentials from scope
|
|
228
|
+
* const stagingVpc = await Vpc("staging-vpc", {
|
|
229
|
+
* cidrBlock: "10.0.0.0/16"
|
|
230
|
+
* });
|
|
231
|
+
*
|
|
232
|
+
* // This VPC overrides to use production credentials
|
|
233
|
+
* const prodVpc = await Vpc("prod-vpc", {
|
|
234
|
+
* cidrBlock: "10.1.0.0/16",
|
|
235
|
+
* profile: "production",
|
|
236
|
+
* region: "us-east-1"
|
|
237
|
+
* });
|
|
238
|
+
* });
|
|
191
239
|
*/
|
|
192
240
|
export const Vpc = Resource(
|
|
193
241
|
"aws::Vpc",
|
|
@@ -196,7 +244,8 @@ export const Vpc = Resource(
|
|
|
196
244
|
_id: string,
|
|
197
245
|
props: VpcProps,
|
|
198
246
|
): Promise<Vpc> {
|
|
199
|
-
|
|
247
|
+
// Create EC2 client with credential resolution handled internally
|
|
248
|
+
const client = await createEC2Client(props);
|
|
200
249
|
const timeoutConfig = mergeTimeoutConfig(VPC_TIMEOUT, props.timeout);
|
|
201
250
|
|
|
202
251
|
if (this.phase === "delete") {
|
package/src/aws/index.ts
CHANGED
|
@@ -102,8 +102,8 @@ export async function extractCloudflareResult<T>(
|
|
|
102
102
|
label: string,
|
|
103
103
|
promise: Promise<Response>,
|
|
104
104
|
): Promise<T> {
|
|
105
|
-
const response = await promise.catch(() => {
|
|
106
|
-
throw new Error(`Failed to ${label}: Failed to fetch
|
|
105
|
+
const response = await promise.catch((cause) => {
|
|
106
|
+
throw new Error(`Failed to ${label}: Failed to fetch`, { cause });
|
|
107
107
|
});
|
|
108
108
|
const json = (await response.json().catch(() => {
|
|
109
109
|
throw new Error(
|
package/src/cloudflare/api.ts
CHANGED
|
@@ -63,6 +63,10 @@ const cloudflareApiCache: Record<string, CloudflareApi> = {};
|
|
|
63
63
|
export async function createCloudflareApi(
|
|
64
64
|
options: Partial<CloudflareApiOptions> | InternalCloudflareApiOptions = {},
|
|
65
65
|
): Promise<CloudflareApi> {
|
|
66
|
+
// TODO: Implement scope-level credential resolution similar to AWS
|
|
67
|
+
// This function should check for scope.providerCredentials.cloudflare
|
|
68
|
+
// and merge those credentials with the provided options, following
|
|
69
|
+
// the same three-tier resolution pattern: global → scope → resource
|
|
66
70
|
const cacheKey = computeCacheKey(options);
|
|
67
71
|
if (cloudflareApiCache[cacheKey]) {
|
|
68
72
|
return cloudflareApiCache[cacheKey];
|
|
@@ -251,3 +255,22 @@ class TooManyRequestsError extends Error {
|
|
|
251
255
|
}
|
|
252
256
|
|
|
253
257
|
class ForbiddenError extends Error {}
|
|
258
|
+
/**
|
|
259
|
+
* Cloudflare scope extensions - adds Cloudflare credential support to scope options.
|
|
260
|
+
* This uses TypeScript module augmentation to extend the ProviderCredentials interface.
|
|
261
|
+
* Since ScopeOptions and RunOptions both extend ProviderCredentials,
|
|
262
|
+
* they automatically inherit these properties.
|
|
263
|
+
*
|
|
264
|
+
* NOTE: These scope credentials are not currently being used by createCloudflareApi.
|
|
265
|
+
* See TODO above in createCloudflareApi function for implementation needed.
|
|
266
|
+
*/
|
|
267
|
+
declare module "../scope.ts" {
|
|
268
|
+
interface ProviderCredentials {
|
|
269
|
+
/**
|
|
270
|
+
* Cloudflare credentials configuration for this scope.
|
|
271
|
+
* All Cloudflare resources created within this scope will inherit these credentials
|
|
272
|
+
* unless overridden at the resource level.
|
|
273
|
+
*/
|
|
274
|
+
cloudflare?: CloudflareApiOptions;
|
|
275
|
+
}
|
|
276
|
+
}
|
package/src/cloudflare/assets.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "
|
|
2
|
+
import * as path from "pathe";
|
|
3
3
|
import type { Context } from "../context.ts";
|
|
4
4
|
import { Resource } from "../resource.ts";
|
|
5
5
|
import { getContentType } from "../util/content-type.ts";
|
|
@@ -118,11 +118,10 @@ export const Assets = Resource(
|
|
|
118
118
|
// Create asset file objects
|
|
119
119
|
const files: AssetFile[] = filesList.map((filePath) => {
|
|
120
120
|
const relativePath = path.relative(props.path, filePath);
|
|
121
|
-
const normalizedPath = relativePath.split(path.sep).join("/"); // Ensure forward slashes for URLs
|
|
122
121
|
|
|
123
122
|
return {
|
|
124
|
-
path:
|
|
125
|
-
filePath,
|
|
123
|
+
path: path.normalize(relativePath),
|
|
124
|
+
filePath: path.normalize(filePath),
|
|
126
125
|
contentType: getContentType(filePath) ?? "application/null",
|
|
127
126
|
};
|
|
128
127
|
});
|