@stacksjs/ts-cloud 0.1.9 → 0.1.14

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.
Files changed (150) hide show
  1. package/README.md +39 -377
  2. package/dist/bin/cli.js +1047 -424
  3. package/dist/index.d.ts +36 -3
  4. package/dist/index.js +76430 -7096
  5. package/package.json +7 -8
  6. package/dist/aws/acm.d.ts +0 -129
  7. package/dist/aws/application-autoscaling.d.ts +0 -282
  8. package/dist/aws/bedrock.d.ts +0 -2292
  9. package/dist/aws/client.d.ts +0 -79
  10. package/dist/aws/cloudformation.d.ts +0 -105
  11. package/dist/aws/cloudfront.d.ts +0 -265
  12. package/dist/aws/cloudwatch-logs.d.ts +0 -48
  13. package/dist/aws/comprehend.d.ts +0 -505
  14. package/dist/aws/connect.d.ts +0 -377
  15. package/dist/aws/deploy-imap.d.ts +0 -14
  16. package/dist/aws/dynamodb.d.ts +0 -176
  17. package/dist/aws/ec2.d.ts +0 -272
  18. package/dist/aws/ecr.d.ts +0 -149
  19. package/dist/aws/ecs.d.ts +0 -162
  20. package/dist/aws/elasticache.d.ts +0 -71
  21. package/dist/aws/elbv2.d.ts +0 -248
  22. package/dist/aws/email.d.ts +0 -175
  23. package/dist/aws/eventbridge.d.ts +0 -142
  24. package/dist/aws/iam.d.ts +0 -638
  25. package/dist/aws/imap-server.d.ts +0 -119
  26. package/dist/aws/index.d.ts +0 -192
  27. package/dist/aws/kendra.d.ts +0 -782
  28. package/dist/aws/lambda.d.ts +0 -232
  29. package/dist/aws/opensearch.d.ts +0 -87
  30. package/dist/aws/personalize.d.ts +0 -516
  31. package/dist/aws/polly.d.ts +0 -214
  32. package/dist/aws/rds.d.ts +0 -240
  33. package/dist/aws/rekognition.d.ts +0 -543
  34. package/dist/aws/route53-domains.d.ts +0 -113
  35. package/dist/aws/route53.d.ts +0 -215
  36. package/dist/aws/s3.d.ts +0 -212
  37. package/dist/aws/scheduler.d.ts +0 -140
  38. package/dist/aws/secrets-manager.d.ts +0 -170
  39. package/dist/aws/ses.d.ts +0 -288
  40. package/dist/aws/setup-phone.d.ts +0 -0
  41. package/dist/aws/setup-sms.d.ts +0 -115
  42. package/dist/aws/sms.d.ts +0 -304
  43. package/dist/aws/smtp-server.d.ts +0 -61
  44. package/dist/aws/sns.d.ts +0 -117
  45. package/dist/aws/sqs.d.ts +0 -65
  46. package/dist/aws/ssm.d.ts +0 -179
  47. package/dist/aws/sts.d.ts +0 -15
  48. package/dist/aws/support.d.ts +0 -104
  49. package/dist/aws/test-imap.d.ts +0 -0
  50. package/dist/aws/textract.d.ts +0 -403
  51. package/dist/aws/transcribe.d.ts +0 -60
  52. package/dist/aws/translate.d.ts +0 -358
  53. package/dist/aws/voice.d.ts +0 -219
  54. package/dist/config.d.ts +0 -7
  55. package/dist/deploy/index.d.ts +0 -2
  56. package/dist/deploy/static-site-external-dns.d.ts +0 -51
  57. package/dist/deploy/static-site.d.ts +0 -71
  58. package/dist/dns/cloudflare.d.ts +0 -52
  59. package/dist/dns/godaddy.d.ts +0 -38
  60. package/dist/dns/index.d.ts +0 -45
  61. package/dist/dns/porkbun.d.ts +0 -18
  62. package/dist/dns/route53-adapter.d.ts +0 -38
  63. package/dist/dns/types.d.ts +0 -77
  64. package/dist/dns/validator.d.ts +0 -78
  65. package/dist/generators/index.d.ts +0 -1
  66. package/dist/generators/infrastructure.d.ts +0 -30
  67. package/dist/push/apns.d.ts +0 -60
  68. package/dist/push/fcm.d.ts +0 -117
  69. package/dist/push/index.d.ts +0 -14
  70. package/dist/security/pre-deploy-scanner.d.ts +0 -69
  71. package/dist/ssl/acme-client.d.ts +0 -67
  72. package/dist/ssl/index.d.ts +0 -2
  73. package/dist/ssl/letsencrypt.d.ts +0 -48
  74. package/dist/types.d.ts +0 -1
  75. package/dist/utils/cli.d.ts +0 -123
  76. package/dist/validation/index.d.ts +0 -1
  77. package/dist/validation/template.d.ts +0 -23
  78. package/src/aws/acm.ts +0 -768
  79. package/src/aws/application-autoscaling.ts +0 -845
  80. package/src/aws/bedrock.ts +0 -4074
  81. package/src/aws/client.ts +0 -891
  82. package/src/aws/cloudformation.ts +0 -896
  83. package/src/aws/cloudfront.ts +0 -1531
  84. package/src/aws/cloudwatch-logs.ts +0 -154
  85. package/src/aws/comprehend.ts +0 -839
  86. package/src/aws/connect.ts +0 -1056
  87. package/src/aws/deploy-imap.ts +0 -384
  88. package/src/aws/dynamodb.ts +0 -340
  89. package/src/aws/ec2.ts +0 -1385
  90. package/src/aws/ecr.ts +0 -621
  91. package/src/aws/ecs.ts +0 -615
  92. package/src/aws/elasticache.ts +0 -301
  93. package/src/aws/elbv2.ts +0 -942
  94. package/src/aws/email.ts +0 -928
  95. package/src/aws/eventbridge.ts +0 -248
  96. package/src/aws/iam.ts +0 -1689
  97. package/src/aws/imap-server.ts +0 -2100
  98. package/src/aws/index.ts +0 -213
  99. package/src/aws/kendra.ts +0 -1097
  100. package/src/aws/lambda.ts +0 -786
  101. package/src/aws/opensearch.ts +0 -158
  102. package/src/aws/personalize.ts +0 -977
  103. package/src/aws/polly.ts +0 -559
  104. package/src/aws/rds.ts +0 -888
  105. package/src/aws/rekognition.ts +0 -846
  106. package/src/aws/route53-domains.ts +0 -359
  107. package/src/aws/route53.ts +0 -1046
  108. package/src/aws/s3.ts +0 -2334
  109. package/src/aws/scheduler.ts +0 -571
  110. package/src/aws/secrets-manager.ts +0 -769
  111. package/src/aws/ses.ts +0 -1081
  112. package/src/aws/setup-phone.ts +0 -104
  113. package/src/aws/setup-sms.ts +0 -580
  114. package/src/aws/sms.ts +0 -1735
  115. package/src/aws/smtp-server.ts +0 -531
  116. package/src/aws/sns.ts +0 -758
  117. package/src/aws/sqs.ts +0 -382
  118. package/src/aws/ssm.ts +0 -807
  119. package/src/aws/sts.ts +0 -92
  120. package/src/aws/support.ts +0 -391
  121. package/src/aws/test-imap.ts +0 -86
  122. package/src/aws/textract.ts +0 -780
  123. package/src/aws/transcribe.ts +0 -108
  124. package/src/aws/translate.ts +0 -641
  125. package/src/aws/voice.ts +0 -1379
  126. package/src/config.ts +0 -35
  127. package/src/deploy/index.ts +0 -7
  128. package/src/deploy/static-site-external-dns.ts +0 -945
  129. package/src/deploy/static-site.ts +0 -1175
  130. package/src/dns/cloudflare.ts +0 -548
  131. package/src/dns/godaddy.ts +0 -412
  132. package/src/dns/index.ts +0 -205
  133. package/src/dns/porkbun.ts +0 -362
  134. package/src/dns/route53-adapter.ts +0 -414
  135. package/src/dns/types.ts +0 -119
  136. package/src/dns/validator.ts +0 -369
  137. package/src/generators/index.ts +0 -5
  138. package/src/generators/infrastructure.ts +0 -1660
  139. package/src/index.ts +0 -163
  140. package/src/push/apns.ts +0 -452
  141. package/src/push/fcm.ts +0 -506
  142. package/src/push/index.ts +0 -58
  143. package/src/security/pre-deploy-scanner.ts +0 -655
  144. package/src/ssl/acme-client.ts +0 -478
  145. package/src/ssl/index.ts +0 -7
  146. package/src/ssl/letsencrypt.ts +0 -747
  147. package/src/types.ts +0 -2
  148. package/src/utils/cli.ts +0 -398
  149. package/src/validation/index.ts +0 -5
  150. package/src/validation/template.ts +0 -405
package/src/aws/ec2.ts DELETED
@@ -1,1385 +0,0 @@
1
- /**
2
- * AWS EC2 Client
3
- * Manages EC2 instances, VPCs, security groups, and related resources using direct API calls
4
- */
5
-
6
- import { AWSClient } from './client'
7
-
8
- export interface Instance {
9
- InstanceId?: string
10
- ImageId?: string
11
- InstanceType?: string
12
- State?: {
13
- Code?: number
14
- Name?: 'pending' | 'running' | 'shutting-down' | 'terminated' | 'stopping' | 'stopped'
15
- }
16
- PrivateIpAddress?: string
17
- PublicIpAddress?: string
18
- SubnetId?: string
19
- VpcId?: string
20
- SecurityGroups?: { GroupId?: string, GroupName?: string }[]
21
- Tags?: { Key?: string, Value?: string }[]
22
- LaunchTime?: string
23
- Placement?: {
24
- AvailabilityZone?: string
25
- Tenancy?: string
26
- }
27
- Architecture?: string
28
- RootDeviceType?: string
29
- RootDeviceName?: string
30
- BlockDeviceMappings?: {
31
- DeviceName?: string
32
- Ebs?: {
33
- VolumeId?: string
34
- Status?: string
35
- AttachTime?: string
36
- DeleteOnTermination?: boolean
37
- }
38
- }[]
39
- IamInstanceProfile?: {
40
- Arn?: string
41
- Id?: string
42
- }
43
- }
44
-
45
- export interface Vpc {
46
- VpcId?: string
47
- CidrBlock?: string
48
- State?: 'pending' | 'available'
49
- DhcpOptionsId?: string
50
- InstanceTenancy?: string
51
- IsDefault?: boolean
52
- Tags?: { Key?: string, Value?: string }[]
53
- }
54
-
55
- export interface Subnet {
56
- SubnetId?: string
57
- VpcId?: string
58
- CidrBlock?: string
59
- AvailabilityZone?: string
60
- AvailableIpAddressCount?: number
61
- State?: 'pending' | 'available'
62
- MapPublicIpOnLaunch?: boolean
63
- Tags?: { Key?: string, Value?: string }[]
64
- }
65
-
66
- export interface SecurityGroup {
67
- GroupId?: string
68
- GroupName?: string
69
- Description?: string
70
- VpcId?: string
71
- IpPermissions?: IpPermission[]
72
- IpPermissionsEgress?: IpPermission[]
73
- Tags?: { Key?: string, Value?: string }[]
74
- }
75
-
76
- export interface IpPermission {
77
- IpProtocol?: string
78
- FromPort?: number
79
- ToPort?: number
80
- IpRanges?: { CidrIp?: string, Description?: string }[]
81
- Ipv6Ranges?: { CidrIpv6?: string, Description?: string }[]
82
- UserIdGroupPairs?: { GroupId?: string, UserId?: string, Description?: string }[]
83
- }
84
-
85
- export interface InternetGateway {
86
- InternetGatewayId?: string
87
- Attachments?: { VpcId?: string, State?: string }[]
88
- Tags?: { Key?: string, Value?: string }[]
89
- }
90
-
91
- export interface RouteTable {
92
- RouteTableId?: string
93
- VpcId?: string
94
- Routes?: {
95
- DestinationCidrBlock?: string
96
- GatewayId?: string
97
- NatGatewayId?: string
98
- State?: string
99
- }[]
100
- Associations?: {
101
- RouteTableAssociationId?: string
102
- SubnetId?: string
103
- Main?: boolean
104
- }[]
105
- Tags?: { Key?: string, Value?: string }[]
106
- }
107
-
108
- export interface Address {
109
- PublicIp?: string
110
- AllocationId?: string
111
- AssociationId?: string
112
- InstanceId?: string
113
- NetworkInterfaceId?: string
114
- PrivateIpAddress?: string
115
- Domain?: 'vpc' | 'standard'
116
- Tags?: { Key?: string, Value?: string }[]
117
- }
118
-
119
- export interface ConsoleOutput {
120
- InstanceId?: string
121
- Output?: string
122
- Timestamp?: string
123
- }
124
-
125
- export interface InstanceStatus {
126
- InstanceId?: string
127
- InstanceState?: {
128
- Code?: number
129
- Name?: string
130
- }
131
- InstanceStatus?: {
132
- Status?: string
133
- Details?: { Name?: string, Status?: string }[]
134
- }
135
- SystemStatus?: {
136
- Status?: string
137
- Details?: { Name?: string, Status?: string }[]
138
- }
139
- }
140
-
141
- /**
142
- * EC2 client using direct API calls
143
- */
144
- export class EC2Client {
145
- private client: AWSClient
146
- private region: string
147
-
148
- constructor(region: string = 'us-east-1') {
149
- this.region = region
150
- this.client = new AWSClient()
151
- }
152
-
153
- /**
154
- * Describe EC2 instances
155
- */
156
- async describeInstances(options?: {
157
- InstanceIds?: string[]
158
- Filters?: { Name: string, Values: string[] }[]
159
- MaxResults?: number
160
- NextToken?: string
161
- }): Promise<{
162
- Reservations?: {
163
- ReservationId?: string
164
- Instances?: Instance[]
165
- }[]
166
- NextToken?: string
167
- }> {
168
- const params: Record<string, string> = {
169
- Action: 'DescribeInstances',
170
- Version: '2016-11-15',
171
- }
172
-
173
- if (options?.InstanceIds) {
174
- options.InstanceIds.forEach((id, i) => {
175
- params[`InstanceId.${i + 1}`] = id
176
- })
177
- }
178
-
179
- if (options?.Filters) {
180
- options.Filters.forEach((filter, i) => {
181
- params[`Filter.${i + 1}.Name`] = filter.Name
182
- filter.Values.forEach((val, j) => {
183
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
184
- })
185
- })
186
- }
187
-
188
- if (options?.MaxResults) {
189
- params.MaxResults = String(options.MaxResults)
190
- }
191
-
192
- if (options?.NextToken) {
193
- params.NextToken = options.NextToken
194
- }
195
-
196
- const result = await this.client.request({
197
- service: 'ec2',
198
- region: this.region,
199
- method: 'POST',
200
- path: '/',
201
- headers: {
202
- 'Content-Type': 'application/x-www-form-urlencoded',
203
- },
204
- body: new URLSearchParams(params).toString(),
205
- })
206
-
207
- // Handle EC2 XML response wrapper
208
- const response = result.DescribeInstancesResponse || result
209
-
210
- return {
211
- Reservations: this.parseReservations(response.reservationSet?.item),
212
- NextToken: response.nextToken,
213
- }
214
- }
215
-
216
- /**
217
- * Get a single instance by ID
218
- */
219
- async getInstance(instanceId: string): Promise<Instance | undefined> {
220
- const result = await this.describeInstances({ InstanceIds: [instanceId] })
221
- return result.Reservations?.[0]?.Instances?.[0]
222
- }
223
-
224
- /**
225
- * Get console output from an EC2 instance
226
- */
227
- async getConsoleOutput(instanceId: string, latest?: boolean): Promise<ConsoleOutput> {
228
- const params: Record<string, string> = {
229
- Action: 'GetConsoleOutput',
230
- Version: '2016-11-15',
231
- InstanceId: instanceId,
232
- }
233
-
234
- if (latest) {
235
- params.Latest = 'true'
236
- }
237
-
238
- const result = await this.client.request({
239
- service: 'ec2',
240
- region: this.region,
241
- method: 'POST',
242
- path: '/',
243
- headers: {
244
- 'Content-Type': 'application/x-www-form-urlencoded',
245
- },
246
- body: new URLSearchParams(params).toString(),
247
- })
248
-
249
- // Handle EC2 XML response wrapper
250
- const response = result.GetConsoleOutputResponse || result
251
-
252
- return {
253
- InstanceId: response.instanceId,
254
- Output: response.output,
255
- Timestamp: response.timestamp,
256
- }
257
- }
258
-
259
- /**
260
- * Get console output decoded (convenience method)
261
- */
262
- async getConsoleOutputDecoded(instanceId: string, options?: {
263
- latest?: boolean
264
- tailLines?: number
265
- }): Promise<string> {
266
- const result = await this.getConsoleOutput(instanceId, options?.latest)
267
-
268
- if (!result.Output) {
269
- return 'No console output available yet'
270
- }
271
-
272
- // Decode base64
273
- const decoded = Buffer.from(result.Output, 'base64').toString('utf-8')
274
-
275
- if (options?.tailLines) {
276
- const lines = decoded.split('\n')
277
- return lines.slice(-options.tailLines).join('\n')
278
- }
279
-
280
- return decoded
281
- }
282
-
283
- /**
284
- * Describe instance status
285
- */
286
- async describeInstanceStatus(options?: {
287
- InstanceIds?: string[]
288
- IncludeAllInstances?: boolean
289
- Filters?: { Name: string, Values: string[] }[]
290
- }): Promise<{
291
- InstanceStatuses?: InstanceStatus[]
292
- }> {
293
- const params: Record<string, string> = {
294
- Action: 'DescribeInstanceStatus',
295
- Version: '2016-11-15',
296
- }
297
-
298
- if (options?.InstanceIds) {
299
- options.InstanceIds.forEach((id, i) => {
300
- params[`InstanceId.${i + 1}`] = id
301
- })
302
- }
303
-
304
- if (options?.IncludeAllInstances) {
305
- params.IncludeAllInstances = 'true'
306
- }
307
-
308
- if (options?.Filters) {
309
- options.Filters.forEach((filter, i) => {
310
- params[`Filter.${i + 1}.Name`] = filter.Name
311
- filter.Values.forEach((val, j) => {
312
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
313
- })
314
- })
315
- }
316
-
317
- const result = await this.client.request({
318
- service: 'ec2',
319
- region: this.region,
320
- method: 'POST',
321
- path: '/',
322
- headers: {
323
- 'Content-Type': 'application/x-www-form-urlencoded',
324
- },
325
- body: new URLSearchParams(params).toString(),
326
- })
327
-
328
- return {
329
- InstanceStatuses: this.parseArray(result.instanceStatusSet?.item).map((item: any) => ({
330
- InstanceId: item.instanceId,
331
- InstanceState: item.instanceState ? {
332
- Code: Number.parseInt(item.instanceState.code),
333
- Name: item.instanceState.name,
334
- } : undefined,
335
- InstanceStatus: item.instanceStatus ? {
336
- Status: item.instanceStatus.status,
337
- Details: this.parseArray(item.instanceStatus.details?.item).map((d: any) => ({
338
- Name: d.name,
339
- Status: d.status,
340
- })),
341
- } : undefined,
342
- SystemStatus: item.systemStatus ? {
343
- Status: item.systemStatus.status,
344
- Details: this.parseArray(item.systemStatus.details?.item).map((d: any) => ({
345
- Name: d.name,
346
- Status: d.status,
347
- })),
348
- } : undefined,
349
- })),
350
- }
351
- }
352
-
353
- /**
354
- * Start instances
355
- */
356
- async startInstances(instanceIds: string[]): Promise<{
357
- StartingInstances?: { InstanceId?: string, CurrentState?: { Name?: string }, PreviousState?: { Name?: string } }[]
358
- }> {
359
- const params: Record<string, string> = {
360
- Action: 'StartInstances',
361
- Version: '2016-11-15',
362
- }
363
-
364
- instanceIds.forEach((id, i) => {
365
- params[`InstanceId.${i + 1}`] = id
366
- })
367
-
368
- const result = await this.client.request({
369
- service: 'ec2',
370
- region: this.region,
371
- method: 'POST',
372
- path: '/',
373
- headers: {
374
- 'Content-Type': 'application/x-www-form-urlencoded',
375
- },
376
- body: new URLSearchParams(params).toString(),
377
- })
378
-
379
- return {
380
- StartingInstances: this.parseArray(result.instancesSet?.item).map((item: any) => ({
381
- InstanceId: item.instanceId,
382
- CurrentState: item.currentState ? { Name: item.currentState.name } : undefined,
383
- PreviousState: item.previousState ? { Name: item.previousState.name } : undefined,
384
- })),
385
- }
386
- }
387
-
388
- /**
389
- * Stop instances
390
- */
391
- async stopInstances(instanceIds: string[], force?: boolean): Promise<{
392
- StoppingInstances?: { InstanceId?: string, CurrentState?: { Name?: string }, PreviousState?: { Name?: string } }[]
393
- }> {
394
- const params: Record<string, string> = {
395
- Action: 'StopInstances',
396
- Version: '2016-11-15',
397
- }
398
-
399
- instanceIds.forEach((id, i) => {
400
- params[`InstanceId.${i + 1}`] = id
401
- })
402
-
403
- if (force) {
404
- params.Force = 'true'
405
- }
406
-
407
- const result = await this.client.request({
408
- service: 'ec2',
409
- region: this.region,
410
- method: 'POST',
411
- path: '/',
412
- headers: {
413
- 'Content-Type': 'application/x-www-form-urlencoded',
414
- },
415
- body: new URLSearchParams(params).toString(),
416
- })
417
-
418
- return {
419
- StoppingInstances: this.parseArray(result.instancesSet?.item).map((item: any) => ({
420
- InstanceId: item.instanceId,
421
- CurrentState: item.currentState ? { Name: item.currentState.name } : undefined,
422
- PreviousState: item.previousState ? { Name: item.previousState.name } : undefined,
423
- })),
424
- }
425
- }
426
-
427
- /**
428
- * Reboot instances
429
- */
430
- async rebootInstances(instanceIds: string[]): Promise<void> {
431
- const params: Record<string, string> = {
432
- Action: 'RebootInstances',
433
- Version: '2016-11-15',
434
- }
435
-
436
- instanceIds.forEach((id, i) => {
437
- params[`InstanceId.${i + 1}`] = id
438
- })
439
-
440
- await this.client.request({
441
- service: 'ec2',
442
- region: this.region,
443
- method: 'POST',
444
- path: '/',
445
- headers: {
446
- 'Content-Type': 'application/x-www-form-urlencoded',
447
- },
448
- body: new URLSearchParams(params).toString(),
449
- })
450
- }
451
-
452
- /**
453
- * Terminate instances
454
- */
455
- async terminateInstances(instanceIds: string[]): Promise<{
456
- TerminatingInstances?: { InstanceId?: string, CurrentState?: { Name?: string }, PreviousState?: { Name?: string } }[]
457
- }> {
458
- const params: Record<string, string> = {
459
- Action: 'TerminateInstances',
460
- Version: '2016-11-15',
461
- }
462
-
463
- instanceIds.forEach((id, i) => {
464
- params[`InstanceId.${i + 1}`] = id
465
- })
466
-
467
- const result = await this.client.request({
468
- service: 'ec2',
469
- region: this.region,
470
- method: 'POST',
471
- path: '/',
472
- headers: {
473
- 'Content-Type': 'application/x-www-form-urlencoded',
474
- },
475
- body: new URLSearchParams(params).toString(),
476
- })
477
-
478
- return {
479
- TerminatingInstances: this.parseArray(result.instancesSet?.item).map((item: any) => ({
480
- InstanceId: item.instanceId,
481
- CurrentState: item.currentState ? { Name: item.currentState.name } : undefined,
482
- PreviousState: item.previousState ? { Name: item.previousState.name } : undefined,
483
- })),
484
- }
485
- }
486
-
487
- /**
488
- * Describe VPCs
489
- */
490
- async describeVpcs(options?: {
491
- VpcIds?: string[]
492
- Filters?: { Name: string, Values: string[] }[]
493
- }): Promise<{
494
- Vpcs?: Vpc[]
495
- }> {
496
- const params: Record<string, string> = {
497
- Action: 'DescribeVpcs',
498
- Version: '2016-11-15',
499
- }
500
-
501
- if (options?.VpcIds) {
502
- options.VpcIds.forEach((id, i) => {
503
- params[`VpcId.${i + 1}`] = id
504
- })
505
- }
506
-
507
- if (options?.Filters) {
508
- options.Filters.forEach((filter, i) => {
509
- params[`Filter.${i + 1}.Name`] = filter.Name
510
- filter.Values.forEach((val, j) => {
511
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
512
- })
513
- })
514
- }
515
-
516
- const result = await this.client.request({
517
- service: 'ec2',
518
- region: this.region,
519
- method: 'POST',
520
- path: '/',
521
- headers: {
522
- 'Content-Type': 'application/x-www-form-urlencoded',
523
- },
524
- body: new URLSearchParams(params).toString(),
525
- })
526
-
527
- return {
528
- Vpcs: this.parseArray(result.vpcSet?.item).map((item: any) => ({
529
- VpcId: item.vpcId,
530
- CidrBlock: item.cidrBlock,
531
- State: item.state,
532
- DhcpOptionsId: item.dhcpOptionsId,
533
- InstanceTenancy: item.instanceTenancy,
534
- IsDefault: item.isDefault === 'true',
535
- Tags: this.parseTags(item.tagSet?.item),
536
- })),
537
- }
538
- }
539
-
540
- /**
541
- * Describe Subnets
542
- */
543
- async describeSubnets(options?: {
544
- SubnetIds?: string[]
545
- Filters?: { Name: string, Values: string[] }[]
546
- }): Promise<{
547
- Subnets?: Subnet[]
548
- }> {
549
- const params: Record<string, string> = {
550
- Action: 'DescribeSubnets',
551
- Version: '2016-11-15',
552
- }
553
-
554
- if (options?.SubnetIds) {
555
- options.SubnetIds.forEach((id, i) => {
556
- params[`SubnetId.${i + 1}`] = id
557
- })
558
- }
559
-
560
- if (options?.Filters) {
561
- options.Filters.forEach((filter, i) => {
562
- params[`Filter.${i + 1}.Name`] = filter.Name
563
- filter.Values.forEach((val, j) => {
564
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
565
- })
566
- })
567
- }
568
-
569
- const result = await this.client.request({
570
- service: 'ec2',
571
- region: this.region,
572
- method: 'POST',
573
- path: '/',
574
- headers: {
575
- 'Content-Type': 'application/x-www-form-urlencoded',
576
- },
577
- body: new URLSearchParams(params).toString(),
578
- })
579
-
580
- return {
581
- Subnets: this.parseArray(result.subnetSet?.item).map((item: any) => ({
582
- SubnetId: item.subnetId,
583
- VpcId: item.vpcId,
584
- CidrBlock: item.cidrBlock,
585
- AvailabilityZone: item.availabilityZone,
586
- AvailableIpAddressCount: Number.parseInt(item.availableIpAddressCount),
587
- State: item.state,
588
- MapPublicIpOnLaunch: item.mapPublicIpOnLaunch === 'true',
589
- Tags: this.parseTags(item.tagSet?.item),
590
- })),
591
- }
592
- }
593
-
594
- /**
595
- * Describe Security Groups
596
- */
597
- async describeSecurityGroups(options?: {
598
- GroupIds?: string[]
599
- GroupNames?: string[]
600
- Filters?: { Name: string, Values: string[] }[]
601
- }): Promise<{
602
- SecurityGroups?: SecurityGroup[]
603
- }> {
604
- const params: Record<string, string> = {
605
- Action: 'DescribeSecurityGroups',
606
- Version: '2016-11-15',
607
- }
608
-
609
- if (options?.GroupIds) {
610
- options.GroupIds.forEach((id, i) => {
611
- params[`GroupId.${i + 1}`] = id
612
- })
613
- }
614
-
615
- if (options?.GroupNames) {
616
- options.GroupNames.forEach((name, i) => {
617
- params[`GroupName.${i + 1}`] = name
618
- })
619
- }
620
-
621
- if (options?.Filters) {
622
- options.Filters.forEach((filter, i) => {
623
- params[`Filter.${i + 1}.Name`] = filter.Name
624
- filter.Values.forEach((val, j) => {
625
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
626
- })
627
- })
628
- }
629
-
630
- const result = await this.client.request({
631
- service: 'ec2',
632
- region: this.region,
633
- method: 'POST',
634
- path: '/',
635
- headers: {
636
- 'Content-Type': 'application/x-www-form-urlencoded',
637
- },
638
- body: new URLSearchParams(params).toString(),
639
- })
640
-
641
- return {
642
- SecurityGroups: this.parseArray(result.securityGroupInfo?.item).map((item: any) => ({
643
- GroupId: item.groupId,
644
- GroupName: item.groupName,
645
- Description: item.groupDescription,
646
- VpcId: item.vpcId,
647
- IpPermissions: this.parseIpPermissions(item.ipPermissions?.item),
648
- IpPermissionsEgress: this.parseIpPermissions(item.ipPermissionsEgress?.item),
649
- Tags: this.parseTags(item.tagSet?.item),
650
- })),
651
- }
652
- }
653
-
654
- /**
655
- * Describe Internet Gateways
656
- */
657
- async describeInternetGateways(options?: {
658
- InternetGatewayIds?: string[]
659
- Filters?: { Name: string, Values: string[] }[]
660
- }): Promise<{
661
- InternetGateways?: InternetGateway[]
662
- }> {
663
- const params: Record<string, string> = {
664
- Action: 'DescribeInternetGateways',
665
- Version: '2016-11-15',
666
- }
667
-
668
- if (options?.InternetGatewayIds) {
669
- options.InternetGatewayIds.forEach((id, i) => {
670
- params[`InternetGatewayId.${i + 1}`] = id
671
- })
672
- }
673
-
674
- if (options?.Filters) {
675
- options.Filters.forEach((filter, i) => {
676
- params[`Filter.${i + 1}.Name`] = filter.Name
677
- filter.Values.forEach((val, j) => {
678
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
679
- })
680
- })
681
- }
682
-
683
- const result = await this.client.request({
684
- service: 'ec2',
685
- region: this.region,
686
- method: 'POST',
687
- path: '/',
688
- headers: {
689
- 'Content-Type': 'application/x-www-form-urlencoded',
690
- },
691
- body: new URLSearchParams(params).toString(),
692
- })
693
-
694
- return {
695
- InternetGateways: this.parseArray(result.internetGatewaySet?.item).map((item: any) => ({
696
- InternetGatewayId: item.internetGatewayId,
697
- Attachments: this.parseArray(item.attachmentSet?.item).map((a: any) => ({
698
- VpcId: a.vpcId,
699
- State: a.state,
700
- })),
701
- Tags: this.parseTags(item.tagSet?.item),
702
- })),
703
- }
704
- }
705
-
706
- /**
707
- * Describe Elastic IPs (Addresses)
708
- */
709
- async describeAddresses(options?: {
710
- AllocationIds?: string[]
711
- PublicIps?: string[]
712
- Filters?: { Name: string, Values: string[] }[]
713
- }): Promise<{
714
- Addresses?: Address[]
715
- }> {
716
- const params: Record<string, string> = {
717
- Action: 'DescribeAddresses',
718
- Version: '2016-11-15',
719
- }
720
-
721
- if (options?.AllocationIds) {
722
- options.AllocationIds.forEach((id, i) => {
723
- params[`AllocationId.${i + 1}`] = id
724
- })
725
- }
726
-
727
- if (options?.PublicIps) {
728
- options.PublicIps.forEach((ip, i) => {
729
- params[`PublicIp.${i + 1}`] = ip
730
- })
731
- }
732
-
733
- if (options?.Filters) {
734
- options.Filters.forEach((filter, i) => {
735
- params[`Filter.${i + 1}.Name`] = filter.Name
736
- filter.Values.forEach((val, j) => {
737
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
738
- })
739
- })
740
- }
741
-
742
- const result = await this.client.request({
743
- service: 'ec2',
744
- region: this.region,
745
- method: 'POST',
746
- path: '/',
747
- headers: {
748
- 'Content-Type': 'application/x-www-form-urlencoded',
749
- },
750
- body: new URLSearchParams(params).toString(),
751
- })
752
-
753
- return {
754
- Addresses: this.parseArray(result.addressesSet?.item).map((item: any) => ({
755
- PublicIp: item.publicIp,
756
- AllocationId: item.allocationId,
757
- AssociationId: item.associationId,
758
- InstanceId: item.instanceId,
759
- NetworkInterfaceId: item.networkInterfaceId,
760
- PrivateIpAddress: item.privateIpAddress,
761
- Domain: item.domain,
762
- Tags: this.parseTags(item.tagSet?.item),
763
- })),
764
- }
765
- }
766
-
767
- /**
768
- * Allocate Elastic IP
769
- */
770
- async allocateAddress(options?: {
771
- Domain?: 'vpc' | 'standard'
772
- TagSpecifications?: { ResourceType: string, Tags: { Key: string, Value: string }[] }[]
773
- }): Promise<{
774
- AllocationId?: string
775
- PublicIp?: string
776
- Domain?: string
777
- }> {
778
- const params: Record<string, string> = {
779
- Action: 'AllocateAddress',
780
- Version: '2016-11-15',
781
- }
782
-
783
- if (options?.Domain) {
784
- params.Domain = options.Domain
785
- }
786
-
787
- if (options?.TagSpecifications) {
788
- options.TagSpecifications.forEach((spec, i) => {
789
- params[`TagSpecification.${i + 1}.ResourceType`] = spec.ResourceType
790
- spec.Tags.forEach((tag, j) => {
791
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Key`] = tag.Key
792
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Value`] = tag.Value
793
- })
794
- })
795
- }
796
-
797
- const result = await this.client.request({
798
- service: 'ec2',
799
- region: this.region,
800
- method: 'POST',
801
- path: '/',
802
- headers: {
803
- 'Content-Type': 'application/x-www-form-urlencoded',
804
- },
805
- body: new URLSearchParams(params).toString(),
806
- })
807
-
808
- return {
809
- AllocationId: result.allocationId,
810
- PublicIp: result.publicIp,
811
- Domain: result.domain,
812
- }
813
- }
814
-
815
- /**
816
- * Associate Elastic IP with instance
817
- */
818
- async associateAddress(options: {
819
- AllocationId?: string
820
- PublicIp?: string
821
- InstanceId?: string
822
- NetworkInterfaceId?: string
823
- PrivateIpAddress?: string
824
- AllowReassociation?: boolean
825
- }): Promise<{
826
- AssociationId?: string
827
- }> {
828
- const params: Record<string, string> = {
829
- Action: 'AssociateAddress',
830
- Version: '2016-11-15',
831
- }
832
-
833
- if (options.AllocationId) {
834
- params.AllocationId = options.AllocationId
835
- }
836
-
837
- if (options.PublicIp) {
838
- params.PublicIp = options.PublicIp
839
- }
840
-
841
- if (options.InstanceId) {
842
- params.InstanceId = options.InstanceId
843
- }
844
-
845
- if (options.NetworkInterfaceId) {
846
- params.NetworkInterfaceId = options.NetworkInterfaceId
847
- }
848
-
849
- if (options.PrivateIpAddress) {
850
- params.PrivateIpAddress = options.PrivateIpAddress
851
- }
852
-
853
- if (options.AllowReassociation) {
854
- params.AllowReassociation = 'true'
855
- }
856
-
857
- const result = await this.client.request({
858
- service: 'ec2',
859
- region: this.region,
860
- method: 'POST',
861
- path: '/',
862
- headers: {
863
- 'Content-Type': 'application/x-www-form-urlencoded',
864
- },
865
- body: new URLSearchParams(params).toString(),
866
- })
867
-
868
- return {
869
- AssociationId: result.associationId,
870
- }
871
- }
872
-
873
- /**
874
- * Create tags for resources
875
- */
876
- async createTags(options: {
877
- Resources: string[]
878
- Tags: { Key: string, Value: string }[]
879
- }): Promise<void> {
880
- const params: Record<string, string> = {
881
- Action: 'CreateTags',
882
- Version: '2016-11-15',
883
- }
884
-
885
- options.Resources.forEach((id, i) => {
886
- params[`ResourceId.${i + 1}`] = id
887
- })
888
-
889
- options.Tags.forEach((tag, i) => {
890
- params[`Tag.${i + 1}.Key`] = tag.Key
891
- params[`Tag.${i + 1}.Value`] = tag.Value
892
- })
893
-
894
- await this.client.request({
895
- service: 'ec2',
896
- region: this.region,
897
- method: 'POST',
898
- path: '/',
899
- headers: {
900
- 'Content-Type': 'application/x-www-form-urlencoded',
901
- },
902
- body: new URLSearchParams(params).toString(),
903
- })
904
- }
905
-
906
- /**
907
- * Wait for instance to reach a specific state
908
- */
909
- async waitForInstanceState(
910
- instanceId: string,
911
- targetState: 'running' | 'stopped' | 'terminated',
912
- options?: {
913
- maxWaitMs?: number
914
- pollIntervalMs?: number
915
- },
916
- ): Promise<Instance | undefined> {
917
- const maxWait = options?.maxWaitMs || 300000 // 5 minutes
918
- const pollInterval = options?.pollIntervalMs || 5000 // 5 seconds
919
- const startTime = Date.now()
920
-
921
- while (Date.now() - startTime < maxWait) {
922
- const instance = await this.getInstance(instanceId)
923
-
924
- if (instance?.State?.Name === targetState) {
925
- return instance
926
- }
927
-
928
- await new Promise(resolve => setTimeout(resolve, pollInterval))
929
- }
930
-
931
- return undefined
932
- }
933
-
934
- /**
935
- * Create a VPC
936
- */
937
- async createVpc(options: {
938
- CidrBlock: string
939
- InstanceTenancy?: string
940
- TagSpecifications?: { ResourceType: string, Tags: { Key: string, Value: string }[] }[]
941
- }): Promise<{
942
- Vpc?: Vpc
943
- }> {
944
- const params: Record<string, string> = {
945
- Action: 'CreateVpc',
946
- Version: '2016-11-15',
947
- CidrBlock: options.CidrBlock,
948
- }
949
-
950
- if (options.InstanceTenancy) {
951
- params.InstanceTenancy = options.InstanceTenancy
952
- }
953
-
954
- if (options.TagSpecifications) {
955
- options.TagSpecifications.forEach((spec, i) => {
956
- params[`TagSpecification.${i + 1}.ResourceType`] = spec.ResourceType
957
- spec.Tags.forEach((tag, j) => {
958
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Key`] = tag.Key
959
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Value`] = tag.Value
960
- })
961
- })
962
- }
963
-
964
- const result = await this.client.request({
965
- service: 'ec2',
966
- region: this.region,
967
- method: 'POST',
968
- path: '/',
969
- headers: {
970
- 'Content-Type': 'application/x-www-form-urlencoded',
971
- },
972
- body: new URLSearchParams(params).toString(),
973
- })
974
-
975
- const response = result.CreateVpcResponse || result
976
- const vpc = response.vpc
977
-
978
- return {
979
- Vpc: vpc ? {
980
- VpcId: vpc.vpcId,
981
- CidrBlock: vpc.cidrBlock,
982
- State: vpc.state,
983
- DhcpOptionsId: vpc.dhcpOptionsId,
984
- InstanceTenancy: vpc.instanceTenancy,
985
- IsDefault: vpc.isDefault === 'true',
986
- Tags: this.parseTags(vpc.tagSet?.item),
987
- } : undefined,
988
- }
989
- }
990
-
991
- /**
992
- * Create a Subnet
993
- */
994
- async createSubnet(options: {
995
- VpcId: string
996
- CidrBlock: string
997
- AvailabilityZone?: string
998
- TagSpecifications?: { ResourceType: string, Tags: { Key: string, Value: string }[] }[]
999
- }): Promise<{
1000
- Subnet?: Subnet
1001
- }> {
1002
- const params: Record<string, string> = {
1003
- Action: 'CreateSubnet',
1004
- Version: '2016-11-15',
1005
- VpcId: options.VpcId,
1006
- CidrBlock: options.CidrBlock,
1007
- }
1008
-
1009
- if (options.AvailabilityZone) {
1010
- params.AvailabilityZone = options.AvailabilityZone
1011
- }
1012
-
1013
- if (options.TagSpecifications) {
1014
- options.TagSpecifications.forEach((spec, i) => {
1015
- params[`TagSpecification.${i + 1}.ResourceType`] = spec.ResourceType
1016
- spec.Tags.forEach((tag, j) => {
1017
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Key`] = tag.Key
1018
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Value`] = tag.Value
1019
- })
1020
- })
1021
- }
1022
-
1023
- const result = await this.client.request({
1024
- service: 'ec2',
1025
- region: this.region,
1026
- method: 'POST',
1027
- path: '/',
1028
- headers: {
1029
- 'Content-Type': 'application/x-www-form-urlencoded',
1030
- },
1031
- body: new URLSearchParams(params).toString(),
1032
- })
1033
-
1034
- const response = result.CreateSubnetResponse || result
1035
- const subnet = response.subnet
1036
-
1037
- return {
1038
- Subnet: subnet ? {
1039
- SubnetId: subnet.subnetId,
1040
- VpcId: subnet.vpcId,
1041
- CidrBlock: subnet.cidrBlock,
1042
- AvailabilityZone: subnet.availabilityZone,
1043
- AvailableIpAddressCount: subnet.availableIpAddressCount ? Number.parseInt(subnet.availableIpAddressCount) : undefined,
1044
- State: subnet.state,
1045
- MapPublicIpOnLaunch: subnet.mapPublicIpOnLaunch === 'true',
1046
- Tags: this.parseTags(subnet.tagSet?.item),
1047
- } : undefined,
1048
- }
1049
- }
1050
-
1051
- /**
1052
- * Modify a Subnet attribute
1053
- */
1054
- async modifySubnetAttribute(options: {
1055
- SubnetId: string
1056
- MapPublicIpOnLaunch?: { Value: boolean }
1057
- }): Promise<void> {
1058
- const params: Record<string, string> = {
1059
- Action: 'ModifySubnetAttribute',
1060
- Version: '2016-11-15',
1061
- SubnetId: options.SubnetId,
1062
- }
1063
-
1064
- if (options.MapPublicIpOnLaunch !== undefined) {
1065
- params['MapPublicIpOnLaunch.Value'] = String(options.MapPublicIpOnLaunch.Value)
1066
- }
1067
-
1068
- await this.client.request({
1069
- service: 'ec2',
1070
- region: this.region,
1071
- method: 'POST',
1072
- path: '/',
1073
- headers: {
1074
- 'Content-Type': 'application/x-www-form-urlencoded',
1075
- },
1076
- body: new URLSearchParams(params).toString(),
1077
- })
1078
- }
1079
-
1080
- /**
1081
- * Create a Security Group
1082
- */
1083
- async createSecurityGroup(options: {
1084
- GroupName: string
1085
- Description: string
1086
- VpcId?: string
1087
- TagSpecifications?: { ResourceType: string, Tags: { Key: string, Value: string }[] }[]
1088
- }): Promise<{
1089
- GroupId?: string
1090
- }> {
1091
- const params: Record<string, string> = {
1092
- Action: 'CreateSecurityGroup',
1093
- Version: '2016-11-15',
1094
- GroupName: options.GroupName,
1095
- GroupDescription: options.Description,
1096
- }
1097
-
1098
- if (options.VpcId) {
1099
- params.VpcId = options.VpcId
1100
- }
1101
-
1102
- if (options.TagSpecifications) {
1103
- options.TagSpecifications.forEach((spec, i) => {
1104
- params[`TagSpecification.${i + 1}.ResourceType`] = spec.ResourceType
1105
- spec.Tags.forEach((tag, j) => {
1106
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Key`] = tag.Key
1107
- params[`TagSpecification.${i + 1}.Tag.${j + 1}.Value`] = tag.Value
1108
- })
1109
- })
1110
- }
1111
-
1112
- const result = await this.client.request({
1113
- service: 'ec2',
1114
- region: this.region,
1115
- method: 'POST',
1116
- path: '/',
1117
- headers: {
1118
- 'Content-Type': 'application/x-www-form-urlencoded',
1119
- },
1120
- body: new URLSearchParams(params).toString(),
1121
- })
1122
-
1123
- const response = result.CreateSecurityGroupResponse || result
1124
-
1125
- return {
1126
- GroupId: response.groupId,
1127
- }
1128
- }
1129
-
1130
- /**
1131
- * Authorize Security Group Ingress (add inbound rule)
1132
- */
1133
- async authorizeSecurityGroupIngress(options: {
1134
- GroupId: string
1135
- IpPermissions: IpPermission[]
1136
- }): Promise<void> {
1137
- const params: Record<string, string> = {
1138
- Action: 'AuthorizeSecurityGroupIngress',
1139
- Version: '2016-11-15',
1140
- GroupId: options.GroupId,
1141
- }
1142
-
1143
- this.encodeIpPermissions(params, options.IpPermissions)
1144
-
1145
- await this.client.request({
1146
- service: 'ec2',
1147
- region: this.region,
1148
- method: 'POST',
1149
- path: '/',
1150
- headers: {
1151
- 'Content-Type': 'application/x-www-form-urlencoded',
1152
- },
1153
- body: new URLSearchParams(params).toString(),
1154
- })
1155
- }
1156
-
1157
- /**
1158
- * Authorize Security Group Egress (add outbound rule)
1159
- */
1160
- async authorizeSecurityGroupEgress(options: {
1161
- GroupId: string
1162
- IpPermissions: IpPermission[]
1163
- }): Promise<void> {
1164
- const params: Record<string, string> = {
1165
- Action: 'AuthorizeSecurityGroupEgress',
1166
- Version: '2016-11-15',
1167
- GroupId: options.GroupId,
1168
- }
1169
-
1170
- this.encodeIpPermissions(params, options.IpPermissions)
1171
-
1172
- await this.client.request({
1173
- service: 'ec2',
1174
- region: this.region,
1175
- method: 'POST',
1176
- path: '/',
1177
- headers: {
1178
- 'Content-Type': 'application/x-www-form-urlencoded',
1179
- },
1180
- body: new URLSearchParams(params).toString(),
1181
- })
1182
- }
1183
-
1184
- /**
1185
- * Describe Route Tables
1186
- */
1187
- async describeRouteTables(options?: {
1188
- RouteTableIds?: string[]
1189
- Filters?: { Name: string, Values: string[] }[]
1190
- }): Promise<{
1191
- RouteTables?: RouteTable[]
1192
- }> {
1193
- const params: Record<string, string> = {
1194
- Action: 'DescribeRouteTables',
1195
- Version: '2016-11-15',
1196
- }
1197
-
1198
- if (options?.RouteTableIds) {
1199
- options.RouteTableIds.forEach((id, i) => {
1200
- params[`RouteTableId.${i + 1}`] = id
1201
- })
1202
- }
1203
-
1204
- if (options?.Filters) {
1205
- options.Filters.forEach((filter, i) => {
1206
- params[`Filter.${i + 1}.Name`] = filter.Name
1207
- filter.Values.forEach((val, j) => {
1208
- params[`Filter.${i + 1}.Value.${j + 1}`] = val
1209
- })
1210
- })
1211
- }
1212
-
1213
- const result = await this.client.request({
1214
- service: 'ec2',
1215
- region: this.region,
1216
- method: 'POST',
1217
- path: '/',
1218
- headers: {
1219
- 'Content-Type': 'application/x-www-form-urlencoded',
1220
- },
1221
- body: new URLSearchParams(params).toString(),
1222
- })
1223
-
1224
- const response = result.DescribeRouteTablesResponse || result
1225
-
1226
- return {
1227
- RouteTables: this.parseArray(response.routeTableSet?.item).map((item: any) => ({
1228
- RouteTableId: item.routeTableId,
1229
- VpcId: item.vpcId,
1230
- Routes: this.parseArray(item.routeSet?.item).map((r: any) => ({
1231
- DestinationCidrBlock: r.destinationCidrBlock,
1232
- GatewayId: r.gatewayId,
1233
- NatGatewayId: r.natGatewayId,
1234
- State: r.state,
1235
- })),
1236
- Associations: this.parseArray(item.associationSet?.item).map((a: any) => ({
1237
- RouteTableAssociationId: a.routeTableAssociationId,
1238
- SubnetId: a.subnetId,
1239
- Main: a.main === 'true' || a.main === true,
1240
- })),
1241
- Tags: this.parseTags(item.tagSet?.item),
1242
- })),
1243
- }
1244
- }
1245
-
1246
- /**
1247
- * Encode IpPermissions into query parameters for security group rules
1248
- */
1249
- private encodeIpPermissions(params: Record<string, string>, permissions: IpPermission[]): void {
1250
- permissions.forEach((perm, i) => {
1251
- const prefix = `IpPermissions.${i + 1}`
1252
-
1253
- if (perm.IpProtocol !== undefined) {
1254
- params[`${prefix}.IpProtocol`] = perm.IpProtocol
1255
- }
1256
- if (perm.FromPort !== undefined) {
1257
- params[`${prefix}.FromPort`] = String(perm.FromPort)
1258
- }
1259
- if (perm.ToPort !== undefined) {
1260
- params[`${prefix}.ToPort`] = String(perm.ToPort)
1261
- }
1262
-
1263
- if (perm.IpRanges) {
1264
- perm.IpRanges.forEach((range, j) => {
1265
- if (range.CidrIp) {
1266
- params[`${prefix}.IpRanges.${j + 1}.CidrIp`] = range.CidrIp
1267
- }
1268
- if (range.Description) {
1269
- params[`${prefix}.IpRanges.${j + 1}.Description`] = range.Description
1270
- }
1271
- })
1272
- }
1273
-
1274
- if (perm.Ipv6Ranges) {
1275
- perm.Ipv6Ranges.forEach((range, j) => {
1276
- if (range.CidrIpv6) {
1277
- params[`${prefix}.Ipv6Ranges.${j + 1}.CidrIpv6`] = range.CidrIpv6
1278
- }
1279
- if (range.Description) {
1280
- params[`${prefix}.Ipv6Ranges.${j + 1}.Description`] = range.Description
1281
- }
1282
- })
1283
- }
1284
-
1285
- if (perm.UserIdGroupPairs) {
1286
- perm.UserIdGroupPairs.forEach((pair, j) => {
1287
- if (pair.GroupId) {
1288
- params[`${prefix}.Groups.${j + 1}.GroupId`] = pair.GroupId
1289
- }
1290
- if (pair.UserId) {
1291
- params[`${prefix}.Groups.${j + 1}.UserId`] = pair.UserId
1292
- }
1293
- if (pair.Description) {
1294
- params[`${prefix}.Groups.${j + 1}.Description`] = pair.Description
1295
- }
1296
- })
1297
- }
1298
- })
1299
- }
1300
-
1301
- // Helper methods for parsing EC2 XML responses
1302
-
1303
- private parseArray(item: any): any[] {
1304
- if (!item)
1305
- return []
1306
- return Array.isArray(item) ? item : [item]
1307
- }
1308
-
1309
- private parseTags(item: any): { Key?: string, Value?: string }[] {
1310
- return this.parseArray(item).map((t: any) => ({
1311
- Key: t.key,
1312
- Value: t.value,
1313
- }))
1314
- }
1315
-
1316
- private parseReservations(item: any): { ReservationId?: string, Instances?: Instance[] }[] {
1317
- return this.parseArray(item).map((r: any) => ({
1318
- ReservationId: r.reservationId,
1319
- Instances: this.parseInstances(r.instancesSet?.item || r.instancesSet),
1320
- }))
1321
- }
1322
-
1323
- private parseInstances(item: any): Instance[] {
1324
- return this.parseArray(item).map((i: any) => ({
1325
- InstanceId: i.instanceId,
1326
- ImageId: i.imageId,
1327
- InstanceType: i.instanceType,
1328
- State: i.instanceState ? {
1329
- Code: Number.parseInt(i.instanceState.code),
1330
- Name: i.instanceState.name,
1331
- } : undefined,
1332
- PrivateIpAddress: i.privateIpAddress,
1333
- PublicIpAddress: i.ipAddress,
1334
- SubnetId: i.subnetId,
1335
- VpcId: i.vpcId,
1336
- SecurityGroups: this.parseArray(i.groupSet?.item).map((g: any) => ({
1337
- GroupId: g.groupId,
1338
- GroupName: g.groupName,
1339
- })),
1340
- Tags: this.parseTags(i.tagSet?.item),
1341
- LaunchTime: i.launchTime,
1342
- Placement: i.placement ? {
1343
- AvailabilityZone: i.placement.availabilityZone,
1344
- Tenancy: i.placement.tenancy,
1345
- } : undefined,
1346
- Architecture: i.architecture,
1347
- RootDeviceType: i.rootDeviceType,
1348
- RootDeviceName: i.rootDeviceName,
1349
- BlockDeviceMappings: this.parseArray(i.blockDeviceMapping?.item).map((b: any) => ({
1350
- DeviceName: b.deviceName,
1351
- Ebs: b.ebs ? {
1352
- VolumeId: b.ebs.volumeId,
1353
- Status: b.ebs.status,
1354
- AttachTime: b.ebs.attachTime,
1355
- DeleteOnTermination: b.ebs.deleteOnTermination === 'true',
1356
- } : undefined,
1357
- })),
1358
- IamInstanceProfile: i.iamInstanceProfile ? {
1359
- Arn: i.iamInstanceProfile.arn,
1360
- Id: i.iamInstanceProfile.id,
1361
- } : undefined,
1362
- }))
1363
- }
1364
-
1365
- private parseIpPermissions(item: any): IpPermission[] {
1366
- return this.parseArray(item).map((p: any) => ({
1367
- IpProtocol: p.ipProtocol,
1368
- FromPort: p.fromPort ? Number.parseInt(p.fromPort) : undefined,
1369
- ToPort: p.toPort ? Number.parseInt(p.toPort) : undefined,
1370
- IpRanges: this.parseArray(p.ipRanges?.item).map((r: any) => ({
1371
- CidrIp: r.cidrIp,
1372
- Description: r.description,
1373
- })),
1374
- Ipv6Ranges: this.parseArray(p.ipv6Ranges?.item).map((r: any) => ({
1375
- CidrIpv6: r.cidrIpv6,
1376
- Description: r.description,
1377
- })),
1378
- UserIdGroupPairs: this.parseArray(p.groups?.item).map((g: any) => ({
1379
- GroupId: g.groupId,
1380
- UserId: g.userId,
1381
- Description: g.description,
1382
- })),
1383
- }))
1384
- }
1385
- }