@stacksjs/ts-cloud 0.1.8 → 0.1.9

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 (75) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/package.json +18 -16
  3. package/src/aws/acm.ts +768 -0
  4. package/src/aws/application-autoscaling.ts +845 -0
  5. package/src/aws/bedrock.ts +4074 -0
  6. package/src/aws/client.ts +891 -0
  7. package/src/aws/cloudformation.ts +896 -0
  8. package/src/aws/cloudfront.ts +1531 -0
  9. package/src/aws/cloudwatch-logs.ts +154 -0
  10. package/src/aws/comprehend.ts +839 -0
  11. package/src/aws/connect.ts +1056 -0
  12. package/src/aws/deploy-imap.ts +384 -0
  13. package/src/aws/dynamodb.ts +340 -0
  14. package/src/aws/ec2.ts +1385 -0
  15. package/src/aws/ecr.ts +621 -0
  16. package/src/aws/ecs.ts +615 -0
  17. package/src/aws/elasticache.ts +301 -0
  18. package/src/aws/elbv2.ts +942 -0
  19. package/src/aws/email.ts +928 -0
  20. package/src/aws/eventbridge.ts +248 -0
  21. package/src/aws/iam.ts +1689 -0
  22. package/src/aws/imap-server.ts +2100 -0
  23. package/src/aws/index.ts +213 -0
  24. package/src/aws/kendra.ts +1097 -0
  25. package/src/aws/lambda.ts +786 -0
  26. package/src/aws/opensearch.ts +158 -0
  27. package/src/aws/personalize.ts +977 -0
  28. package/src/aws/polly.ts +559 -0
  29. package/src/aws/rds.ts +888 -0
  30. package/src/aws/rekognition.ts +846 -0
  31. package/src/aws/route53-domains.ts +359 -0
  32. package/src/aws/route53.ts +1046 -0
  33. package/src/aws/s3.ts +2334 -0
  34. package/src/aws/scheduler.ts +571 -0
  35. package/src/aws/secrets-manager.ts +769 -0
  36. package/src/aws/ses.ts +1081 -0
  37. package/src/aws/setup-phone.ts +104 -0
  38. package/src/aws/setup-sms.ts +580 -0
  39. package/src/aws/sms.ts +1735 -0
  40. package/src/aws/smtp-server.ts +531 -0
  41. package/src/aws/sns.ts +758 -0
  42. package/src/aws/sqs.ts +382 -0
  43. package/src/aws/ssm.ts +807 -0
  44. package/src/aws/sts.ts +92 -0
  45. package/src/aws/support.ts +391 -0
  46. package/src/aws/test-imap.ts +86 -0
  47. package/src/aws/textract.ts +780 -0
  48. package/src/aws/transcribe.ts +108 -0
  49. package/src/aws/translate.ts +641 -0
  50. package/src/aws/voice.ts +1379 -0
  51. package/src/config.ts +35 -0
  52. package/src/deploy/index.ts +7 -0
  53. package/src/deploy/static-site-external-dns.ts +945 -0
  54. package/src/deploy/static-site.ts +1175 -0
  55. package/src/dns/cloudflare.ts +548 -0
  56. package/src/dns/godaddy.ts +412 -0
  57. package/src/dns/index.ts +205 -0
  58. package/src/dns/porkbun.ts +362 -0
  59. package/src/dns/route53-adapter.ts +414 -0
  60. package/src/dns/types.ts +119 -0
  61. package/src/dns/validator.ts +369 -0
  62. package/src/generators/index.ts +5 -0
  63. package/src/generators/infrastructure.ts +1660 -0
  64. package/src/index.ts +163 -0
  65. package/src/push/apns.ts +452 -0
  66. package/src/push/fcm.ts +506 -0
  67. package/src/push/index.ts +58 -0
  68. package/src/security/pre-deploy-scanner.ts +655 -0
  69. package/src/ssl/acme-client.ts +478 -0
  70. package/src/ssl/index.ts +7 -0
  71. package/src/ssl/letsencrypt.ts +747 -0
  72. package/src/types.ts +2 -0
  73. package/src/utils/cli.ts +398 -0
  74. package/src/validation/index.ts +5 -0
  75. package/src/validation/template.ts +405 -0
package/src/aws/iam.ts ADDED
@@ -0,0 +1,1689 @@
1
+ /**
2
+ * AWS IAM (Identity and Access Management) Operations
3
+ * Direct API calls without AWS SDK dependency
4
+ */
5
+
6
+ import { AWSClient } from './client'
7
+
8
+ // ============================================================================
9
+ // Types - Users
10
+ // ============================================================================
11
+
12
+ export interface IAMUser {
13
+ UserName: string
14
+ UserId: string
15
+ Arn: string
16
+ Path?: string
17
+ CreateDate?: string
18
+ PasswordLastUsed?: string
19
+ PermissionsBoundary?: {
20
+ PermissionsBoundaryType: string
21
+ PermissionsBoundaryArn: string
22
+ }
23
+ Tags?: Array<{ Key: string; Value: string }>
24
+ }
25
+
26
+ export interface CreateUserParams {
27
+ UserName: string
28
+ Path?: string
29
+ PermissionsBoundary?: string
30
+ Tags?: Array<{ Key: string; Value: string }>
31
+ }
32
+
33
+ export interface GetUserParams {
34
+ UserName?: string
35
+ }
36
+
37
+ export interface ListUsersParams {
38
+ PathPrefix?: string
39
+ Marker?: string
40
+ MaxItems?: number
41
+ }
42
+
43
+ export interface UpdateUserParams {
44
+ UserName: string
45
+ NewUserName?: string
46
+ NewPath?: string
47
+ }
48
+
49
+ export interface DeleteUserParams {
50
+ UserName: string
51
+ }
52
+
53
+ // ============================================================================
54
+ // Types - Groups
55
+ // ============================================================================
56
+
57
+ export interface IAMGroup {
58
+ GroupName: string
59
+ GroupId: string
60
+ Arn: string
61
+ Path?: string
62
+ CreateDate?: string
63
+ }
64
+
65
+ export interface CreateGroupParams {
66
+ GroupName: string
67
+ Path?: string
68
+ }
69
+
70
+ export interface GetGroupParams {
71
+ GroupName: string
72
+ Marker?: string
73
+ MaxItems?: number
74
+ }
75
+
76
+ export interface ListGroupsParams {
77
+ PathPrefix?: string
78
+ Marker?: string
79
+ MaxItems?: number
80
+ }
81
+
82
+ export interface UpdateGroupParams {
83
+ GroupName: string
84
+ NewGroupName?: string
85
+ NewPath?: string
86
+ }
87
+
88
+ export interface DeleteGroupParams {
89
+ GroupName: string
90
+ }
91
+
92
+ export interface AddUserToGroupParams {
93
+ GroupName: string
94
+ UserName: string
95
+ }
96
+
97
+ export interface RemoveUserFromGroupParams {
98
+ GroupName: string
99
+ UserName: string
100
+ }
101
+
102
+ export interface ListGroupsForUserParams {
103
+ UserName: string
104
+ Marker?: string
105
+ MaxItems?: number
106
+ }
107
+
108
+ // ============================================================================
109
+ // Types - Roles
110
+ // ============================================================================
111
+
112
+ export interface IAMRole {
113
+ RoleName: string
114
+ RoleId: string
115
+ Arn: string
116
+ Path?: string
117
+ CreateDate?: string
118
+ AssumeRolePolicyDocument?: string
119
+ Description?: string
120
+ MaxSessionDuration?: number
121
+ PermissionsBoundary?: {
122
+ PermissionsBoundaryType: string
123
+ PermissionsBoundaryArn: string
124
+ }
125
+ Tags?: Array<{ Key: string; Value: string }>
126
+ RoleLastUsed?: {
127
+ LastUsedDate?: string
128
+ Region?: string
129
+ }
130
+ }
131
+
132
+ export interface CreateRoleParams {
133
+ RoleName: string
134
+ AssumeRolePolicyDocument: string
135
+ Path?: string
136
+ Description?: string
137
+ MaxSessionDuration?: number
138
+ PermissionsBoundary?: string
139
+ Tags?: Array<{ Key: string; Value: string }>
140
+ }
141
+
142
+ export interface GetRoleParams {
143
+ RoleName: string
144
+ }
145
+
146
+ export interface ListRolesParams {
147
+ PathPrefix?: string
148
+ Marker?: string
149
+ MaxItems?: number
150
+ }
151
+
152
+ export interface UpdateRoleParams {
153
+ RoleName: string
154
+ Description?: string
155
+ MaxSessionDuration?: number
156
+ }
157
+
158
+ export interface UpdateRoleDescriptionParams {
159
+ RoleName: string
160
+ Description: string
161
+ }
162
+
163
+ export interface UpdateAssumeRolePolicyParams {
164
+ RoleName: string
165
+ PolicyDocument: string
166
+ }
167
+
168
+ export interface DeleteRoleParams {
169
+ RoleName: string
170
+ }
171
+
172
+ export interface TagRoleParams {
173
+ RoleName: string
174
+ Tags: Array<{ Key: string; Value: string }>
175
+ }
176
+
177
+ export interface UntagRoleParams {
178
+ RoleName: string
179
+ TagKeys: string[]
180
+ }
181
+
182
+ export interface ListRoleTagsParams {
183
+ RoleName: string
184
+ Marker?: string
185
+ MaxItems?: number
186
+ }
187
+
188
+ // ============================================================================
189
+ // Types - Policies
190
+ // ============================================================================
191
+
192
+ export interface IAMPolicy {
193
+ PolicyName: string
194
+ PolicyId: string
195
+ Arn: string
196
+ Path?: string
197
+ DefaultVersionId?: string
198
+ AttachmentCount?: number
199
+ PermissionsBoundaryUsageCount?: number
200
+ IsAttachable?: boolean
201
+ Description?: string
202
+ CreateDate?: string
203
+ UpdateDate?: string
204
+ Tags?: Array<{ Key: string; Value: string }>
205
+ }
206
+
207
+ export interface PolicyVersion {
208
+ VersionId: string
209
+ IsDefaultVersion: boolean
210
+ CreateDate?: string
211
+ Document?: string
212
+ }
213
+
214
+ export interface CreatePolicyParams {
215
+ PolicyName: string
216
+ PolicyDocument: string
217
+ Path?: string
218
+ Description?: string
219
+ Tags?: Array<{ Key: string; Value: string }>
220
+ }
221
+
222
+ export interface GetPolicyParams {
223
+ PolicyArn: string
224
+ }
225
+
226
+ export interface GetPolicyVersionParams {
227
+ PolicyArn: string
228
+ VersionId: string
229
+ }
230
+
231
+ export interface ListPoliciesParams {
232
+ Scope?: 'All' | 'AWS' | 'Local'
233
+ OnlyAttached?: boolean
234
+ PathPrefix?: string
235
+ PolicyUsageFilter?: 'PermissionsPolicy' | 'PermissionsBoundary'
236
+ Marker?: string
237
+ MaxItems?: number
238
+ }
239
+
240
+ export interface ListPolicyVersionsParams {
241
+ PolicyArn: string
242
+ Marker?: string
243
+ MaxItems?: number
244
+ }
245
+
246
+ export interface CreatePolicyVersionParams {
247
+ PolicyArn: string
248
+ PolicyDocument: string
249
+ SetAsDefault?: boolean
250
+ }
251
+
252
+ export interface DeletePolicyVersionParams {
253
+ PolicyArn: string
254
+ VersionId: string
255
+ }
256
+
257
+ export interface SetDefaultPolicyVersionParams {
258
+ PolicyArn: string
259
+ VersionId: string
260
+ }
261
+
262
+ export interface DeletePolicyParams {
263
+ PolicyArn: string
264
+ }
265
+
266
+ export interface AttachUserPolicyParams {
267
+ UserName: string
268
+ PolicyArn: string
269
+ }
270
+
271
+ export interface DetachUserPolicyParams {
272
+ UserName: string
273
+ PolicyArn: string
274
+ }
275
+
276
+ export interface AttachGroupPolicyParams {
277
+ GroupName: string
278
+ PolicyArn: string
279
+ }
280
+
281
+ export interface DetachGroupPolicyParams {
282
+ GroupName: string
283
+ PolicyArn: string
284
+ }
285
+
286
+ export interface AttachRolePolicyParams {
287
+ RoleName: string
288
+ PolicyArn: string
289
+ }
290
+
291
+ export interface DetachRolePolicyParams {
292
+ RoleName: string
293
+ PolicyArn: string
294
+ }
295
+
296
+ export interface ListAttachedUserPoliciesParams {
297
+ UserName: string
298
+ PathPrefix?: string
299
+ Marker?: string
300
+ MaxItems?: number
301
+ }
302
+
303
+ export interface ListAttachedGroupPoliciesParams {
304
+ GroupName: string
305
+ PathPrefix?: string
306
+ Marker?: string
307
+ MaxItems?: number
308
+ }
309
+
310
+ export interface ListAttachedRolePoliciesParams {
311
+ RoleName: string
312
+ PathPrefix?: string
313
+ Marker?: string
314
+ MaxItems?: number
315
+ }
316
+
317
+ export interface ListEntitiesForPolicyParams {
318
+ PolicyArn: string
319
+ EntityFilter?: 'User' | 'Role' | 'Group' | 'LocalManagedPolicy' | 'AWSManagedPolicy'
320
+ PathPrefix?: string
321
+ PolicyUsageFilter?: 'PermissionsPolicy' | 'PermissionsBoundary'
322
+ Marker?: string
323
+ MaxItems?: number
324
+ }
325
+
326
+ // ============================================================================
327
+ // Types - Inline Policies
328
+ // ============================================================================
329
+
330
+ export interface PutUserPolicyParams {
331
+ UserName: string
332
+ PolicyName: string
333
+ PolicyDocument: string
334
+ }
335
+
336
+ export interface GetUserPolicyParams {
337
+ UserName: string
338
+ PolicyName: string
339
+ }
340
+
341
+ export interface DeleteUserPolicyParams {
342
+ UserName: string
343
+ PolicyName: string
344
+ }
345
+
346
+ export interface ListUserPoliciesParams {
347
+ UserName: string
348
+ Marker?: string
349
+ MaxItems?: number
350
+ }
351
+
352
+ export interface PutGroupPolicyParams {
353
+ GroupName: string
354
+ PolicyName: string
355
+ PolicyDocument: string
356
+ }
357
+
358
+ export interface GetGroupPolicyParams {
359
+ GroupName: string
360
+ PolicyName: string
361
+ }
362
+
363
+ export interface DeleteGroupPolicyParams {
364
+ GroupName: string
365
+ PolicyName: string
366
+ }
367
+
368
+ export interface ListGroupPoliciesParams {
369
+ GroupName: string
370
+ Marker?: string
371
+ MaxItems?: number
372
+ }
373
+
374
+ export interface PutRolePolicyParams {
375
+ RoleName: string
376
+ PolicyName: string
377
+ PolicyDocument: string
378
+ }
379
+
380
+ export interface GetRolePolicyParams {
381
+ RoleName: string
382
+ PolicyName: string
383
+ }
384
+
385
+ export interface DeleteRolePolicyParams {
386
+ RoleName: string
387
+ PolicyName: string
388
+ }
389
+
390
+ export interface ListRolePoliciesParams {
391
+ RoleName: string
392
+ Marker?: string
393
+ MaxItems?: number
394
+ }
395
+
396
+ // ============================================================================
397
+ // Types - Access Keys
398
+ // ============================================================================
399
+
400
+ export interface AccessKey {
401
+ UserName: string
402
+ AccessKeyId: string
403
+ Status: 'Active' | 'Inactive'
404
+ CreateDate?: string
405
+ }
406
+
407
+ export interface AccessKeyMetadata {
408
+ UserName?: string
409
+ AccessKeyId: string
410
+ Status: 'Active' | 'Inactive'
411
+ CreateDate?: string
412
+ }
413
+
414
+ export interface CreateAccessKeyParams {
415
+ UserName?: string
416
+ }
417
+
418
+ export interface CreateAccessKeyResult {
419
+ AccessKey: {
420
+ UserName: string
421
+ AccessKeyId: string
422
+ Status: 'Active' | 'Inactive'
423
+ SecretAccessKey: string
424
+ CreateDate?: string
425
+ }
426
+ }
427
+
428
+ export interface ListAccessKeysParams {
429
+ UserName?: string
430
+ Marker?: string
431
+ MaxItems?: number
432
+ }
433
+
434
+ export interface UpdateAccessKeyParams {
435
+ UserName?: string
436
+ AccessKeyId: string
437
+ Status: 'Active' | 'Inactive'
438
+ }
439
+
440
+ export interface DeleteAccessKeyParams {
441
+ UserName?: string
442
+ AccessKeyId: string
443
+ }
444
+
445
+ export interface GetAccessKeyLastUsedParams {
446
+ AccessKeyId: string
447
+ }
448
+
449
+ // ============================================================================
450
+ // Types - Instance Profiles
451
+ // ============================================================================
452
+
453
+ export interface InstanceProfile {
454
+ InstanceProfileName: string
455
+ InstanceProfileId: string
456
+ Arn: string
457
+ Path?: string
458
+ CreateDate?: string
459
+ Roles?: IAMRole[]
460
+ Tags?: Array<{ Key: string; Value: string }>
461
+ }
462
+
463
+ export interface CreateInstanceProfileParams {
464
+ InstanceProfileName: string
465
+ Path?: string
466
+ Tags?: Array<{ Key: string; Value: string }>
467
+ }
468
+
469
+ export interface GetInstanceProfileParams {
470
+ InstanceProfileName: string
471
+ }
472
+
473
+ export interface ListInstanceProfilesParams {
474
+ PathPrefix?: string
475
+ Marker?: string
476
+ MaxItems?: number
477
+ }
478
+
479
+ export interface ListInstanceProfilesForRoleParams {
480
+ RoleName: string
481
+ Marker?: string
482
+ MaxItems?: number
483
+ }
484
+
485
+ export interface AddRoleToInstanceProfileParams {
486
+ InstanceProfileName: string
487
+ RoleName: string
488
+ }
489
+
490
+ export interface RemoveRoleFromInstanceProfileParams {
491
+ InstanceProfileName: string
492
+ RoleName: string
493
+ }
494
+
495
+ export interface DeleteInstanceProfileParams {
496
+ InstanceProfileName: string
497
+ }
498
+
499
+ // ============================================================================
500
+ // Types - Server Certificates
501
+ // ============================================================================
502
+
503
+ export interface ServerCertificate {
504
+ ServerCertificateName: string
505
+ ServerCertificateId: string
506
+ Arn: string
507
+ Path?: string
508
+ UploadDate?: string
509
+ Expiration?: string
510
+ }
511
+
512
+ export interface ServerCertificateMetadata {
513
+ ServerCertificateName: string
514
+ ServerCertificateId: string
515
+ Arn: string
516
+ Path?: string
517
+ UploadDate?: string
518
+ Expiration?: string
519
+ }
520
+
521
+ export interface UploadServerCertificateParams {
522
+ ServerCertificateName: string
523
+ CertificateBody: string
524
+ PrivateKey: string
525
+ CertificateChain?: string
526
+ Path?: string
527
+ Tags?: Array<{ Key: string; Value: string }>
528
+ }
529
+
530
+ export interface GetServerCertificateParams {
531
+ ServerCertificateName: string
532
+ }
533
+
534
+ export interface ListServerCertificatesParams {
535
+ PathPrefix?: string
536
+ Marker?: string
537
+ MaxItems?: number
538
+ }
539
+
540
+ export interface UpdateServerCertificateParams {
541
+ ServerCertificateName: string
542
+ NewServerCertificateName?: string
543
+ NewPath?: string
544
+ }
545
+
546
+ export interface DeleteServerCertificateParams {
547
+ ServerCertificateName: string
548
+ }
549
+
550
+ // ============================================================================
551
+ // Types - Account and Password Policy
552
+ // ============================================================================
553
+
554
+ export interface PasswordPolicy {
555
+ MinimumPasswordLength?: number
556
+ RequireSymbols?: boolean
557
+ RequireNumbers?: boolean
558
+ RequireUppercaseCharacters?: boolean
559
+ RequireLowercaseCharacters?: boolean
560
+ AllowUsersToChangePassword?: boolean
561
+ ExpirePasswords?: boolean
562
+ MaxPasswordAge?: number
563
+ PasswordReusePrevention?: number
564
+ HardExpiry?: boolean
565
+ }
566
+
567
+ export interface UpdateAccountPasswordPolicyParams {
568
+ MinimumPasswordLength?: number
569
+ RequireSymbols?: boolean
570
+ RequireNumbers?: boolean
571
+ RequireUppercaseCharacters?: boolean
572
+ RequireLowercaseCharacters?: boolean
573
+ AllowUsersToChangePassword?: boolean
574
+ MaxPasswordAge?: number
575
+ PasswordReusePrevention?: number
576
+ HardExpiry?: boolean
577
+ }
578
+
579
+ export interface AccountSummary {
580
+ Users?: number
581
+ UsersQuota?: number
582
+ Groups?: number
583
+ GroupsQuota?: number
584
+ ServerCertificates?: number
585
+ ServerCertificatesQuota?: number
586
+ UserPolicySizeQuota?: number
587
+ GroupPolicySizeQuota?: number
588
+ GroupsPerUserQuota?: number
589
+ SigningCertificatesPerUserQuota?: number
590
+ AccessKeysPerUserQuota?: number
591
+ MFADevices?: number
592
+ MFADevicesInUse?: number
593
+ AccountMFAEnabled?: number
594
+ AccountAccessKeysPresent?: number
595
+ AccountSigningCertificatesPresent?: number
596
+ AttachedPoliciesPerGroupQuota?: number
597
+ AttachedPoliciesPerRoleQuota?: number
598
+ AttachedPoliciesPerUserQuota?: number
599
+ Policies?: number
600
+ PoliciesQuota?: number
601
+ PolicySizeQuota?: number
602
+ PolicyVersionsInUse?: number
603
+ PolicyVersionsInUseQuota?: number
604
+ VersionsPerPolicyQuota?: number
605
+ GlobalEndpointTokenVersion?: number
606
+ }
607
+
608
+ // ============================================================================
609
+ // Types - Policy Simulation
610
+ // ============================================================================
611
+
612
+ export interface SimulatePrincipalPolicyParams {
613
+ PolicySourceArn: string
614
+ ActionNames: string[]
615
+ ResourceArns?: string[]
616
+ ResourcePolicy?: string
617
+ ResourceOwner?: string
618
+ CallerArn?: string
619
+ ContextEntries?: Array<{
620
+ ContextKeyName: string
621
+ ContextKeyValues: string[]
622
+ ContextKeyType: string
623
+ }>
624
+ ResourceHandlingOption?: string
625
+ MaxItems?: number
626
+ Marker?: string
627
+ }
628
+
629
+ export interface EvaluationResult {
630
+ EvalActionName: string
631
+ EvalResourceName?: string
632
+ EvalDecision: string
633
+ MatchedStatements?: Array<{
634
+ SourcePolicyId?: string
635
+ SourcePolicyType?: string
636
+ }>
637
+ MissingContextValues?: string[]
638
+ }
639
+
640
+ export interface SimulatePolicyResponse {
641
+ EvaluationResults: EvaluationResult[]
642
+ IsTruncated: boolean
643
+ Marker?: string
644
+ }
645
+
646
+ // ============================================================================
647
+ // Helper Functions
648
+ // ============================================================================
649
+
650
+ /**
651
+ * Build query string from parameters for IAM API
652
+ */
653
+ function buildQueryParams(action: string, params: Record<string, unknown>): string {
654
+ const queryParams: string[] = [`Action=${action}`, 'Version=2010-05-08']
655
+
656
+ for (const [key, value] of Object.entries(params)) {
657
+ if (value === undefined || value === null) continue
658
+
659
+ if (Array.isArray(value)) {
660
+ // Handle arrays (e.g., Tags, TagKeys)
661
+ value.forEach((item, index) => {
662
+ if (typeof item === 'object' && item !== null) {
663
+ // Handle array of objects (e.g., Tags)
664
+ for (const [subKey, subValue] of Object.entries(item as Record<string, unknown>)) {
665
+ queryParams.push(`${key}.member.${index + 1}.${subKey}=${encodeURIComponent(String(subValue))}`)
666
+ }
667
+ } else {
668
+ queryParams.push(`${key}.member.${index + 1}=${encodeURIComponent(String(item))}`)
669
+ }
670
+ })
671
+ } else if (typeof value === 'object') {
672
+ // Handle nested objects
673
+ for (const [subKey, subValue] of Object.entries(value as Record<string, unknown>)) {
674
+ if (subValue !== undefined && subValue !== null) {
675
+ queryParams.push(`${key}.${subKey}=${encodeURIComponent(String(subValue))}`)
676
+ }
677
+ }
678
+ } else {
679
+ queryParams.push(`${key}=${encodeURIComponent(String(value))}`)
680
+ }
681
+ }
682
+
683
+ return queryParams.join('&')
684
+ }
685
+
686
+ /**
687
+ * Parse XML response from IAM API
688
+ */
689
+ function parseXmlValue(xml: string, tag: string): string | undefined {
690
+ const regex = new RegExp(`<${tag}>([^<]*)</${tag}>`)
691
+ const match = xml.match(regex)
692
+ return match ? match[1] : undefined
693
+ }
694
+
695
+ /**
696
+ * Parse XML array from IAM API
697
+ */
698
+ function parseXmlArray(xml: string, containerTag: string, itemTag: string): string[] {
699
+ const containerRegex = new RegExp(`<${containerTag}>([\\s\\S]*?)</${containerTag}>`)
700
+ const containerMatch = xml.match(containerRegex)
701
+ if (!containerMatch) return []
702
+
703
+ const items: string[] = []
704
+ const itemRegex = new RegExp(`<${itemTag}>([\\s\\S]*?)</${itemTag}>`, 'g')
705
+ let match
706
+ while ((match = itemRegex.exec(containerMatch[1])) !== null) {
707
+ items.push(match[1])
708
+ }
709
+ return items
710
+ }
711
+
712
+ // ============================================================================
713
+ // IAM Client
714
+ // ============================================================================
715
+
716
+ export class IAMClient {
717
+ private client: AWSClient
718
+ private region: string
719
+
720
+ constructor(region: string = 'us-east-1') {
721
+ this.client = new AWSClient()
722
+ this.region = region
723
+ }
724
+
725
+ /**
726
+ * Make IAM API request
727
+ */
728
+ private async request(action: string, params: object = {}): Promise<any> {
729
+ const body = buildQueryParams(action, params as Record<string, unknown>)
730
+
731
+ const response = await this.client.request({
732
+ service: 'iam',
733
+ region: this.region,
734
+ method: 'POST',
735
+ path: '/',
736
+ headers: {
737
+ 'Content-Type': 'application/x-www-form-urlencoded',
738
+ },
739
+ body,
740
+ })
741
+
742
+ return response
743
+ }
744
+
745
+ // ==========================================================================
746
+ // User Operations
747
+ // ==========================================================================
748
+
749
+ /**
750
+ * Create a new IAM user
751
+ */
752
+ async createUser(params: CreateUserParams): Promise<IAMUser> {
753
+ const response = await this.request('CreateUser', params)
754
+ return this.parseUser(response)
755
+ }
756
+
757
+ /**
758
+ * Get information about an IAM user
759
+ */
760
+ async getUser(params: GetUserParams = {}): Promise<IAMUser> {
761
+ const response = await this.request('GetUser', params)
762
+ return this.parseUser(response)
763
+ }
764
+
765
+ /**
766
+ * List IAM users
767
+ */
768
+ async listUsers(params: ListUsersParams = {}): Promise<{ Users: IAMUser[]; IsTruncated: boolean; Marker?: string }> {
769
+ const response = await this.request('ListUsers', params)
770
+ const users = this.parseUsers(response)
771
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
772
+ const marker = parseXmlValue(response, 'Marker')
773
+ return { Users: users, IsTruncated: isTruncated, Marker: marker }
774
+ }
775
+
776
+ /**
777
+ * Update an IAM user
778
+ */
779
+ async updateUser(params: UpdateUserParams): Promise<void> {
780
+ await this.request('UpdateUser', params)
781
+ }
782
+
783
+ /**
784
+ * Delete an IAM user
785
+ */
786
+ async deleteUser(params: DeleteUserParams): Promise<void> {
787
+ await this.request('DeleteUser', params)
788
+ }
789
+
790
+ /**
791
+ * Parse user from XML response
792
+ */
793
+ private parseUser(xml: string): IAMUser {
794
+ return {
795
+ UserName: parseXmlValue(xml, 'UserName') || '',
796
+ UserId: parseXmlValue(xml, 'UserId') || '',
797
+ Arn: parseXmlValue(xml, 'Arn') || '',
798
+ Path: parseXmlValue(xml, 'Path'),
799
+ CreateDate: parseXmlValue(xml, 'CreateDate'),
800
+ PasswordLastUsed: parseXmlValue(xml, 'PasswordLastUsed'),
801
+ }
802
+ }
803
+
804
+ /**
805
+ * Parse users array from XML response
806
+ */
807
+ private parseUsers(xml: string): IAMUser[] {
808
+ const memberXmls = parseXmlArray(xml, 'Users', 'member')
809
+ return memberXmls.map((memberXml) => ({
810
+ UserName: parseXmlValue(memberXml, 'UserName') || '',
811
+ UserId: parseXmlValue(memberXml, 'UserId') || '',
812
+ Arn: parseXmlValue(memberXml, 'Arn') || '',
813
+ Path: parseXmlValue(memberXml, 'Path'),
814
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
815
+ PasswordLastUsed: parseXmlValue(memberXml, 'PasswordLastUsed'),
816
+ }))
817
+ }
818
+
819
+ // ==========================================================================
820
+ // Group Operations
821
+ // ==========================================================================
822
+
823
+ /**
824
+ * Create a new IAM group
825
+ */
826
+ async createGroup(params: CreateGroupParams): Promise<IAMGroup> {
827
+ const response = await this.request('CreateGroup', params)
828
+ return this.parseGroup(response)
829
+ }
830
+
831
+ /**
832
+ * Get information about an IAM group
833
+ */
834
+ async getGroup(params: GetGroupParams): Promise<{ Group: IAMGroup; Users: IAMUser[]; IsTruncated: boolean; Marker?: string }> {
835
+ const response = await this.request('GetGroup', params)
836
+ const group = this.parseGroup(response)
837
+ const users = this.parseUsers(response)
838
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
839
+ const marker = parseXmlValue(response, 'Marker')
840
+ return { Group: group, Users: users, IsTruncated: isTruncated, Marker: marker }
841
+ }
842
+
843
+ /**
844
+ * List IAM groups
845
+ */
846
+ async listGroups(params: ListGroupsParams = {}): Promise<{ Groups: IAMGroup[]; IsTruncated: boolean; Marker?: string }> {
847
+ const response = await this.request('ListGroups', params)
848
+ const groups = this.parseGroups(response)
849
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
850
+ const marker = parseXmlValue(response, 'Marker')
851
+ return { Groups: groups, IsTruncated: isTruncated, Marker: marker }
852
+ }
853
+
854
+ /**
855
+ * Update an IAM group
856
+ */
857
+ async updateGroup(params: UpdateGroupParams): Promise<void> {
858
+ await this.request('UpdateGroup', params)
859
+ }
860
+
861
+ /**
862
+ * Delete an IAM group
863
+ */
864
+ async deleteGroup(params: DeleteGroupParams): Promise<void> {
865
+ await this.request('DeleteGroup', params)
866
+ }
867
+
868
+ /**
869
+ * Add a user to a group
870
+ */
871
+ async addUserToGroup(params: AddUserToGroupParams): Promise<void> {
872
+ await this.request('AddUserToGroup', params)
873
+ }
874
+
875
+ /**
876
+ * Remove a user from a group
877
+ */
878
+ async removeUserFromGroup(params: RemoveUserFromGroupParams): Promise<void> {
879
+ await this.request('RemoveUserFromGroup', params)
880
+ }
881
+
882
+ /**
883
+ * List groups for a user
884
+ */
885
+ async listGroupsForUser(params: ListGroupsForUserParams): Promise<{ Groups: IAMGroup[]; IsTruncated: boolean; Marker?: string }> {
886
+ const response = await this.request('ListGroupsForUser', params)
887
+ const groups = this.parseGroups(response)
888
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
889
+ const marker = parseXmlValue(response, 'Marker')
890
+ return { Groups: groups, IsTruncated: isTruncated, Marker: marker }
891
+ }
892
+
893
+ /**
894
+ * Parse group from XML response
895
+ */
896
+ private parseGroup(xml: string): IAMGroup {
897
+ return {
898
+ GroupName: parseXmlValue(xml, 'GroupName') || '',
899
+ GroupId: parseXmlValue(xml, 'GroupId') || '',
900
+ Arn: parseXmlValue(xml, 'Arn') || '',
901
+ Path: parseXmlValue(xml, 'Path'),
902
+ CreateDate: parseXmlValue(xml, 'CreateDate'),
903
+ }
904
+ }
905
+
906
+ /**
907
+ * Parse groups array from XML response
908
+ */
909
+ private parseGroups(xml: string): IAMGroup[] {
910
+ const memberXmls = parseXmlArray(xml, 'Groups', 'member')
911
+ return memberXmls.map((memberXml) => ({
912
+ GroupName: parseXmlValue(memberXml, 'GroupName') || '',
913
+ GroupId: parseXmlValue(memberXml, 'GroupId') || '',
914
+ Arn: parseXmlValue(memberXml, 'Arn') || '',
915
+ Path: parseXmlValue(memberXml, 'Path'),
916
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
917
+ }))
918
+ }
919
+
920
+ // ==========================================================================
921
+ // Role Operations
922
+ // ==========================================================================
923
+
924
+ /**
925
+ * Create a new IAM role
926
+ */
927
+ async createRole(params: CreateRoleParams): Promise<IAMRole> {
928
+ const response = await this.request('CreateRole', params)
929
+ return this.parseRole(response)
930
+ }
931
+
932
+ /**
933
+ * Get information about an IAM role
934
+ */
935
+ async getRole(params: GetRoleParams): Promise<IAMRole> {
936
+ const response = await this.request('GetRole', params)
937
+ return this.parseRole(response)
938
+ }
939
+
940
+ /**
941
+ * List IAM roles
942
+ */
943
+ async listRoles(params: ListRolesParams = {}): Promise<{ Roles: IAMRole[]; IsTruncated: boolean; Marker?: string }> {
944
+ const response = await this.request('ListRoles', params)
945
+ const roles = this.parseRoles(response)
946
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
947
+ const marker = parseXmlValue(response, 'Marker')
948
+ return { Roles: roles, IsTruncated: isTruncated, Marker: marker }
949
+ }
950
+
951
+ /**
952
+ * Update an IAM role
953
+ */
954
+ async updateRole(params: UpdateRoleParams): Promise<void> {
955
+ await this.request('UpdateRole', params)
956
+ }
957
+
958
+ /**
959
+ * Update an IAM role description
960
+ */
961
+ async updateRoleDescription(params: UpdateRoleDescriptionParams): Promise<IAMRole> {
962
+ const response = await this.request('UpdateRoleDescription', params)
963
+ return this.parseRole(response)
964
+ }
965
+
966
+ /**
967
+ * Update the assume role policy for a role
968
+ */
969
+ async updateAssumeRolePolicy(params: UpdateAssumeRolePolicyParams): Promise<void> {
970
+ await this.request('UpdateAssumeRolePolicy', params)
971
+ }
972
+
973
+ /**
974
+ * Delete an IAM role
975
+ */
976
+ async deleteRole(params: DeleteRoleParams): Promise<void> {
977
+ await this.request('DeleteRole', params)
978
+ }
979
+
980
+ /**
981
+ * Tag an IAM role
982
+ */
983
+ async tagRole(params: TagRoleParams): Promise<void> {
984
+ await this.request('TagRole', params)
985
+ }
986
+
987
+ /**
988
+ * Untag an IAM role
989
+ */
990
+ async untagRole(params: UntagRoleParams): Promise<void> {
991
+ await this.request('UntagRole', params)
992
+ }
993
+
994
+ /**
995
+ * List tags for an IAM role
996
+ */
997
+ async listRoleTags(params: ListRoleTagsParams): Promise<{ Tags: Array<{ Key: string; Value: string }>; IsTruncated: boolean; Marker?: string }> {
998
+ const response = await this.request('ListRoleTags', params)
999
+ const tags = this.parseTags(response)
1000
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1001
+ const marker = parseXmlValue(response, 'Marker')
1002
+ return { Tags: tags, IsTruncated: isTruncated, Marker: marker }
1003
+ }
1004
+
1005
+ /**
1006
+ * Parse role from XML response
1007
+ */
1008
+ private parseRole(xml: string): IAMRole {
1009
+ return {
1010
+ RoleName: parseXmlValue(xml, 'RoleName') || '',
1011
+ RoleId: parseXmlValue(xml, 'RoleId') || '',
1012
+ Arn: parseXmlValue(xml, 'Arn') || '',
1013
+ Path: parseXmlValue(xml, 'Path'),
1014
+ CreateDate: parseXmlValue(xml, 'CreateDate'),
1015
+ AssumeRolePolicyDocument: parseXmlValue(xml, 'AssumeRolePolicyDocument'),
1016
+ Description: parseXmlValue(xml, 'Description'),
1017
+ MaxSessionDuration: parseXmlValue(xml, 'MaxSessionDuration') ? Number.parseInt(parseXmlValue(xml, 'MaxSessionDuration')!, 10) : undefined,
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * Parse roles array from XML response
1023
+ */
1024
+ private parseRoles(xml: string): IAMRole[] {
1025
+ const memberXmls = parseXmlArray(xml, 'Roles', 'member')
1026
+ return memberXmls.map((memberXml) => ({
1027
+ RoleName: parseXmlValue(memberXml, 'RoleName') || '',
1028
+ RoleId: parseXmlValue(memberXml, 'RoleId') || '',
1029
+ Arn: parseXmlValue(memberXml, 'Arn') || '',
1030
+ Path: parseXmlValue(memberXml, 'Path'),
1031
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
1032
+ AssumeRolePolicyDocument: parseXmlValue(memberXml, 'AssumeRolePolicyDocument'),
1033
+ Description: parseXmlValue(memberXml, 'Description'),
1034
+ MaxSessionDuration: parseXmlValue(memberXml, 'MaxSessionDuration') ? Number.parseInt(parseXmlValue(memberXml, 'MaxSessionDuration')!, 10) : undefined,
1035
+ }))
1036
+ }
1037
+
1038
+ /**
1039
+ * Parse tags from XML response
1040
+ */
1041
+ private parseTags(xml: string): Array<{ Key: string; Value: string }> {
1042
+ const memberXmls = parseXmlArray(xml, 'Tags', 'member')
1043
+ return memberXmls.map((memberXml) => ({
1044
+ Key: parseXmlValue(memberXml, 'Key') || '',
1045
+ Value: parseXmlValue(memberXml, 'Value') || '',
1046
+ }))
1047
+ }
1048
+
1049
+ // ==========================================================================
1050
+ // Managed Policy Operations
1051
+ // ==========================================================================
1052
+
1053
+ /**
1054
+ * Create a new managed policy
1055
+ */
1056
+ async createPolicy(params: CreatePolicyParams): Promise<IAMPolicy> {
1057
+ const response = await this.request('CreatePolicy', params)
1058
+ return this.parsePolicy(response)
1059
+ }
1060
+
1061
+ /**
1062
+ * Get information about a managed policy
1063
+ */
1064
+ async getPolicy(params: GetPolicyParams): Promise<IAMPolicy> {
1065
+ const response = await this.request('GetPolicy', params)
1066
+ return this.parsePolicy(response)
1067
+ }
1068
+
1069
+ /**
1070
+ * Get a specific version of a managed policy
1071
+ */
1072
+ async getPolicyVersion(params: GetPolicyVersionParams): Promise<PolicyVersion> {
1073
+ const response = await this.request('GetPolicyVersion', params)
1074
+ return {
1075
+ VersionId: parseXmlValue(response, 'VersionId') || '',
1076
+ IsDefaultVersion: parseXmlValue(response, 'IsDefaultVersion') === 'true',
1077
+ CreateDate: parseXmlValue(response, 'CreateDate'),
1078
+ Document: parseXmlValue(response, 'Document'),
1079
+ }
1080
+ }
1081
+
1082
+ /**
1083
+ * List managed policies
1084
+ */
1085
+ async listPolicies(params: ListPoliciesParams = {}): Promise<{ Policies: IAMPolicy[]; IsTruncated: boolean; Marker?: string }> {
1086
+ const response = await this.request('ListPolicies', params)
1087
+ const policies = this.parsePolicies(response)
1088
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1089
+ const marker = parseXmlValue(response, 'Marker')
1090
+ return { Policies: policies, IsTruncated: isTruncated, Marker: marker }
1091
+ }
1092
+
1093
+ /**
1094
+ * List versions of a managed policy
1095
+ */
1096
+ async listPolicyVersions(params: ListPolicyVersionsParams): Promise<{ Versions: PolicyVersion[]; IsTruncated: boolean; Marker?: string }> {
1097
+ const response = await this.request('ListPolicyVersions', params)
1098
+ const versions = this.parsePolicyVersions(response)
1099
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1100
+ const marker = parseXmlValue(response, 'Marker')
1101
+ return { Versions: versions, IsTruncated: isTruncated, Marker: marker }
1102
+ }
1103
+
1104
+ /**
1105
+ * Create a new version of a managed policy
1106
+ */
1107
+ async createPolicyVersion(params: CreatePolicyVersionParams): Promise<PolicyVersion> {
1108
+ const response = await this.request('CreatePolicyVersion', params)
1109
+ return {
1110
+ VersionId: parseXmlValue(response, 'VersionId') || '',
1111
+ IsDefaultVersion: parseXmlValue(response, 'IsDefaultVersion') === 'true',
1112
+ CreateDate: parseXmlValue(response, 'CreateDate'),
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * Delete a version of a managed policy
1118
+ */
1119
+ async deletePolicyVersion(params: DeletePolicyVersionParams): Promise<void> {
1120
+ await this.request('DeletePolicyVersion', params)
1121
+ }
1122
+
1123
+ /**
1124
+ * Set the default version of a managed policy
1125
+ */
1126
+ async setDefaultPolicyVersion(params: SetDefaultPolicyVersionParams): Promise<void> {
1127
+ await this.request('SetDefaultPolicyVersion', params)
1128
+ }
1129
+
1130
+ /**
1131
+ * Delete a managed policy
1132
+ */
1133
+ async deletePolicy(params: DeletePolicyParams): Promise<void> {
1134
+ await this.request('DeletePolicy', params)
1135
+ }
1136
+
1137
+ /**
1138
+ * Attach a managed policy to a user
1139
+ */
1140
+ async attachUserPolicy(params: AttachUserPolicyParams): Promise<void> {
1141
+ await this.request('AttachUserPolicy', params)
1142
+ }
1143
+
1144
+ /**
1145
+ * Detach a managed policy from a user
1146
+ */
1147
+ async detachUserPolicy(params: DetachUserPolicyParams): Promise<void> {
1148
+ await this.request('DetachUserPolicy', params)
1149
+ }
1150
+
1151
+ /**
1152
+ * Attach a managed policy to a group
1153
+ */
1154
+ async attachGroupPolicy(params: AttachGroupPolicyParams): Promise<void> {
1155
+ await this.request('AttachGroupPolicy', params)
1156
+ }
1157
+
1158
+ /**
1159
+ * Detach a managed policy from a group
1160
+ */
1161
+ async detachGroupPolicy(params: DetachGroupPolicyParams): Promise<void> {
1162
+ await this.request('DetachGroupPolicy', params)
1163
+ }
1164
+
1165
+ /**
1166
+ * Attach a managed policy to a role
1167
+ */
1168
+ async attachRolePolicy(params: AttachRolePolicyParams): Promise<void> {
1169
+ await this.request('AttachRolePolicy', params)
1170
+ }
1171
+
1172
+ /**
1173
+ * Detach a managed policy from a role
1174
+ */
1175
+ async detachRolePolicy(params: DetachRolePolicyParams): Promise<void> {
1176
+ await this.request('DetachRolePolicy', params)
1177
+ }
1178
+
1179
+ /**
1180
+ * List managed policies attached to a user
1181
+ */
1182
+ async listAttachedUserPolicies(params: ListAttachedUserPoliciesParams): Promise<{ AttachedPolicies: Array<{ PolicyName: string; PolicyArn: string }>; IsTruncated: boolean; Marker?: string }> {
1183
+ const response = await this.request('ListAttachedUserPolicies', params)
1184
+ const policies = this.parseAttachedPolicies(response)
1185
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1186
+ const marker = parseXmlValue(response, 'Marker')
1187
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker }
1188
+ }
1189
+
1190
+ /**
1191
+ * List managed policies attached to a group
1192
+ */
1193
+ async listAttachedGroupPolicies(params: ListAttachedGroupPoliciesParams): Promise<{ AttachedPolicies: Array<{ PolicyName: string; PolicyArn: string }>; IsTruncated: boolean; Marker?: string }> {
1194
+ const response = await this.request('ListAttachedGroupPolicies', params)
1195
+ const policies = this.parseAttachedPolicies(response)
1196
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1197
+ const marker = parseXmlValue(response, 'Marker')
1198
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker }
1199
+ }
1200
+
1201
+ /**
1202
+ * List managed policies attached to a role
1203
+ */
1204
+ async listAttachedRolePolicies(params: ListAttachedRolePoliciesParams): Promise<{ AttachedPolicies: Array<{ PolicyName: string; PolicyArn: string }>; IsTruncated: boolean; Marker?: string }> {
1205
+ const response = await this.request('ListAttachedRolePolicies', params)
1206
+ const policies = this.parseAttachedPolicies(response)
1207
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1208
+ const marker = parseXmlValue(response, 'Marker')
1209
+ return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker }
1210
+ }
1211
+
1212
+ /**
1213
+ * Parse policy from XML response
1214
+ */
1215
+ private parsePolicy(xml: string): IAMPolicy {
1216
+ return {
1217
+ PolicyName: parseXmlValue(xml, 'PolicyName') || '',
1218
+ PolicyId: parseXmlValue(xml, 'PolicyId') || '',
1219
+ Arn: parseXmlValue(xml, 'Arn') || '',
1220
+ Path: parseXmlValue(xml, 'Path'),
1221
+ DefaultVersionId: parseXmlValue(xml, 'DefaultVersionId'),
1222
+ AttachmentCount: parseXmlValue(xml, 'AttachmentCount') ? Number.parseInt(parseXmlValue(xml, 'AttachmentCount')!, 10) : undefined,
1223
+ PermissionsBoundaryUsageCount: parseXmlValue(xml, 'PermissionsBoundaryUsageCount') ? Number.parseInt(parseXmlValue(xml, 'PermissionsBoundaryUsageCount')!, 10) : undefined,
1224
+ IsAttachable: parseXmlValue(xml, 'IsAttachable') === 'true',
1225
+ Description: parseXmlValue(xml, 'Description'),
1226
+ CreateDate: parseXmlValue(xml, 'CreateDate'),
1227
+ UpdateDate: parseXmlValue(xml, 'UpdateDate'),
1228
+ }
1229
+ }
1230
+
1231
+ /**
1232
+ * Parse policies array from XML response
1233
+ */
1234
+ private parsePolicies(xml: string): IAMPolicy[] {
1235
+ const memberXmls = parseXmlArray(xml, 'Policies', 'member')
1236
+ return memberXmls.map((memberXml) => ({
1237
+ PolicyName: parseXmlValue(memberXml, 'PolicyName') || '',
1238
+ PolicyId: parseXmlValue(memberXml, 'PolicyId') || '',
1239
+ Arn: parseXmlValue(memberXml, 'Arn') || '',
1240
+ Path: parseXmlValue(memberXml, 'Path'),
1241
+ DefaultVersionId: parseXmlValue(memberXml, 'DefaultVersionId'),
1242
+ AttachmentCount: parseXmlValue(memberXml, 'AttachmentCount') ? Number.parseInt(parseXmlValue(memberXml, 'AttachmentCount')!, 10) : undefined,
1243
+ PermissionsBoundaryUsageCount: parseXmlValue(memberXml, 'PermissionsBoundaryUsageCount') ? Number.parseInt(parseXmlValue(memberXml, 'PermissionsBoundaryUsageCount')!, 10) : undefined,
1244
+ IsAttachable: parseXmlValue(memberXml, 'IsAttachable') === 'true',
1245
+ Description: parseXmlValue(memberXml, 'Description'),
1246
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
1247
+ UpdateDate: parseXmlValue(memberXml, 'UpdateDate'),
1248
+ }))
1249
+ }
1250
+
1251
+ /**
1252
+ * Parse policy versions from XML response
1253
+ */
1254
+ private parsePolicyVersions(xml: string): PolicyVersion[] {
1255
+ const memberXmls = parseXmlArray(xml, 'Versions', 'member')
1256
+ return memberXmls.map((memberXml) => ({
1257
+ VersionId: parseXmlValue(memberXml, 'VersionId') || '',
1258
+ IsDefaultVersion: parseXmlValue(memberXml, 'IsDefaultVersion') === 'true',
1259
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
1260
+ }))
1261
+ }
1262
+
1263
+ /**
1264
+ * Parse attached policies from XML response
1265
+ */
1266
+ private parseAttachedPolicies(xml: string): Array<{ PolicyName: string; PolicyArn: string }> {
1267
+ const memberXmls = parseXmlArray(xml, 'AttachedPolicies', 'member')
1268
+ return memberXmls.map((memberXml) => ({
1269
+ PolicyName: parseXmlValue(memberXml, 'PolicyName') || '',
1270
+ PolicyArn: parseXmlValue(memberXml, 'PolicyArn') || '',
1271
+ }))
1272
+ }
1273
+
1274
+ // ==========================================================================
1275
+ // Inline Policy Operations
1276
+ // ==========================================================================
1277
+
1278
+ /**
1279
+ * Add or update an inline policy for a user
1280
+ */
1281
+ async putUserPolicy(params: PutUserPolicyParams): Promise<void> {
1282
+ await this.request('PutUserPolicy', params)
1283
+ }
1284
+
1285
+ /**
1286
+ * Get an inline policy for a user
1287
+ */
1288
+ async getUserPolicy(params: GetUserPolicyParams): Promise<{ UserName: string; PolicyName: string; PolicyDocument: string }> {
1289
+ const response = await this.request('GetUserPolicy', params)
1290
+ return {
1291
+ UserName: parseXmlValue(response, 'UserName') || '',
1292
+ PolicyName: parseXmlValue(response, 'PolicyName') || '',
1293
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, 'PolicyDocument') || ''),
1294
+ }
1295
+ }
1296
+
1297
+ /**
1298
+ * Delete an inline policy from a user
1299
+ */
1300
+ async deleteUserPolicy(params: DeleteUserPolicyParams): Promise<void> {
1301
+ await this.request('DeleteUserPolicy', params)
1302
+ }
1303
+
1304
+ /**
1305
+ * List inline policies for a user
1306
+ */
1307
+ async listUserPolicies(params: ListUserPoliciesParams): Promise<{ PolicyNames: string[]; IsTruncated: boolean; Marker?: string }> {
1308
+ const response = await this.request('ListUserPolicies', params)
1309
+ const policyNames = parseXmlArray(response, 'PolicyNames', 'member')
1310
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1311
+ const marker = parseXmlValue(response, 'Marker')
1312
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker }
1313
+ }
1314
+
1315
+ /**
1316
+ * Add or update an inline policy for a group
1317
+ */
1318
+ async putGroupPolicy(params: PutGroupPolicyParams): Promise<void> {
1319
+ await this.request('PutGroupPolicy', params)
1320
+ }
1321
+
1322
+ /**
1323
+ * Get an inline policy for a group
1324
+ */
1325
+ async getGroupPolicy(params: GetGroupPolicyParams): Promise<{ GroupName: string; PolicyName: string; PolicyDocument: string }> {
1326
+ const response = await this.request('GetGroupPolicy', params)
1327
+ return {
1328
+ GroupName: parseXmlValue(response, 'GroupName') || '',
1329
+ PolicyName: parseXmlValue(response, 'PolicyName') || '',
1330
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, 'PolicyDocument') || ''),
1331
+ }
1332
+ }
1333
+
1334
+ /**
1335
+ * Delete an inline policy from a group
1336
+ */
1337
+ async deleteGroupPolicy(params: DeleteGroupPolicyParams): Promise<void> {
1338
+ await this.request('DeleteGroupPolicy', params)
1339
+ }
1340
+
1341
+ /**
1342
+ * List inline policies for a group
1343
+ */
1344
+ async listGroupPolicies(params: ListGroupPoliciesParams): Promise<{ PolicyNames: string[]; IsTruncated: boolean; Marker?: string }> {
1345
+ const response = await this.request('ListGroupPolicies', params)
1346
+ const policyNames = parseXmlArray(response, 'PolicyNames', 'member')
1347
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1348
+ const marker = parseXmlValue(response, 'Marker')
1349
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker }
1350
+ }
1351
+
1352
+ /**
1353
+ * Add or update an inline policy for a role
1354
+ */
1355
+ async putRolePolicy(params: PutRolePolicyParams): Promise<void> {
1356
+ await this.request('PutRolePolicy', params)
1357
+ }
1358
+
1359
+ /**
1360
+ * Get an inline policy for a role
1361
+ */
1362
+ async getRolePolicy(params: GetRolePolicyParams): Promise<{ RoleName: string; PolicyName: string; PolicyDocument: string }> {
1363
+ const response = await this.request('GetRolePolicy', params)
1364
+ return {
1365
+ RoleName: parseXmlValue(response, 'RoleName') || '',
1366
+ PolicyName: parseXmlValue(response, 'PolicyName') || '',
1367
+ PolicyDocument: decodeURIComponent(parseXmlValue(response, 'PolicyDocument') || ''),
1368
+ }
1369
+ }
1370
+
1371
+ /**
1372
+ * Delete an inline policy from a role
1373
+ */
1374
+ async deleteRolePolicy(params: DeleteRolePolicyParams): Promise<void> {
1375
+ await this.request('DeleteRolePolicy', params)
1376
+ }
1377
+
1378
+ /**
1379
+ * List inline policies for a role
1380
+ */
1381
+ async listRolePolicies(params: ListRolePoliciesParams): Promise<{ PolicyNames: string[]; IsTruncated: boolean; Marker?: string }> {
1382
+ const response = await this.request('ListRolePolicies', params)
1383
+ const policyNames = parseXmlArray(response, 'PolicyNames', 'member')
1384
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1385
+ const marker = parseXmlValue(response, 'Marker')
1386
+ return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker }
1387
+ }
1388
+
1389
+ // ==========================================================================
1390
+ // Access Key Operations
1391
+ // ==========================================================================
1392
+
1393
+ /**
1394
+ * Create an access key for a user
1395
+ */
1396
+ async createAccessKey(params: CreateAccessKeyParams = {}): Promise<CreateAccessKeyResult> {
1397
+ const response = await this.request('CreateAccessKey', params)
1398
+
1399
+ // Handle both string (XML) and object (parsed) responses
1400
+ if (typeof response === 'object') {
1401
+ const accessKey = (response as any)?.CreateAccessKeyResult?.AccessKey
1402
+ || (response as any)?.AccessKey
1403
+ if (accessKey) {
1404
+ return {
1405
+ AccessKey: {
1406
+ UserName: accessKey.UserName || '',
1407
+ AccessKeyId: accessKey.AccessKeyId || '',
1408
+ Status: (accessKey.Status as 'Active' | 'Inactive') || 'Active',
1409
+ SecretAccessKey: accessKey.SecretAccessKey || '',
1410
+ CreateDate: accessKey.CreateDate,
1411
+ },
1412
+ }
1413
+ }
1414
+ }
1415
+
1416
+ // Fallback to XML parsing for string responses
1417
+ return {
1418
+ AccessKey: {
1419
+ UserName: parseXmlValue(response as string, 'UserName') || '',
1420
+ AccessKeyId: parseXmlValue(response as string, 'AccessKeyId') || '',
1421
+ Status: (parseXmlValue(response as string, 'Status') as 'Active' | 'Inactive') || 'Active',
1422
+ SecretAccessKey: parseXmlValue(response as string, 'SecretAccessKey') || '',
1423
+ CreateDate: parseXmlValue(response as string, 'CreateDate'),
1424
+ },
1425
+ }
1426
+ }
1427
+
1428
+ /**
1429
+ * List access keys for a user
1430
+ */
1431
+ async listAccessKeys(params: ListAccessKeysParams = {}): Promise<{ AccessKeyMetadata: AccessKeyMetadata[]; IsTruncated: boolean; Marker?: string }> {
1432
+ const response = await this.request('ListAccessKeys', params)
1433
+ const keys = this.parseAccessKeys(response)
1434
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1435
+ const marker = parseXmlValue(response, 'Marker')
1436
+ return { AccessKeyMetadata: keys, IsTruncated: isTruncated, Marker: marker }
1437
+ }
1438
+
1439
+ /**
1440
+ * Update an access key status
1441
+ */
1442
+ async updateAccessKey(params: UpdateAccessKeyParams): Promise<void> {
1443
+ await this.request('UpdateAccessKey', params)
1444
+ }
1445
+
1446
+ /**
1447
+ * Delete an access key
1448
+ */
1449
+ async deleteAccessKey(params: DeleteAccessKeyParams): Promise<void> {
1450
+ await this.request('DeleteAccessKey', params)
1451
+ }
1452
+
1453
+ /**
1454
+ * Get information about when an access key was last used
1455
+ */
1456
+ async getAccessKeyLastUsed(params: GetAccessKeyLastUsedParams): Promise<{ UserName: string; AccessKeyLastUsed: { LastUsedDate?: string; ServiceName?: string; Region?: string } }> {
1457
+ const response = await this.request('GetAccessKeyLastUsed', params)
1458
+ return {
1459
+ UserName: parseXmlValue(response, 'UserName') || '',
1460
+ AccessKeyLastUsed: {
1461
+ LastUsedDate: parseXmlValue(response, 'LastUsedDate'),
1462
+ ServiceName: parseXmlValue(response, 'ServiceName'),
1463
+ Region: parseXmlValue(response, 'Region'),
1464
+ },
1465
+ }
1466
+ }
1467
+
1468
+ /**
1469
+ * Parse access keys from XML response
1470
+ */
1471
+ private parseAccessKeys(xml: string): AccessKeyMetadata[] {
1472
+ const memberXmls = parseXmlArray(xml, 'AccessKeyMetadata', 'member')
1473
+ return memberXmls.map((memberXml) => ({
1474
+ UserName: parseXmlValue(memberXml, 'UserName'),
1475
+ AccessKeyId: parseXmlValue(memberXml, 'AccessKeyId') || '',
1476
+ Status: (parseXmlValue(memberXml, 'Status') as 'Active' | 'Inactive') || 'Active',
1477
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
1478
+ }))
1479
+ }
1480
+
1481
+ // ==========================================================================
1482
+ // Instance Profile Operations
1483
+ // ==========================================================================
1484
+
1485
+ /**
1486
+ * Create an instance profile
1487
+ */
1488
+ async createInstanceProfile(params: CreateInstanceProfileParams): Promise<InstanceProfile> {
1489
+ const response = await this.request('CreateInstanceProfile', params)
1490
+ return this.parseInstanceProfile(response)
1491
+ }
1492
+
1493
+ /**
1494
+ * Get information about an instance profile
1495
+ */
1496
+ async getInstanceProfile(params: GetInstanceProfileParams): Promise<InstanceProfile> {
1497
+ const response = await this.request('GetInstanceProfile', params)
1498
+ return this.parseInstanceProfile(response)
1499
+ }
1500
+
1501
+ /**
1502
+ * List instance profiles
1503
+ */
1504
+ async listInstanceProfiles(params: ListInstanceProfilesParams = {}): Promise<{ InstanceProfiles: InstanceProfile[]; IsTruncated: boolean; Marker?: string }> {
1505
+ const response = await this.request('ListInstanceProfiles', params)
1506
+ const profiles = this.parseInstanceProfiles(response)
1507
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1508
+ const marker = parseXmlValue(response, 'Marker')
1509
+ return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker }
1510
+ }
1511
+
1512
+ /**
1513
+ * List instance profiles for a role
1514
+ */
1515
+ async listInstanceProfilesForRole(params: ListInstanceProfilesForRoleParams): Promise<{ InstanceProfiles: InstanceProfile[]; IsTruncated: boolean; Marker?: string }> {
1516
+ const response = await this.request('ListInstanceProfilesForRole', params)
1517
+ const profiles = this.parseInstanceProfiles(response)
1518
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1519
+ const marker = parseXmlValue(response, 'Marker')
1520
+ return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker }
1521
+ }
1522
+
1523
+ /**
1524
+ * Add a role to an instance profile
1525
+ */
1526
+ async addRoleToInstanceProfile(params: AddRoleToInstanceProfileParams): Promise<void> {
1527
+ await this.request('AddRoleToInstanceProfile', params)
1528
+ }
1529
+
1530
+ /**
1531
+ * Remove a role from an instance profile
1532
+ */
1533
+ async removeRoleFromInstanceProfile(params: RemoveRoleFromInstanceProfileParams): Promise<void> {
1534
+ await this.request('RemoveRoleFromInstanceProfile', params)
1535
+ }
1536
+
1537
+ /**
1538
+ * Delete an instance profile
1539
+ */
1540
+ async deleteInstanceProfile(params: DeleteInstanceProfileParams): Promise<void> {
1541
+ await this.request('DeleteInstanceProfile', params)
1542
+ }
1543
+
1544
+ /**
1545
+ * Parse instance profile from XML response
1546
+ */
1547
+ private parseInstanceProfile(xml: string): InstanceProfile {
1548
+ return {
1549
+ InstanceProfileName: parseXmlValue(xml, 'InstanceProfileName') || '',
1550
+ InstanceProfileId: parseXmlValue(xml, 'InstanceProfileId') || '',
1551
+ Arn: parseXmlValue(xml, 'Arn') || '',
1552
+ Path: parseXmlValue(xml, 'Path'),
1553
+ CreateDate: parseXmlValue(xml, 'CreateDate'),
1554
+ }
1555
+ }
1556
+
1557
+ /**
1558
+ * Parse instance profiles from XML response
1559
+ */
1560
+ private parseInstanceProfiles(xml: string): InstanceProfile[] {
1561
+ const memberXmls = parseXmlArray(xml, 'InstanceProfiles', 'member')
1562
+ return memberXmls.map((memberXml) => ({
1563
+ InstanceProfileName: parseXmlValue(memberXml, 'InstanceProfileName') || '',
1564
+ InstanceProfileId: parseXmlValue(memberXml, 'InstanceProfileId') || '',
1565
+ Arn: parseXmlValue(memberXml, 'Arn') || '',
1566
+ Path: parseXmlValue(memberXml, 'Path'),
1567
+ CreateDate: parseXmlValue(memberXml, 'CreateDate'),
1568
+ }))
1569
+ }
1570
+
1571
+ // ==========================================================================
1572
+ // Account Operations
1573
+ // ==========================================================================
1574
+
1575
+ /**
1576
+ * Get account password policy
1577
+ */
1578
+ async getAccountPasswordPolicy(): Promise<PasswordPolicy> {
1579
+ const response = await this.request('GetAccountPasswordPolicy')
1580
+ return {
1581
+ MinimumPasswordLength: parseXmlValue(response, 'MinimumPasswordLength') ? Number.parseInt(parseXmlValue(response, 'MinimumPasswordLength')!, 10) : undefined,
1582
+ RequireSymbols: parseXmlValue(response, 'RequireSymbols') === 'true',
1583
+ RequireNumbers: parseXmlValue(response, 'RequireNumbers') === 'true',
1584
+ RequireUppercaseCharacters: parseXmlValue(response, 'RequireUppercaseCharacters') === 'true',
1585
+ RequireLowercaseCharacters: parseXmlValue(response, 'RequireLowercaseCharacters') === 'true',
1586
+ AllowUsersToChangePassword: parseXmlValue(response, 'AllowUsersToChangePassword') === 'true',
1587
+ ExpirePasswords: parseXmlValue(response, 'ExpirePasswords') === 'true',
1588
+ MaxPasswordAge: parseXmlValue(response, 'MaxPasswordAge') ? Number.parseInt(parseXmlValue(response, 'MaxPasswordAge')!, 10) : undefined,
1589
+ PasswordReusePrevention: parseXmlValue(response, 'PasswordReusePrevention') ? Number.parseInt(parseXmlValue(response, 'PasswordReusePrevention')!, 10) : undefined,
1590
+ HardExpiry: parseXmlValue(response, 'HardExpiry') === 'true',
1591
+ }
1592
+ }
1593
+
1594
+ /**
1595
+ * Update account password policy
1596
+ */
1597
+ async updateAccountPasswordPolicy(params: UpdateAccountPasswordPolicyParams): Promise<void> {
1598
+ await this.request('UpdateAccountPasswordPolicy', params)
1599
+ }
1600
+
1601
+ /**
1602
+ * Delete account password policy
1603
+ */
1604
+ async deleteAccountPasswordPolicy(): Promise<void> {
1605
+ await this.request('DeleteAccountPasswordPolicy')
1606
+ }
1607
+
1608
+ /**
1609
+ * Get account summary
1610
+ */
1611
+ async getAccountSummary(): Promise<AccountSummary> {
1612
+ const response = await this.request('GetAccountSummary')
1613
+ const summary: AccountSummary = {}
1614
+
1615
+ // Parse the SummaryMap entries
1616
+ const entries = parseXmlArray(response, 'SummaryMap', 'entry')
1617
+ for (const entry of entries) {
1618
+ const key = parseXmlValue(entry, 'key')
1619
+ const value = parseXmlValue(entry, 'value')
1620
+ if (key && value) {
1621
+ (summary as Record<string, number>)[key] = Number.parseInt(value, 10)
1622
+ }
1623
+ }
1624
+
1625
+ return summary
1626
+ }
1627
+
1628
+ /**
1629
+ * Get the account alias
1630
+ */
1631
+ async listAccountAliases(): Promise<{ AccountAliases: string[]; IsTruncated: boolean; Marker?: string }> {
1632
+ const response = await this.request('ListAccountAliases')
1633
+ const aliases = parseXmlArray(response, 'AccountAliases', 'member')
1634
+ const isTruncated = parseXmlValue(response, 'IsTruncated') === 'true'
1635
+ const marker = parseXmlValue(response, 'Marker')
1636
+ return { AccountAliases: aliases, IsTruncated: isTruncated, Marker: marker }
1637
+ }
1638
+
1639
+ /**
1640
+ * Create an account alias
1641
+ */
1642
+ async createAccountAlias(params: { AccountAlias: string }): Promise<void> {
1643
+ await this.request('CreateAccountAlias', params)
1644
+ }
1645
+
1646
+ /**
1647
+ * Delete an account alias
1648
+ */
1649
+ async deleteAccountAlias(params: { AccountAlias: string }): Promise<void> {
1650
+ await this.request('DeleteAccountAlias', params)
1651
+ }
1652
+
1653
+ // ==========================================================================
1654
+ // Policy Simulation Operations
1655
+ // ==========================================================================
1656
+
1657
+ /**
1658
+ * Simulate the effect of policies attached to a principal
1659
+ */
1660
+ async simulatePrincipalPolicy(params: SimulatePrincipalPolicyParams): Promise<SimulatePolicyResponse> {
1661
+ const response = await this.request('SimulatePrincipalPolicy', params)
1662
+ return this.parseSimulationResults(response)
1663
+ }
1664
+
1665
+ /**
1666
+ * Parse simulation results from XML response
1667
+ */
1668
+ private parseSimulationResults(xml: string): SimulatePolicyResponse {
1669
+ const resultXmls = parseXmlArray(xml, 'EvaluationResults', 'member')
1670
+ const evaluationResults: EvaluationResult[] = resultXmls.map((resultXml) => {
1671
+ const matchedStatementXmls = parseXmlArray(resultXml, 'MatchedStatements', 'member')
1672
+ const matchedStatements = matchedStatementXmls.map((stmtXml) => ({
1673
+ SourcePolicyId: parseXmlValue(stmtXml, 'SourcePolicyId'),
1674
+ SourcePolicyType: parseXmlValue(stmtXml, 'SourcePolicyType'),
1675
+ }))
1676
+
1677
+ return {
1678
+ EvalActionName: parseXmlValue(resultXml, 'EvalActionName') || '',
1679
+ EvalResourceName: parseXmlValue(resultXml, 'EvalResourceName'),
1680
+ EvalDecision: parseXmlValue(resultXml, 'EvalDecision') || '',
1681
+ MatchedStatements: matchedStatements.length > 0 ? matchedStatements : undefined,
1682
+ }
1683
+ })
1684
+
1685
+ const isTruncated = parseXmlValue(xml, 'IsTruncated') === 'true'
1686
+ const marker = parseXmlValue(xml, 'Marker')
1687
+ return { EvaluationResults: evaluationResults, IsTruncated: isTruncated, Marker: marker }
1688
+ }
1689
+ }