alchemy-effect 0.4.0 → 0.6.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 +13 -7
- package/bin/alchemy-effect.js +307 -266
- package/bin/alchemy-effect.js.map +1 -1
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +253 -246
- package/lib/apply.js.map +1 -1
- package/lib/aws/client.d.ts.map +1 -1
- package/lib/aws/client.js +15 -2
- package/lib/aws/client.js.map +1 -1
- package/lib/aws/dynamodb/secondary-index.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.js.map +1 -1
- package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.provider.js +10 -2
- package/lib/aws/dynamodb/table.provider.js.map +1 -1
- package/lib/aws/ec2/egress-only-igw.d.ts +55 -0
- package/lib/aws/ec2/egress-only-igw.d.ts.map +1 -0
- package/lib/aws/ec2/egress-only-igw.js +4 -0
- package/lib/aws/ec2/egress-only-igw.js.map +1 -0
- package/lib/aws/ec2/egress-only-igw.provider.d.ts +6 -0
- package/lib/aws/ec2/egress-only-igw.provider.d.ts.map +1 -0
- package/lib/aws/ec2/egress-only-igw.provider.js +114 -0
- package/lib/aws/ec2/egress-only-igw.provider.js.map +1 -0
- package/lib/aws/ec2/eip.d.ts +85 -0
- package/lib/aws/ec2/eip.d.ts.map +1 -0
- package/lib/aws/ec2/eip.js +4 -0
- package/lib/aws/ec2/eip.js.map +1 -0
- package/lib/aws/ec2/eip.provider.d.ts +6 -0
- package/lib/aws/ec2/eip.provider.d.ts.map +1 -0
- package/lib/aws/ec2/eip.provider.js +135 -0
- package/lib/aws/ec2/eip.provider.js.map +1 -0
- package/lib/aws/ec2/index.d.ts +18 -0
- package/lib/aws/ec2/index.d.ts.map +1 -1
- package/lib/aws/ec2/index.js +18 -0
- package/lib/aws/ec2/index.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway.provider.js +9 -5
- package/lib/aws/ec2/internet-gateway.provider.js.map +1 -1
- package/lib/aws/ec2/nat-gateway.d.ts +127 -0
- package/lib/aws/ec2/nat-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/nat-gateway.js +4 -0
- package/lib/aws/ec2/nat-gateway.js.map +1 -0
- package/lib/aws/ec2/nat-gateway.provider.d.ts +6 -0
- package/lib/aws/ec2/nat-gateway.provider.d.ts.map +1 -0
- package/lib/aws/ec2/nat-gateway.provider.js +222 -0
- package/lib/aws/ec2/nat-gateway.provider.js.map +1 -0
- package/lib/aws/ec2/network-acl-association.d.ts +44 -0
- package/lib/aws/ec2/network-acl-association.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl-association.js +4 -0
- package/lib/aws/ec2/network-acl-association.js.map +1 -0
- package/lib/aws/ec2/network-acl-association.provider.d.ts +4 -0
- package/lib/aws/ec2/network-acl-association.provider.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl-association.provider.js +115 -0
- package/lib/aws/ec2/network-acl-association.provider.js.map +1 -0
- package/lib/aws/ec2/network-acl-entry.d.ts +118 -0
- package/lib/aws/ec2/network-acl-entry.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl-entry.js +3 -0
- package/lib/aws/ec2/network-acl-entry.js.map +1 -0
- package/lib/aws/ec2/network-acl-entry.provider.d.ts +4 -0
- package/lib/aws/ec2/network-acl-entry.provider.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl-entry.provider.js +129 -0
- package/lib/aws/ec2/network-acl-entry.provider.js.map +1 -0
- package/lib/aws/ec2/network-acl.d.ts +82 -0
- package/lib/aws/ec2/network-acl.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl.js +4 -0
- package/lib/aws/ec2/network-acl.js.map +1 -0
- package/lib/aws/ec2/network-acl.provider.d.ts +6 -0
- package/lib/aws/ec2/network-acl.provider.d.ts.map +1 -0
- package/lib/aws/ec2/network-acl.provider.js +136 -0
- package/lib/aws/ec2/network-acl.provider.js.map +1 -0
- package/lib/aws/ec2/route-table-association.d.ts.map +1 -1
- package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -1
- package/lib/aws/ec2/route-table-association.provider.js.map +1 -1
- package/lib/aws/ec2/route-table.d.ts.map +1 -1
- package/lib/aws/ec2/route-table.provider.d.ts.map +1 -1
- package/lib/aws/ec2/route-table.provider.js.map +1 -1
- package/lib/aws/ec2/route.d.ts.map +1 -1
- package/lib/aws/ec2/route.provider.d.ts.map +1 -1
- package/lib/aws/ec2/route.provider.js.map +1 -1
- package/lib/aws/ec2/security-group-rule.d.ts +118 -0
- package/lib/aws/ec2/security-group-rule.d.ts.map +1 -0
- package/lib/aws/ec2/security-group-rule.js +4 -0
- package/lib/aws/ec2/security-group-rule.js.map +1 -0
- package/lib/aws/ec2/security-group-rule.provider.d.ts +4 -0
- package/lib/aws/ec2/security-group-rule.provider.d.ts.map +1 -0
- package/lib/aws/ec2/security-group-rule.provider.js +193 -0
- package/lib/aws/ec2/security-group-rule.provider.js.map +1 -0
- package/lib/aws/ec2/security-group.d.ts +147 -0
- package/lib/aws/ec2/security-group.d.ts.map +1 -0
- package/lib/aws/ec2/security-group.js +4 -0
- package/lib/aws/ec2/security-group.js.map +1 -0
- package/lib/aws/ec2/security-group.provider.d.ts +6 -0
- package/lib/aws/ec2/security-group.provider.d.ts.map +1 -0
- package/lib/aws/ec2/security-group.provider.js +291 -0
- package/lib/aws/ec2/security-group.provider.js.map +1 -0
- package/lib/aws/ec2/subnet.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.provider.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.provider.js +33 -30
- package/lib/aws/ec2/subnet.provider.js.map +1 -1
- package/lib/aws/ec2/vpc-endpoint.d.ts +176 -0
- package/lib/aws/ec2/vpc-endpoint.d.ts.map +1 -0
- package/lib/aws/ec2/vpc-endpoint.js +4 -0
- package/lib/aws/ec2/vpc-endpoint.js.map +1 -0
- package/lib/aws/ec2/vpc-endpoint.provider.d.ts +6 -0
- package/lib/aws/ec2/vpc-endpoint.provider.d.ts.map +1 -0
- package/lib/aws/ec2/vpc-endpoint.provider.js +315 -0
- package/lib/aws/ec2/vpc-endpoint.provider.js.map +1 -0
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.js +38 -32
- package/lib/aws/ec2/vpc.provider.js.map +1 -1
- package/lib/aws/index.d.ts +2 -2
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +1 -1
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/lambda/function.d.ts.map +1 -1
- package/lib/aws/lambda/function.invoke.d.ts.map +1 -1
- package/lib/aws/lambda/function.invoke.js.map +1 -1
- package/lib/aws/lambda/function.js.map +1 -1
- package/lib/aws/lambda/function.provider.d.ts.map +1 -1
- package/lib/aws/lambda/function.provider.js +19 -23
- package/lib/aws/lambda/function.provider.js.map +1 -1
- package/lib/aws/sqs/queue.d.ts +1 -2
- package/lib/aws/sqs/queue.d.ts.map +1 -1
- package/lib/aws/sqs/queue.event-source.d.ts.map +1 -1
- package/lib/aws/sqs/queue.event-source.js +2 -2
- package/lib/aws/sqs/queue.event-source.js.map +1 -1
- package/lib/aws/sqs/queue.js.map +1 -1
- package/lib/aws/sqs/queue.provider.d.ts +1 -2
- package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
- package/lib/aws/sqs/queue.provider.js +35 -20
- package/lib/aws/sqs/queue.provider.js.map +1 -1
- package/lib/aws/sqs/queue.send-message.d.ts.map +1 -1
- package/lib/aws/sqs/queue.send-message.js.map +1 -1
- package/lib/binding.d.ts.map +1 -1
- package/lib/cli/components/PlanProgress.d.ts.map +1 -1
- package/lib/cli/components/PlanProgress.js.map +1 -1
- package/lib/cli/index.d.ts +8 -4
- package/lib/cli/index.d.ts.map +1 -1
- package/lib/cli/index.js.map +1 -1
- package/lib/cloudflare/kv/namespace.binding.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.binding.js.map +1 -1
- package/lib/cloudflare/kv/namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.d.ts +1 -2
- package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.js +11 -8
- package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
- package/lib/cloudflare/r2/bucket.binding.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.binding.js +1 -1
- package/lib/cloudflare/r2/bucket.binding.js.map +1 -1
- package/lib/cloudflare/r2/bucket.d.ts +1 -1
- package/lib/cloudflare/r2/bucket.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.d.ts +1 -2
- package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.js +20 -14
- package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
- package/lib/cloudflare/worker/worker.d.ts +2 -2
- package/lib/cloudflare/worker/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.d.ts +1 -2
- package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.js +40 -13
- package/lib/cloudflare/worker/worker.provider.js.map +1 -1
- package/lib/instance-id.d.ts +5 -1
- package/lib/instance-id.d.ts.map +1 -1
- package/lib/instance-id.js +4 -0
- package/lib/instance-id.js.map +1 -1
- package/lib/output.d.ts.map +1 -1
- package/lib/output.js.map +1 -1
- package/lib/physical-name.d.ts +15 -4
- package/lib/physical-name.d.ts.map +1 -1
- package/lib/physical-name.js +12 -3
- package/lib/physical-name.js.map +1 -1
- package/lib/plan.d.ts +2 -2
- package/lib/plan.d.ts.map +1 -1
- package/lib/plan.js +73 -24
- package/lib/plan.js.map +1 -1
- package/lib/provider.d.ts +1 -2
- package/lib/provider.d.ts.map +1 -1
- package/lib/resource.d.ts +3 -2
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js.map +1 -1
- package/lib/runtime.d.ts.map +1 -1
- package/lib/tags.d.ts +12 -0
- package/lib/tags.d.ts.map +1 -1
- package/lib/tags.js +24 -0
- package/lib/tags.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +51 -51
- package/src/apply.ts +316 -297
- package/src/aws/client.ts +22 -1
- package/src/aws/dynamodb/secondary-index.ts +5 -5
- package/src/aws/dynamodb/table.provider.ts +15 -4
- package/src/aws/dynamodb/table.ts +8 -11
- package/src/aws/ec2/egress-only-igw.provider.ts +181 -0
- package/src/aws/ec2/egress-only-igw.ts +77 -0
- package/src/aws/ec2/eip.provider.ts +191 -0
- package/src/aws/ec2/eip.ts +106 -0
- package/src/aws/ec2/index.ts +18 -0
- package/src/aws/ec2/internet-gateway.provider.ts +15 -6
- package/src/aws/ec2/internet-gateway.ts +6 -6
- package/src/aws/ec2/nat-gateway.provider.ts +341 -0
- package/src/aws/ec2/nat-gateway.ts +155 -0
- package/src/aws/ec2/network-acl-association.provider.ts +181 -0
- package/src/aws/ec2/network-acl-association.ts +60 -0
- package/src/aws/ec2/network-acl-entry.provider.ts +218 -0
- package/src/aws/ec2/network-acl-entry.ts +140 -0
- package/src/aws/ec2/network-acl.provider.ts +195 -0
- package/src/aws/ec2/network-acl.ts +102 -0
- package/src/aws/ec2/route-table-association.provider.ts +1 -2
- package/src/aws/ec2/route-table-association.ts +6 -6
- package/src/aws/ec2/route-table.provider.ts +1 -2
- package/src/aws/ec2/route-table.ts +6 -6
- package/src/aws/ec2/route.provider.ts +1 -2
- package/src/aws/ec2/route.ts +6 -6
- package/src/aws/ec2/security-group-rule.provider.ts +264 -0
- package/src/aws/ec2/security-group-rule.ts +151 -0
- package/src/aws/ec2/security-group.provider.ts +392 -0
- package/src/aws/ec2/security-group.ts +182 -0
- package/src/aws/ec2/subnet.provider.ts +57 -56
- package/src/aws/ec2/subnet.ts +6 -6
- package/src/aws/ec2/vpc-endpoint.provider.ts +466 -0
- package/src/aws/ec2/vpc-endpoint.ts +213 -0
- package/src/aws/ec2/vpc.provider.ts +60 -52
- package/src/aws/ec2/vpc.ts +6 -6
- package/src/aws/index.ts +9 -0
- package/src/aws/lambda/function.invoke.ts +4 -2
- package/src/aws/lambda/function.provider.ts +42 -31
- package/src/aws/lambda/function.ts +4 -2
- package/src/aws/sqs/queue.event-source.ts +12 -14
- package/src/aws/sqs/queue.provider.ts +40 -24
- package/src/aws/sqs/queue.send-message.ts +4 -2
- package/src/aws/sqs/queue.ts +1 -8
- package/src/binding.ts +7 -7
- package/src/cli/components/PlanProgress.tsx +3 -2
- package/src/cloudflare/kv/namespace.binding.ts +4 -2
- package/src/cloudflare/kv/namespace.provider.ts +18 -16
- package/src/cloudflare/kv/namespace.ts +6 -6
- package/src/cloudflare/r2/bucket.binding.ts +5 -3
- package/src/cloudflare/r2/bucket.provider.ts +34 -30
- package/src/cloudflare/r2/bucket.ts +7 -7
- package/src/cloudflare/worker/worker.provider.ts +39 -13
- package/src/cloudflare/worker/worker.ts +2 -2
- package/src/instance-id.ts +5 -1
- package/src/output.ts +5 -3
- package/src/physical-name.ts +27 -5
- package/src/plan.ts +108 -41
- package/src/provider.ts +12 -12
- package/src/resource.ts +17 -6
- package/src/runtime.ts +2 -2
- package/src/tags.ts +29 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Input } from "../../input.ts";
|
|
2
|
+
import { Resource } from "../../resource.ts";
|
|
3
|
+
import type { SecurityGroupId } from "./security-group.ts";
|
|
4
|
+
|
|
5
|
+
export const SecurityGroupRule = Resource<{
|
|
6
|
+
<const ID extends string, const Props extends SecurityGroupRuleProps>(
|
|
7
|
+
id: ID,
|
|
8
|
+
props: Props,
|
|
9
|
+
): SecurityGroupRule<ID, Props>;
|
|
10
|
+
}>("AWS.EC2.SecurityGroupRule");
|
|
11
|
+
|
|
12
|
+
export interface SecurityGroupRule<
|
|
13
|
+
ID extends string = string,
|
|
14
|
+
Props extends SecurityGroupRuleProps = SecurityGroupRuleProps,
|
|
15
|
+
> extends Resource<
|
|
16
|
+
"AWS.EC2.SecurityGroupRule",
|
|
17
|
+
ID,
|
|
18
|
+
Props,
|
|
19
|
+
SecurityGroupRuleAttrs<Input.Resolve<Props>>,
|
|
20
|
+
SecurityGroupRule
|
|
21
|
+
> {}
|
|
22
|
+
|
|
23
|
+
export type SecurityGroupRuleId<ID extends string = string> = `sgr-${ID}`;
|
|
24
|
+
export const SecurityGroupRuleId = <ID extends string>(
|
|
25
|
+
id: ID,
|
|
26
|
+
): ID & SecurityGroupRuleId<ID> => `sgr-${id}` as ID & SecurityGroupRuleId<ID>;
|
|
27
|
+
|
|
28
|
+
export interface SecurityGroupRuleProps {
|
|
29
|
+
/**
|
|
30
|
+
* The ID of the security group.
|
|
31
|
+
*/
|
|
32
|
+
groupId: Input<SecurityGroupId>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether this is an ingress (inbound) or egress (outbound) rule.
|
|
36
|
+
*/
|
|
37
|
+
type: "ingress" | "egress";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The IP protocol name or number.
|
|
41
|
+
* Use -1 to specify all protocols.
|
|
42
|
+
*/
|
|
43
|
+
ipProtocol: string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The start of the port range.
|
|
47
|
+
* For ICMP, use the ICMP type number.
|
|
48
|
+
*/
|
|
49
|
+
fromPort?: number;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The end of the port range.
|
|
53
|
+
* For ICMP, use the ICMP code.
|
|
54
|
+
*/
|
|
55
|
+
toPort?: number;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* IPv4 CIDR range to allow.
|
|
59
|
+
*/
|
|
60
|
+
cidrIpv4?: string;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* IPv6 CIDR range to allow.
|
|
64
|
+
*/
|
|
65
|
+
cidrIpv6?: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* ID of a security group to allow traffic from/to.
|
|
69
|
+
*/
|
|
70
|
+
referencedGroupId?: Input<SecurityGroupId>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* ID of a prefix list.
|
|
74
|
+
*/
|
|
75
|
+
prefixListId?: Input<string>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Description for the rule.
|
|
79
|
+
*/
|
|
80
|
+
description?: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Tags to assign to the security group rule.
|
|
84
|
+
*/
|
|
85
|
+
tags?: Record<string, Input<string>>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SecurityGroupRuleAttrs<
|
|
89
|
+
Props extends Input.Resolve<SecurityGroupRuleProps> =
|
|
90
|
+
Input.Resolve<SecurityGroupRuleProps>,
|
|
91
|
+
> {
|
|
92
|
+
/**
|
|
93
|
+
* The ID of the security group rule.
|
|
94
|
+
*/
|
|
95
|
+
securityGroupRuleId: SecurityGroupRuleId;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The ID of the security group.
|
|
99
|
+
*/
|
|
100
|
+
groupId: Props["groupId"];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The ID of the AWS account that owns the security group.
|
|
104
|
+
*/
|
|
105
|
+
groupOwnerId: string;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Whether this is an egress rule.
|
|
109
|
+
*/
|
|
110
|
+
isEgress: Props["type"] extends "egress" ? true : false;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The IP protocol.
|
|
114
|
+
*/
|
|
115
|
+
ipProtocol: Props["ipProtocol"];
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The start of the port range.
|
|
119
|
+
*/
|
|
120
|
+
fromPort?: number;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The end of the port range.
|
|
124
|
+
*/
|
|
125
|
+
toPort?: number;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The IPv4 CIDR range.
|
|
129
|
+
*/
|
|
130
|
+
cidrIpv4?: Props["cidrIpv4"];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The IPv6 CIDR range.
|
|
134
|
+
*/
|
|
135
|
+
cidrIpv6?: Props["cidrIpv6"];
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The ID of the referenced security group.
|
|
139
|
+
*/
|
|
140
|
+
referencedGroupId?: string;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* The ID of the prefix list.
|
|
144
|
+
*/
|
|
145
|
+
prefixListId?: string;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The description.
|
|
149
|
+
*/
|
|
150
|
+
description?: Props["description"];
|
|
151
|
+
}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import * as EC2 from "itty-aws/ec2";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Schedule from "effect/Schedule";
|
|
4
|
+
|
|
5
|
+
import { createPhysicalName } from "../../physical-name.ts";
|
|
6
|
+
import { createTagger, createTagsList, diffTags } from "../../tags.ts";
|
|
7
|
+
import { Account } from "../account.ts";
|
|
8
|
+
import { Region } from "../region.ts";
|
|
9
|
+
import { EC2Client } from "./client.ts";
|
|
10
|
+
import {
|
|
11
|
+
type SecurityGroupArn,
|
|
12
|
+
type SecurityGroupRuleData,
|
|
13
|
+
SecurityGroup,
|
|
14
|
+
type SecurityGroupAttrs,
|
|
15
|
+
type SecurityGroupId,
|
|
16
|
+
} from "./security-group.ts";
|
|
17
|
+
import type { VpcId } from "./vpc.ts";
|
|
18
|
+
|
|
19
|
+
export const securityGroupProvider = () =>
|
|
20
|
+
SecurityGroup.provider.effect(
|
|
21
|
+
Effect.gen(function* () {
|
|
22
|
+
const ec2 = yield* EC2Client;
|
|
23
|
+
const region = yield* Region;
|
|
24
|
+
const accountId = yield* Account;
|
|
25
|
+
const tagged = yield* createTagger();
|
|
26
|
+
|
|
27
|
+
const createTags = (
|
|
28
|
+
id: string,
|
|
29
|
+
tags?: Record<string, string>,
|
|
30
|
+
): Record<string, string> => ({
|
|
31
|
+
Name: id,
|
|
32
|
+
...tagged(id),
|
|
33
|
+
...tags,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const createGroupName = (id: string, name: string | undefined) =>
|
|
37
|
+
Effect.gen(function* () {
|
|
38
|
+
if (name) return name;
|
|
39
|
+
return yield* createPhysicalName({ id, maxLength: 255 });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const describeSecurityGroup = (groupId: string) =>
|
|
43
|
+
ec2.describeSecurityGroups({ GroupIds: [groupId] }).pipe(
|
|
44
|
+
Effect.map((r) => r.SecurityGroups?.[0]),
|
|
45
|
+
Effect.flatMap((sg) =>
|
|
46
|
+
sg
|
|
47
|
+
? Effect.succeed(sg)
|
|
48
|
+
: Effect.fail(new Error(`Security Group ${groupId} not found`)),
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const describeSecurityGroupRules = (groupId: string) =>
|
|
53
|
+
ec2.describeSecurityGroupRules({
|
|
54
|
+
Filters: [{ Name: "group-id", Values: [groupId] }],
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const toAttrs = (
|
|
58
|
+
sg: EC2.SecurityGroup,
|
|
59
|
+
rules: EC2.SecurityGroupRule[],
|
|
60
|
+
): SecurityGroupAttrs => ({
|
|
61
|
+
groupId: sg.GroupId as SecurityGroupId,
|
|
62
|
+
groupArn:
|
|
63
|
+
`arn:aws:ec2:${region}:${accountId}:security-group/${sg.GroupId as SecurityGroupId}` as SecurityGroupArn,
|
|
64
|
+
groupName: sg.GroupName!,
|
|
65
|
+
description: sg.Description!,
|
|
66
|
+
vpcId: sg.VpcId as VpcId,
|
|
67
|
+
ownerId: sg.OwnerId!,
|
|
68
|
+
ingressRules: rules
|
|
69
|
+
.filter((r) => !r.IsEgress)
|
|
70
|
+
.map((r) => ({
|
|
71
|
+
securityGroupRuleId: r.SecurityGroupRuleId!,
|
|
72
|
+
ipProtocol: r.IpProtocol!,
|
|
73
|
+
fromPort: r.FromPort,
|
|
74
|
+
toPort: r.ToPort,
|
|
75
|
+
cidrIpv4: r.CidrIpv4,
|
|
76
|
+
cidrIpv6: r.CidrIpv6,
|
|
77
|
+
referencedGroupId: r.ReferencedGroupInfo?.GroupId,
|
|
78
|
+
prefixListId: r.PrefixListId,
|
|
79
|
+
description: r.Description,
|
|
80
|
+
isEgress: false as const,
|
|
81
|
+
})),
|
|
82
|
+
egressRules: rules
|
|
83
|
+
.filter((r) => r.IsEgress)
|
|
84
|
+
.map((r) => ({
|
|
85
|
+
securityGroupRuleId: r.SecurityGroupRuleId!,
|
|
86
|
+
ipProtocol: r.IpProtocol!,
|
|
87
|
+
fromPort: r.FromPort,
|
|
88
|
+
toPort: r.ToPort,
|
|
89
|
+
cidrIpv4: r.CidrIpv4,
|
|
90
|
+
cidrIpv6: r.CidrIpv6,
|
|
91
|
+
referencedGroupId: r.ReferencedGroupInfo?.GroupId,
|
|
92
|
+
prefixListId: r.PrefixListId,
|
|
93
|
+
description: r.Description,
|
|
94
|
+
isEgress: true as const,
|
|
95
|
+
})),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const toIpPermission = (
|
|
99
|
+
rule: SecurityGroupRuleData,
|
|
100
|
+
): EC2.IpPermission => ({
|
|
101
|
+
IpProtocol: rule.ipProtocol,
|
|
102
|
+
FromPort: rule.fromPort,
|
|
103
|
+
ToPort: rule.toPort,
|
|
104
|
+
IpRanges: rule.cidrIpv4
|
|
105
|
+
? [{ CidrIp: rule.cidrIpv4, Description: rule.description }]
|
|
106
|
+
: undefined,
|
|
107
|
+
Ipv6Ranges: rule.cidrIpv6
|
|
108
|
+
? [{ CidrIpv6: rule.cidrIpv6, Description: rule.description }]
|
|
109
|
+
: undefined,
|
|
110
|
+
UserIdGroupPairs: rule.referencedGroupId
|
|
111
|
+
? [
|
|
112
|
+
{
|
|
113
|
+
GroupId: rule.referencedGroupId as string,
|
|
114
|
+
Description: rule.description,
|
|
115
|
+
},
|
|
116
|
+
]
|
|
117
|
+
: undefined,
|
|
118
|
+
PrefixListIds: rule.prefixListId
|
|
119
|
+
? [
|
|
120
|
+
{
|
|
121
|
+
PrefixListId: rule.prefixListId as string,
|
|
122
|
+
Description: rule.description,
|
|
123
|
+
},
|
|
124
|
+
]
|
|
125
|
+
: undefined,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
stables: ["groupId", "groupArn", "ownerId"],
|
|
130
|
+
|
|
131
|
+
read: Effect.fn(function* ({ output }) {
|
|
132
|
+
if (!output) return undefined;
|
|
133
|
+
const sg = yield* describeSecurityGroup(output.groupId);
|
|
134
|
+
const rulesResult = yield* describeSecurityGroupRules(output.groupId);
|
|
135
|
+
return toAttrs(sg, rulesResult.SecurityGroupRules ?? []);
|
|
136
|
+
}),
|
|
137
|
+
|
|
138
|
+
diff: Effect.fn(function* ({ id, news, olds, output }) {
|
|
139
|
+
// VPC change requires replacement
|
|
140
|
+
if (news.vpcId !== olds.vpcId) {
|
|
141
|
+
return { action: "replace" };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Group name change requires replacement
|
|
145
|
+
const newGroupName = yield* createGroupName(id, news.groupName);
|
|
146
|
+
if (newGroupName !== output.groupName) {
|
|
147
|
+
return { action: "replace" };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Other changes can be updated in-place
|
|
151
|
+
}),
|
|
152
|
+
|
|
153
|
+
create: Effect.fn(function* ({ id, news, session }) {
|
|
154
|
+
const groupName = yield* createGroupName(id, news.groupName);
|
|
155
|
+
|
|
156
|
+
yield* session.note(`Creating Security Group: ${groupName}`);
|
|
157
|
+
|
|
158
|
+
const result = yield* ec2.createSecurityGroup({
|
|
159
|
+
GroupName: groupName,
|
|
160
|
+
Description: news.description ?? "Managed by Alchemy",
|
|
161
|
+
VpcId: news.vpcId as string,
|
|
162
|
+
TagSpecifications: [
|
|
163
|
+
{
|
|
164
|
+
ResourceType: "security-group",
|
|
165
|
+
Tags: createTagsList(createTags(id, news.tags)),
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
DryRun: false,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const groupId = result.GroupId! as SecurityGroupId;
|
|
172
|
+
yield* session.note(`Security Group created: ${groupId}`);
|
|
173
|
+
|
|
174
|
+
// Revoke the default egress rule if we have custom egress rules
|
|
175
|
+
if (news.egress && news.egress.length > 0) {
|
|
176
|
+
yield* ec2
|
|
177
|
+
.revokeSecurityGroupEgress({
|
|
178
|
+
GroupId: groupId,
|
|
179
|
+
IpPermissions: [
|
|
180
|
+
{
|
|
181
|
+
IpProtocol: "-1",
|
|
182
|
+
IpRanges: [{ CidrIp: "0.0.0.0/0" }],
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
DryRun: false,
|
|
186
|
+
})
|
|
187
|
+
.pipe(
|
|
188
|
+
Effect.catchTag(
|
|
189
|
+
"InvalidPermission.NotFound",
|
|
190
|
+
() => Effect.void,
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Add ingress rules
|
|
196
|
+
if (news.ingress && news.ingress.length > 0) {
|
|
197
|
+
yield* ec2.authorizeSecurityGroupIngress({
|
|
198
|
+
GroupId: groupId,
|
|
199
|
+
IpPermissions: news.ingress.map(toIpPermission),
|
|
200
|
+
DryRun: false,
|
|
201
|
+
});
|
|
202
|
+
yield* session.note(`Added ${news.ingress.length} ingress rules`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Add egress rules
|
|
206
|
+
if (news.egress && news.egress.length > 0) {
|
|
207
|
+
yield* ec2.authorizeSecurityGroupEgress({
|
|
208
|
+
GroupId: groupId,
|
|
209
|
+
IpPermissions: news.egress.map(toIpPermission),
|
|
210
|
+
DryRun: false,
|
|
211
|
+
});
|
|
212
|
+
yield* session.note(`Added ${news.egress.length} egress rules`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Fetch the final state
|
|
216
|
+
const sg = yield* describeSecurityGroup(groupId);
|
|
217
|
+
const rulesResult = yield* describeSecurityGroupRules(groupId);
|
|
218
|
+
return toAttrs(sg, rulesResult.SecurityGroupRules ?? []);
|
|
219
|
+
}),
|
|
220
|
+
|
|
221
|
+
update: Effect.fn(function* ({ id, news, olds, output, session }) {
|
|
222
|
+
const groupId = output.groupId;
|
|
223
|
+
|
|
224
|
+
// Handle description update (requires modifying the group)
|
|
225
|
+
if (news.description !== olds.description) {
|
|
226
|
+
yield* ec2.modifySecurityGroupRules({
|
|
227
|
+
GroupId: groupId,
|
|
228
|
+
// Description can't actually be changed after creation in EC2
|
|
229
|
+
// This is a no-op but we log it
|
|
230
|
+
SecurityGroupRules: [],
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Handle tag updates
|
|
235
|
+
const newTags = createTags(id, news.tags);
|
|
236
|
+
const oldTags =
|
|
237
|
+
(yield* ec2
|
|
238
|
+
.describeTags({
|
|
239
|
+
Filters: [
|
|
240
|
+
{ Name: "resource-id", Values: [groupId] },
|
|
241
|
+
{ Name: "resource-type", Values: ["security-group"] },
|
|
242
|
+
],
|
|
243
|
+
})
|
|
244
|
+
.pipe(
|
|
245
|
+
Effect.map(
|
|
246
|
+
(r) =>
|
|
247
|
+
Object.fromEntries(
|
|
248
|
+
r.Tags?.map((t) => [t.Key!, t.Value!]) ?? [],
|
|
249
|
+
) as Record<string, string>,
|
|
250
|
+
),
|
|
251
|
+
)) ?? {};
|
|
252
|
+
|
|
253
|
+
const { removed, upsert } = diffTags(oldTags, newTags);
|
|
254
|
+
|
|
255
|
+
if (removed.length > 0) {
|
|
256
|
+
yield* ec2.deleteTags({
|
|
257
|
+
Resources: [groupId],
|
|
258
|
+
Tags: removed.map((key) => ({ Key: key })),
|
|
259
|
+
DryRun: false,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (upsert.length > 0) {
|
|
263
|
+
yield* ec2.createTags({
|
|
264
|
+
Resources: [groupId],
|
|
265
|
+
Tags: upsert,
|
|
266
|
+
DryRun: false,
|
|
267
|
+
});
|
|
268
|
+
yield* session.note("Updated tags");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Handle rule updates - simple approach: revoke all, then add all
|
|
272
|
+
// Get current rules
|
|
273
|
+
const currentRulesResult = yield* describeSecurityGroupRules(groupId);
|
|
274
|
+
const currentRules = currentRulesResult.SecurityGroupRules ?? [];
|
|
275
|
+
|
|
276
|
+
// Revoke existing ingress rules (except default)
|
|
277
|
+
const currentIngress = currentRules.filter((r) => !r.IsEgress);
|
|
278
|
+
if (currentIngress.length > 0) {
|
|
279
|
+
yield* ec2
|
|
280
|
+
.revokeSecurityGroupIngress({
|
|
281
|
+
GroupId: groupId,
|
|
282
|
+
SecurityGroupRuleIds: currentIngress.map(
|
|
283
|
+
(r) => r.SecurityGroupRuleId!,
|
|
284
|
+
),
|
|
285
|
+
DryRun: false,
|
|
286
|
+
})
|
|
287
|
+
.pipe(
|
|
288
|
+
Effect.catchTag(
|
|
289
|
+
"InvalidPermission.NotFound",
|
|
290
|
+
() => Effect.void,
|
|
291
|
+
),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Revoke existing egress rules
|
|
296
|
+
const currentEgress = currentRules.filter((r) => r.IsEgress);
|
|
297
|
+
if (currentEgress.length > 0) {
|
|
298
|
+
yield* ec2
|
|
299
|
+
.revokeSecurityGroupEgress({
|
|
300
|
+
GroupId: groupId,
|
|
301
|
+
SecurityGroupRuleIds: currentEgress.map(
|
|
302
|
+
(r) => r.SecurityGroupRuleId!,
|
|
303
|
+
),
|
|
304
|
+
DryRun: false,
|
|
305
|
+
})
|
|
306
|
+
.pipe(
|
|
307
|
+
Effect.catchTag(
|
|
308
|
+
"InvalidPermission.NotFound",
|
|
309
|
+
() => Effect.void,
|
|
310
|
+
),
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Add new ingress rules
|
|
315
|
+
if (news.ingress && news.ingress.length > 0) {
|
|
316
|
+
yield* ec2.authorizeSecurityGroupIngress({
|
|
317
|
+
GroupId: groupId,
|
|
318
|
+
IpPermissions: news.ingress.map(toIpPermission),
|
|
319
|
+
DryRun: false,
|
|
320
|
+
});
|
|
321
|
+
yield* session.note(
|
|
322
|
+
`Updated ingress rules (${news.ingress.length} rules)`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Add new egress rules (or restore default)
|
|
327
|
+
if (news.egress && news.egress.length > 0) {
|
|
328
|
+
yield* ec2.authorizeSecurityGroupEgress({
|
|
329
|
+
GroupId: groupId,
|
|
330
|
+
IpPermissions: news.egress.map(toIpPermission),
|
|
331
|
+
DryRun: false,
|
|
332
|
+
});
|
|
333
|
+
yield* session.note(
|
|
334
|
+
`Updated egress rules (${news.egress.length} rules)`,
|
|
335
|
+
);
|
|
336
|
+
} else {
|
|
337
|
+
// Restore default egress rule
|
|
338
|
+
yield* ec2.authorizeSecurityGroupEgress({
|
|
339
|
+
GroupId: groupId,
|
|
340
|
+
IpPermissions: [
|
|
341
|
+
{
|
|
342
|
+
IpProtocol: "-1",
|
|
343
|
+
IpRanges: [{ CidrIp: "0.0.0.0/0" }],
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
DryRun: false,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Fetch the final state
|
|
351
|
+
const sg = yield* describeSecurityGroup(groupId);
|
|
352
|
+
const rulesResult = yield* describeSecurityGroupRules(groupId);
|
|
353
|
+
return toAttrs(sg, rulesResult.SecurityGroupRules ?? []);
|
|
354
|
+
}),
|
|
355
|
+
|
|
356
|
+
delete: Effect.fn(function* ({ output, session }) {
|
|
357
|
+
const groupId = output.groupId;
|
|
358
|
+
|
|
359
|
+
yield* session.note(`Deleting Security Group: ${groupId}`);
|
|
360
|
+
|
|
361
|
+
yield* ec2
|
|
362
|
+
.deleteSecurityGroup({
|
|
363
|
+
GroupId: groupId,
|
|
364
|
+
DryRun: false,
|
|
365
|
+
})
|
|
366
|
+
.pipe(
|
|
367
|
+
Effect.catchTag("InvalidGroup.NotFound", () => Effect.void),
|
|
368
|
+
// Retry on dependency violations (e.g., ENIs still using the security group)
|
|
369
|
+
Effect.retry({
|
|
370
|
+
while: (e) => {
|
|
371
|
+
return (
|
|
372
|
+
e._tag === "DependencyViolation" ||
|
|
373
|
+
(e._tag === "ValidationError" &&
|
|
374
|
+
e.message?.includes("DependencyViolation"))
|
|
375
|
+
);
|
|
376
|
+
},
|
|
377
|
+
schedule: Schedule.exponential(1000, 1.5).pipe(
|
|
378
|
+
Schedule.intersect(Schedule.recurs(15)),
|
|
379
|
+
Schedule.tapOutput(([, attempt]) =>
|
|
380
|
+
session.note(
|
|
381
|
+
`Waiting for dependencies to clear... (attempt ${attempt + 1})`,
|
|
382
|
+
),
|
|
383
|
+
),
|
|
384
|
+
),
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
yield* session.note(`Security Group ${groupId} deleted`);
|
|
389
|
+
}),
|
|
390
|
+
};
|
|
391
|
+
}),
|
|
392
|
+
);
|