@mtndev/cli 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/cjs/__chunks/VH7oDXx4.cjs +28 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.cjs +261 -0
- package/cjs/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/cjs/commands/aws/ec2/allow-ip/index.cjs +25 -0
- package/cjs/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.cjs +33 -0
- package/cjs/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.cjs +379 -0
- package/cjs/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/cjs/commands/aws/iam/admin-group.cjs +111 -0
- package/cjs/commands/aws/iam/admin-group.d.ts +55 -0
- package/cjs/commands/aws/iam/break-glass/handler.cjs +127 -0
- package/cjs/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/cjs/commands/aws/iam/break-glass/index.cjs +7 -0
- package/cjs/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/cjs/commands/aws/iam/break-glass/schema.cjs +21 -0
- package/cjs/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/cjs/commands/aws/iam/check-admin/handler.cjs +89 -0
- package/cjs/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/check-admin/index.cjs +7 -0
- package/cjs/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/check-admin/schema.cjs +19 -0
- package/cjs/commands/aws/iam/check-admin/schema.d.ts +19 -0
- package/cjs/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
- package/cjs/commands/aws/iam/policies/sts-federation.json +11 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
- package/cjs/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
- package/cjs/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
- package/cjs/commands/aws/iam/policies/user-admins-write-list.json +117 -0
- package/cjs/commands/aws/iam/remove-admin/handler.cjs +120 -0
- package/cjs/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/remove-admin/index.cjs +7 -0
- package/cjs/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/cjs/commands/aws/iam/remove-admin/schema.cjs +22 -0
- package/cjs/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/cjs/commands/aws/iam/setup-acl/handler.cjs +227 -0
- package/cjs/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/cjs/commands/aws/iam/setup-acl/index.cjs +13 -0
- package/cjs/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/cjs/commands/aws/iam/setup-acl/schema.cjs +19 -0
- package/cjs/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/cjs/commands/aws/iam/setup-acl/utils.cjs +305 -0
- package/cjs/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/cjs/commands/aws/iam/users/handler.cjs +59 -0
- package/cjs/commands/aws/iam/users/handler.d.ts +5 -0
- package/cjs/commands/aws/iam/users/index.cjs +18 -0
- package/cjs/commands/aws/iam/users/index.d.ts +3 -0
- package/cjs/commands/aws/iam/users/schema.cjs +17 -0
- package/cjs/commands/aws/iam/users/schema.d.ts +18 -0
- package/cjs/commands/aws/iam/users/utils.cjs +454 -0
- package/cjs/commands/aws/iam/users/utils.d.ts +119 -0
- package/cjs/commands/aws/index.cjs +223 -0
- package/cjs/commands/aws/index.d.ts +14 -0
- package/cjs/commands/aws/route53/add-record/handler.cjs +264 -0
- package/cjs/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/cjs/commands/aws/route53/add-record/index.cjs +26 -0
- package/cjs/commands/aws/route53/add-record/index.d.ts +3 -0
- package/cjs/commands/aws/route53/add-record/schema.cjs +43 -0
- package/cjs/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/cjs/commands/aws/route53/add-record/utils.cjs +404 -0
- package/cjs/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/cjs/commands/aws/route53/remove-record/handler.cjs +114 -0
- package/cjs/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/cjs/commands/aws/route53/remove-record/index.cjs +5 -0
- package/cjs/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/cjs/commands/aws/route53/remove-record/schema.cjs +22 -0
- package/cjs/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/cjs/commands/aws/schema.cjs +19 -0
- package/cjs/commands/aws/schema.d.ts +13 -0
- package/cjs/commands/aws/ses/send-email/handler.cjs +73 -0
- package/cjs/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/cjs/commands/aws/ses/send-email/index.cjs +9 -0
- package/cjs/commands/aws/ses/send-email/index.d.ts +3 -0
- package/cjs/commands/aws/ses/send-email/schema.cjs +24 -0
- package/cjs/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/cjs/commands/aws/ses/send-email/utils.cjs +87 -0
- package/cjs/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/cjs/commands/aws/ses/smtp-password.cjs +72 -0
- package/cjs/commands/aws/ses/smtp-password.d.ts +21 -0
- package/cjs/commands/aws/utils/exec-aws.cjs +145 -0
- package/cjs/commands/aws/utils/exec-aws.d.ts +79 -0
- package/cjs/commands/aws/utils/resolve-policy-path.cjs +15 -0
- package/cjs/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/cjs/commands/aws/utils/resolve-profile.cjs +93 -0
- package/cjs/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/cjs/commands/aws/utils/tty.cjs +54 -0
- package/cjs/commands/aws/utils/tty.d.ts +24 -0
- package/cjs/commands/db/create-readonly-user/handler.cjs +254 -0
- package/cjs/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/cjs/commands/db/create-readonly-user/index.cjs +18 -0
- package/cjs/commands/db/create-readonly-user/index.d.ts +3 -0
- package/cjs/commands/db/create-readonly-user/mysql.cjs +102 -0
- package/cjs/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/cjs/commands/db/create-readonly-user/postgres.cjs +209 -0
- package/cjs/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/cjs/commands/db/create-readonly-user/schema.cjs +39 -0
- package/cjs/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/cjs/commands/db/create-readonly-user/utils.cjs +160 -0
- package/cjs/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/cjs/commands/db/index.cjs +46 -0
- package/cjs/commands/db/index.d.ts +6 -0
- package/cjs/commands/doppler/handler.cjs +95 -9
- package/cjs/commands/doppler/index.cjs +33 -41
- package/cjs/commands/doppler/index.d.ts +1 -0
- package/cjs/commands/doppler/schema.cjs +130 -6
- package/cjs/commands/doppler/schema.d.ts +233 -17
- package/cjs/commands/doppler/set-secret.cjs +378 -0
- package/cjs/commands/doppler/set-secret.d.ts +113 -0
- package/cjs/commands/doppler/utils/exec.cjs +106 -12
- package/cjs/commands/doppler/utils.cjs +412 -30
- package/cjs/commands/doppler/utils.d.ts +32 -8
- package/cjs/commands/icons/handler.cjs +82 -8
- package/cjs/commands/icons/index.cjs +15 -31
- package/cjs/commands/icons/schema.cjs +101 -13
- package/cjs/commands/icons/schema.d.ts +1 -1
- package/cjs/commands/icons/utils.cjs +131 -13
- package/cjs/commands/index.cjs +54 -82
- package/cjs/commands/schema.cjs +25 -8
- package/cjs/index.cjs +3 -4
- package/esm/commands/aws/ec2/allow-ip/handler.d.ts +6 -0
- package/esm/commands/aws/ec2/allow-ip/handler.js +258 -0
- package/esm/commands/aws/ec2/allow-ip/index.d.ts +3 -0
- package/esm/commands/aws/ec2/allow-ip/index.js +4 -0
- package/esm/commands/aws/ec2/allow-ip/schema.d.ts +33 -0
- package/esm/commands/aws/ec2/allow-ip/schema.js +29 -0
- package/esm/commands/aws/ec2/allow-ip/utils.d.ts +189 -0
- package/esm/commands/aws/ec2/allow-ip/utils.js +363 -0
- package/esm/commands/aws/iam/admin-group.d.ts +55 -0
- package/esm/commands/aws/iam/admin-group.js +104 -0
- package/esm/commands/aws/iam/break-glass/handler.d.ts +7 -0
- package/esm/commands/aws/iam/break-glass/handler.js +124 -0
- package/esm/commands/aws/iam/break-glass/index.d.ts +2 -0
- package/esm/commands/aws/iam/break-glass/index.js +4 -0
- package/esm/commands/aws/iam/break-glass/schema.d.ts +25 -0
- package/esm/commands/aws/iam/break-glass/schema.js +19 -0
- package/esm/commands/aws/iam/check-admin/handler.d.ts +5 -0
- package/esm/commands/aws/iam/check-admin/handler.js +86 -0
- package/esm/commands/aws/iam/check-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/check-admin/index.js +4 -0
- package/esm/commands/aws/iam/check-admin/schema.d.ts +19 -0
- package/esm/commands/aws/iam/check-admin/schema.js +17 -0
- package/esm/commands/aws/iam/policies/s3-restrictions-deny-non-s3-services.json +20 -0
- package/esm/commands/aws/iam/policies/sts-federation.json +11 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-list.json +175 -0
- package/esm/commands/aws/iam/policies/user-admins-deny-regions-list.json +41 -0
- package/esm/commands/aws/iam/policies/user-admins-self-service-iam.json +56 -0
- package/esm/commands/aws/iam/policies/user-admins-write-list.json +117 -0
- package/esm/commands/aws/iam/remove-admin/handler.d.ts +6 -0
- package/esm/commands/aws/iam/remove-admin/handler.js +117 -0
- package/esm/commands/aws/iam/remove-admin/index.d.ts +2 -0
- package/esm/commands/aws/iam/remove-admin/index.js +4 -0
- package/esm/commands/aws/iam/remove-admin/schema.d.ts +28 -0
- package/esm/commands/aws/iam/remove-admin/schema.js +20 -0
- package/esm/commands/aws/iam/setup-acl/handler.d.ts +6 -0
- package/esm/commands/aws/iam/setup-acl/handler.js +224 -0
- package/esm/commands/aws/iam/setup-acl/index.d.ts +3 -0
- package/esm/commands/aws/iam/setup-acl/index.js +4 -0
- package/esm/commands/aws/iam/setup-acl/schema.d.ts +24 -0
- package/esm/commands/aws/iam/setup-acl/schema.js +18 -0
- package/esm/commands/aws/iam/setup-acl/utils.d.ts +61 -0
- package/esm/commands/aws/iam/setup-acl/utils.js +298 -0
- package/esm/commands/aws/iam/users/handler.d.ts +5 -0
- package/esm/commands/aws/iam/users/handler.js +56 -0
- package/esm/commands/aws/iam/users/index.d.ts +3 -0
- package/esm/commands/aws/iam/users/index.js +4 -0
- package/esm/commands/aws/iam/users/schema.d.ts +18 -0
- package/esm/commands/aws/iam/users/schema.js +16 -0
- package/esm/commands/aws/iam/users/utils.d.ts +119 -0
- package/esm/commands/aws/iam/users/utils.js +440 -0
- package/esm/commands/aws/index.d.ts +14 -0
- package/esm/commands/aws/index.js +142 -0
- package/esm/commands/aws/route53/add-record/handler.d.ts +36 -0
- package/esm/commands/aws/route53/add-record/handler.js +258 -0
- package/esm/commands/aws/route53/add-record/index.d.ts +3 -0
- package/esm/commands/aws/route53/add-record/index.js +4 -0
- package/esm/commands/aws/route53/add-record/schema.d.ts +45 -0
- package/esm/commands/aws/route53/add-record/schema.js +40 -0
- package/esm/commands/aws/route53/add-record/utils.d.ts +216 -0
- package/esm/commands/aws/route53/add-record/utils.js +389 -0
- package/esm/commands/aws/route53/remove-record/handler.d.ts +6 -0
- package/esm/commands/aws/route53/remove-record/handler.js +111 -0
- package/esm/commands/aws/route53/remove-record/index.d.ts +2 -0
- package/esm/commands/aws/route53/remove-record/index.js +3 -0
- package/esm/commands/aws/route53/remove-record/schema.d.ts +30 -0
- package/esm/commands/aws/route53/remove-record/schema.js +21 -0
- package/esm/commands/aws/schema.d.ts +13 -0
- package/esm/commands/aws/schema.js +16 -0
- package/esm/commands/aws/ses/send-email/handler.d.ts +5 -0
- package/esm/commands/aws/ses/send-email/handler.js +70 -0
- package/esm/commands/aws/ses/send-email/index.d.ts +3 -0
- package/esm/commands/aws/ses/send-email/index.js +4 -0
- package/esm/commands/aws/ses/send-email/schema.d.ts +39 -0
- package/esm/commands/aws/ses/send-email/schema.js +23 -0
- package/esm/commands/aws/ses/send-email/utils.d.ts +47 -0
- package/esm/commands/aws/ses/send-email/utils.js +84 -0
- package/esm/commands/aws/ses/smtp-password.d.ts +21 -0
- package/esm/commands/aws/ses/smtp-password.js +69 -0
- package/esm/commands/aws/utils/exec-aws.d.ts +79 -0
- package/esm/commands/aws/utils/exec-aws.js +137 -0
- package/esm/commands/aws/utils/resolve-policy-path.d.ts +8 -0
- package/esm/commands/aws/utils/resolve-policy-path.js +14 -0
- package/esm/commands/aws/utils/resolve-profile.d.ts +28 -0
- package/esm/commands/aws/utils/resolve-profile.js +88 -0
- package/esm/commands/aws/utils/tty.d.ts +24 -0
- package/esm/commands/aws/utils/tty.js +51 -0
- package/esm/commands/db/create-readonly-user/handler.d.ts +5 -0
- package/esm/commands/db/create-readonly-user/handler.js +251 -0
- package/esm/commands/db/create-readonly-user/index.d.ts +3 -0
- package/esm/commands/db/create-readonly-user/index.js +4 -0
- package/esm/commands/db/create-readonly-user/mysql.d.ts +33 -0
- package/esm/commands/db/create-readonly-user/mysql.js +98 -0
- package/esm/commands/db/create-readonly-user/postgres.d.ts +31 -0
- package/esm/commands/db/create-readonly-user/postgres.js +205 -0
- package/esm/commands/db/create-readonly-user/schema.d.ts +55 -0
- package/esm/commands/db/create-readonly-user/schema.js +35 -0
- package/esm/commands/db/create-readonly-user/utils.d.ts +112 -0
- package/esm/commands/db/create-readonly-user/utils.js +151 -0
- package/esm/commands/db/index.d.ts +6 -0
- package/esm/commands/db/index.js +31 -0
- package/esm/commands/doppler/handler.js +92 -9
- package/esm/commands/doppler/index.d.ts +1 -0
- package/esm/commands/doppler/index.js +6 -41
- package/esm/commands/doppler/schema.d.ts +233 -17
- package/esm/commands/doppler/schema.js +125 -7
- package/esm/commands/doppler/set-secret.d.ts +113 -0
- package/esm/commands/doppler/set-secret.js +370 -0
- package/esm/commands/doppler/utils/exec.js +100 -12
- package/esm/commands/doppler/utils.d.ts +32 -8
- package/esm/commands/doppler/utils.js +396 -30
- package/esm/commands/icons/handler.js +79 -8
- package/esm/commands/icons/index.js +4 -31
- package/esm/commands/icons/schema.d.ts +1 -1
- package/esm/commands/icons/schema.js +97 -15
- package/esm/commands/icons/utils.js +125 -13
- package/esm/commands/index.js +49 -78
- package/esm/commands/schema.js +22 -7
- package/esm/index.js +3 -4
- package/package.json +12 -2
- package/vitest.config.ts +11 -0
- package/cjs/__chunks/4GFJUCSP.cjs +0 -52
- package/cjs/__chunks/4SFWHCB3.cjs +0 -133
- package/cjs/__chunks/B2KFW7T7.cjs +0 -1
- package/cjs/__chunks/EMMC33QA.cjs +0 -401
- package/cjs/__chunks/INFOPAHN.cjs +0 -117
- package/cjs/__chunks/J36P6UNE.cjs +0 -75
- package/cjs/__chunks/NIMXXHJQ.cjs +0 -92
- package/cjs/__chunks/NSRFG4DF.cjs +0 -100
- package/cjs/__chunks/WNJ3ZYLI.cjs +0 -1
- package/cjs/__chunks/ZMDIOZPT.cjs +0 -22
- package/esm/__chunks/46HGXFME.js +0 -0
- package/esm/__chunks/6G4PXXKF.js +0 -75
- package/esm/__chunks/6L5RNR7G.js +0 -100
- package/esm/__chunks/FL7AILGH.js +0 -133
- package/esm/__chunks/HJSXC6HP.js +0 -117
- package/esm/__chunks/O262GK4B.js +0 -401
- package/esm/__chunks/O3CCA76Q.js +0 -22
- package/esm/__chunks/SJXQJ7RS.js +0 -92
- package/esm/__chunks/SNCKIAOR.js +0 -0
- package/esm/__chunks/X3R5PSWH.js +0 -52
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import { prompt, promptYesNo } from "../../utils/tty.js";
|
|
2
|
+
import { runAws } from "../../utils/exec-aws.js";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
//#region src/commands/aws/iam/users/utils.js
|
|
5
|
+
const KNOWN_ROLES = [
|
|
6
|
+
"read-only",
|
|
7
|
+
"iam-admin",
|
|
8
|
+
"power-user"
|
|
9
|
+
];
|
|
10
|
+
const REQUIRED_ACL_GROUPS = [
|
|
11
|
+
"acl-read-only",
|
|
12
|
+
"acl-iam-full-access",
|
|
13
|
+
"acl-power-users"
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Map a user role to its ACL group.
|
|
17
|
+
* @param {string} role
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function roleToGroup(role) {
|
|
21
|
+
switch (role) {
|
|
22
|
+
case "read-only": return "acl-read-only";
|
|
23
|
+
case "iam-admin": return "acl-iam-full-access";
|
|
24
|
+
case "power-user": return "acl-power-users";
|
|
25
|
+
default: throw new Error(`Unknown role: ${role}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Lowercase and hyphenate a raw value into a slug.
|
|
30
|
+
* @param {string} value
|
|
31
|
+
* @returns {string}
|
|
32
|
+
*/
|
|
33
|
+
function normalizeSlug(value) {
|
|
34
|
+
return value.toLowerCase().replace(/ /g, "-");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} value
|
|
38
|
+
* @returns {boolean}
|
|
39
|
+
*/
|
|
40
|
+
function isValidSlug(value) {
|
|
41
|
+
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(value);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Build the canonical IAM username.
|
|
45
|
+
* @param {{ identifier: string; accountName: string; role: string }} params
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
function buildUsername(params) {
|
|
49
|
+
const { identifier, accountName, role } = params;
|
|
50
|
+
return `md-${identifier}-${accountName}-${role}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Strip md- prefix, role suffix, and account suffix from a raw identifier.
|
|
54
|
+
* @param {{ raw: string; accountName: string; role: string }} params
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
function normalizeIdentifier(params) {
|
|
58
|
+
const { raw, accountName, role } = params;
|
|
59
|
+
let identifier = normalizeSlug(raw);
|
|
60
|
+
if (identifier.startsWith("md-")) identifier = identifier.slice(3);
|
|
61
|
+
if (identifier.endsWith(`-${role}`)) identifier = identifier.slice(0, -(role.length + 1));
|
|
62
|
+
else for (const knownRole of KNOWN_ROLES) if (identifier.endsWith(`-${knownRole}`)) {
|
|
63
|
+
identifier = identifier.slice(0, -(knownRole.length + 1));
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
if (identifier.endsWith(`-${accountName}`)) identifier = identifier.slice(0, -(accountName.length + 1));
|
|
67
|
+
return identifier;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse an md-{identifier}-{account}-{role} username into parts.
|
|
71
|
+
* Identifier is the first segment; account is everything between it and the role
|
|
72
|
+
* (so multi-segment account slugs still work).
|
|
73
|
+
* @param {string} name - Profile name or IAM user name
|
|
74
|
+
* @returns {{ identifier: string; accountName: string; role: string } | null}
|
|
75
|
+
*/
|
|
76
|
+
function parseMdUsername(name) {
|
|
77
|
+
let value = normalizeSlug(name);
|
|
78
|
+
if (!value.startsWith("md-")) return null;
|
|
79
|
+
value = value.slice(3);
|
|
80
|
+
const rolesByLength = [...KNOWN_ROLES].sort((a, b) => b.length - a.length);
|
|
81
|
+
/** @type {string | null} */
|
|
82
|
+
let role = null;
|
|
83
|
+
for (const knownRole of rolesByLength) if (value.endsWith(`-${knownRole}`)) {
|
|
84
|
+
role = knownRole;
|
|
85
|
+
value = value.slice(0, -(knownRole.length + 1));
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
if (!role) return null;
|
|
89
|
+
const dash = value.indexOf("-");
|
|
90
|
+
if (dash <= 0 || dash === value.length - 1) return null;
|
|
91
|
+
const identifier = value.slice(0, dash);
|
|
92
|
+
const accountName = value.slice(dash + 1);
|
|
93
|
+
if (!isValidSlug(identifier) || !isValidSlug(accountName)) return null;
|
|
94
|
+
return {
|
|
95
|
+
identifier,
|
|
96
|
+
accountName,
|
|
97
|
+
role
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Infer the AWS account slug from the caller IAM user / profile name.
|
|
102
|
+
* Prefers the ARN username, then the profile string.
|
|
103
|
+
* @param {{
|
|
104
|
+
* profile?: string;
|
|
105
|
+
* identity?: { Arn?: string };
|
|
106
|
+
* }} params
|
|
107
|
+
* @returns {string | null}
|
|
108
|
+
*/
|
|
109
|
+
function inferAccountName(params) {
|
|
110
|
+
const { profile, identity } = params;
|
|
111
|
+
const arn = identity?.Arn ?? "";
|
|
112
|
+
const username = arn.includes("/") ? arn.split("/").pop() ?? "" : "";
|
|
113
|
+
for (const candidate of [username, profile ?? ""]) {
|
|
114
|
+
if (!candidate) continue;
|
|
115
|
+
const parsed = parseMdUsername(candidate);
|
|
116
|
+
if (parsed) return parsed.accountName;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @param {{ username: string; profile?: string; verbose?: boolean }} params
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
function userExists(params) {
|
|
125
|
+
const { username, profile, verbose } = params;
|
|
126
|
+
try {
|
|
127
|
+
runAws({
|
|
128
|
+
profile,
|
|
129
|
+
args: [
|
|
130
|
+
"iam",
|
|
131
|
+
"get-user",
|
|
132
|
+
"--user-name",
|
|
133
|
+
username
|
|
134
|
+
],
|
|
135
|
+
verbose
|
|
136
|
+
});
|
|
137
|
+
return true;
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create the IAM user if it does not exist.
|
|
144
|
+
* @param {{ username: string; profile?: string; verbose?: boolean }} params
|
|
145
|
+
*/
|
|
146
|
+
function ensureUser(params) {
|
|
147
|
+
const { username, profile, verbose } = params;
|
|
148
|
+
if (userExists({
|
|
149
|
+
username,
|
|
150
|
+
profile,
|
|
151
|
+
verbose
|
|
152
|
+
})) {
|
|
153
|
+
console.log(` User exists: ${username}`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(` Create user: ${username}`);
|
|
157
|
+
runAws({
|
|
158
|
+
profile,
|
|
159
|
+
args: [
|
|
160
|
+
"iam",
|
|
161
|
+
"create-user",
|
|
162
|
+
"--user-name",
|
|
163
|
+
username,
|
|
164
|
+
"--path",
|
|
165
|
+
"/"
|
|
166
|
+
],
|
|
167
|
+
verbose
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Add the user to the group if not already a member.
|
|
172
|
+
* @param {{ username: string; group: string; profile?: string; verbose?: boolean }} params
|
|
173
|
+
*/
|
|
174
|
+
function ensureUserInGroup(params) {
|
|
175
|
+
const { username, group, profile, verbose } = params;
|
|
176
|
+
if (runAws({
|
|
177
|
+
profile,
|
|
178
|
+
args: [
|
|
179
|
+
"iam",
|
|
180
|
+
"get-group",
|
|
181
|
+
"--group-name",
|
|
182
|
+
group,
|
|
183
|
+
"--query",
|
|
184
|
+
`Users[?UserName=='${username}'].UserName`,
|
|
185
|
+
"--output",
|
|
186
|
+
"text"
|
|
187
|
+
],
|
|
188
|
+
expectJson: false,
|
|
189
|
+
verbose
|
|
190
|
+
})) {
|
|
191
|
+
console.log(` Already in group: ${group}`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
console.log(` Add to group: ${group}`);
|
|
195
|
+
runAws({
|
|
196
|
+
profile,
|
|
197
|
+
args: [
|
|
198
|
+
"iam",
|
|
199
|
+
"add-user-to-group",
|
|
200
|
+
"--user-name",
|
|
201
|
+
username,
|
|
202
|
+
"--group-name",
|
|
203
|
+
group
|
|
204
|
+
],
|
|
205
|
+
expectJson: false,
|
|
206
|
+
verbose
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create a CLI access key for the user and print a one-line setup command.
|
|
211
|
+
* @param {{ username: string; profile?: string; verbose?: boolean }} params
|
|
212
|
+
* @returns {boolean} False when the user already has the maximum number of keys
|
|
213
|
+
*/
|
|
214
|
+
function createCliAccessKey(params) {
|
|
215
|
+
const { username, profile, verbose } = params;
|
|
216
|
+
const keyCountRaw = runAws({
|
|
217
|
+
profile,
|
|
218
|
+
args: [
|
|
219
|
+
"iam",
|
|
220
|
+
"list-access-keys",
|
|
221
|
+
"--user-name",
|
|
222
|
+
username,
|
|
223
|
+
"--query",
|
|
224
|
+
"length(AccessKeyMetadata)",
|
|
225
|
+
"--output",
|
|
226
|
+
"text"
|
|
227
|
+
],
|
|
228
|
+
expectJson: false,
|
|
229
|
+
verbose
|
|
230
|
+
});
|
|
231
|
+
if ((Number.parseInt(keyCountRaw, 10) || 0) >= 2) {
|
|
232
|
+
console.log(chalk.yellow(` WARNING: ${username} already has 2 access keys (AWS max).`));
|
|
233
|
+
console.log(chalk.yellow(" Delete an existing key before creating a new one."));
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const [accessKeyId, secretAccessKey] = runAws({
|
|
237
|
+
profile,
|
|
238
|
+
args: [
|
|
239
|
+
"iam",
|
|
240
|
+
"create-access-key",
|
|
241
|
+
"--user-name",
|
|
242
|
+
username,
|
|
243
|
+
"--query",
|
|
244
|
+
"AccessKey.[AccessKeyId,SecretAccessKey]",
|
|
245
|
+
"--output",
|
|
246
|
+
"text"
|
|
247
|
+
],
|
|
248
|
+
expectJson: false,
|
|
249
|
+
verbose
|
|
250
|
+
}).split(/\s+/);
|
|
251
|
+
if (!accessKeyId || !secretAccessKey) throw new Error("Failed to parse the newly created access key");
|
|
252
|
+
const setupCmd = ` export AWS_ACCESS_KEY_ID='${accessKeyId}' AWS_SECRET_ACCESS_KEY='${secretAccessKey}' && aws-vault add ${username} --env --add-config && aws configure set aws_access_key_id "$AWS_ACCESS_KEY_ID" --profile ${username} && aws configure set aws_secret_access_key "$AWS_SECRET_ACCESS_KEY" --profile ${username} && aws configure set region us-east-1 --profile ${username}`;
|
|
253
|
+
console.log("");
|
|
254
|
+
console.log(` CLI credentials for ${username} (secret shown once — copy now):`);
|
|
255
|
+
console.log(" ────────────────────────────────────────────────────────────────");
|
|
256
|
+
console.log(" YOU MUST run the command below with a LEADING SPACE so it is not saved");
|
|
257
|
+
console.log(" to shell history — same idea as: TEST=\"a\" && echo $TEST");
|
|
258
|
+
console.log("");
|
|
259
|
+
console.log(setupCmd);
|
|
260
|
+
console.log(" ────────────────────────────────────────────────────────────────");
|
|
261
|
+
console.log("");
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Verify the required ACL groups exist before creating users.
|
|
266
|
+
* @param {{ profile?: string; verbose?: boolean }} params
|
|
267
|
+
* @returns {boolean}
|
|
268
|
+
*/
|
|
269
|
+
function ensureAclGroupsExist(params) {
|
|
270
|
+
const { profile, verbose } = params;
|
|
271
|
+
const missing = REQUIRED_ACL_GROUPS.filter((group) => {
|
|
272
|
+
try {
|
|
273
|
+
runAws({
|
|
274
|
+
profile,
|
|
275
|
+
args: [
|
|
276
|
+
"iam",
|
|
277
|
+
"get-group",
|
|
278
|
+
"--group-name",
|
|
279
|
+
group
|
|
280
|
+
],
|
|
281
|
+
verbose
|
|
282
|
+
});
|
|
283
|
+
return false;
|
|
284
|
+
} catch {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
if (missing.length > 0) {
|
|
289
|
+
missing.forEach((group) => console.log(` Missing group: ${group}`));
|
|
290
|
+
console.log("");
|
|
291
|
+
console.log(" Run `mtn aws iam setup-acl` first to create ACL groups and policies.");
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* @param {{ accountName?: string; inferredAccountName?: string | null }} params
|
|
298
|
+
* @returns {Promise<string>}
|
|
299
|
+
*/
|
|
300
|
+
async function promptAccountName(params) {
|
|
301
|
+
const { accountName, inferredAccountName } = params;
|
|
302
|
+
if (accountName) {
|
|
303
|
+
const slug = normalizeSlug(accountName);
|
|
304
|
+
if (isValidSlug(slug)) {
|
|
305
|
+
console.log(` Account name: ${slug}`);
|
|
306
|
+
return slug;
|
|
307
|
+
}
|
|
308
|
+
console.log(" Invalid account name option — falling back to prompt.");
|
|
309
|
+
}
|
|
310
|
+
const defaultSlug = inferredAccountName && isValidSlug(normalizeSlug(inferredAccountName)) ? normalizeSlug(inferredAccountName) : null;
|
|
311
|
+
for (;;) {
|
|
312
|
+
const hint = defaultSlug ? ` [default: ${defaultSlug}]` : " (e.g. vincent)";
|
|
313
|
+
const raw = await prompt(` AWS account name${hint} — used in md-{identifier}-{account}-{role}: `);
|
|
314
|
+
if (raw === "" && defaultSlug) {
|
|
315
|
+
console.log(` → account name: ${defaultSlug}`);
|
|
316
|
+
return defaultSlug;
|
|
317
|
+
}
|
|
318
|
+
const slug = normalizeSlug(raw);
|
|
319
|
+
if (isValidSlug(slug)) {
|
|
320
|
+
if (raw !== slug) console.log(` → account name: ${slug}`);
|
|
321
|
+
return slug;
|
|
322
|
+
}
|
|
323
|
+
console.log(" Invalid account name — use lowercase letters, numbers, and hyphens" + (defaultSlug ? ", or press Enter to accept the default." : "."));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* @returns {Promise<string>}
|
|
328
|
+
*/
|
|
329
|
+
async function promptRole() {
|
|
330
|
+
for (;;) {
|
|
331
|
+
console.log(" Role (username will be md-{identifier}-{account}-{role}):");
|
|
332
|
+
console.log(" 1) read-only → acl-read-only");
|
|
333
|
+
console.log(" 2) iam-admin → acl-iam-full-access");
|
|
334
|
+
console.log(" 3) power-user → acl-power-users");
|
|
335
|
+
switch (await prompt(" Choice [1-3]: ")) {
|
|
336
|
+
case "1":
|
|
337
|
+
case "read-only": return "read-only";
|
|
338
|
+
case "2":
|
|
339
|
+
case "iam-admin": return "iam-admin";
|
|
340
|
+
case "3":
|
|
341
|
+
case "power-user": return "power-user";
|
|
342
|
+
default: console.log(" Invalid choice — enter 1, 2, 3, or the role name.");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* @param {{ accountName: string; role: string }} params
|
|
348
|
+
* @returns {Promise<string>}
|
|
349
|
+
*/
|
|
350
|
+
async function promptIdentifier(params) {
|
|
351
|
+
const { accountName, role } = params;
|
|
352
|
+
const exampleUser = buildUsername({
|
|
353
|
+
identifier: "declan",
|
|
354
|
+
accountName,
|
|
355
|
+
role
|
|
356
|
+
});
|
|
357
|
+
for (;;) {
|
|
358
|
+
const raw = await prompt(` Identifier (e.g. declan, or full ${exampleUser}): `);
|
|
359
|
+
const lowerRaw = normalizeSlug(raw);
|
|
360
|
+
const identifier = normalizeIdentifier({
|
|
361
|
+
raw,
|
|
362
|
+
accountName,
|
|
363
|
+
role
|
|
364
|
+
});
|
|
365
|
+
if (isValidSlug(identifier)) {
|
|
366
|
+
if (lowerRaw !== identifier) console.log(` → parsed identifier: ${identifier}`);
|
|
367
|
+
return identifier;
|
|
368
|
+
}
|
|
369
|
+
console.log(" Invalid identifier — use lowercase letters, numbers, and hyphens.");
|
|
370
|
+
console.log(" Tip: enter just the short name (e.g. declan).");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Interactively create IAM users, assign ACL groups, and print CLI credentials.
|
|
375
|
+
* @param {{
|
|
376
|
+
* accountName?: string;
|
|
377
|
+
* profile?: string;
|
|
378
|
+
* identity?: { Arn?: string };
|
|
379
|
+
* verbose?: boolean;
|
|
380
|
+
* }} params
|
|
381
|
+
*/
|
|
382
|
+
async function createUsersInteractive(params) {
|
|
383
|
+
const { accountName: accountNameOption, profile, identity, verbose } = params;
|
|
384
|
+
if (!ensureAclGroupsExist({
|
|
385
|
+
profile,
|
|
386
|
+
verbose
|
|
387
|
+
})) return;
|
|
388
|
+
console.log("");
|
|
389
|
+
const accountName = await promptAccountName({
|
|
390
|
+
accountName: accountNameOption,
|
|
391
|
+
inferredAccountName: inferAccountName({
|
|
392
|
+
profile,
|
|
393
|
+
identity
|
|
394
|
+
})
|
|
395
|
+
});
|
|
396
|
+
console.log("");
|
|
397
|
+
while (await promptYesNo({
|
|
398
|
+
message: " Create another user?",
|
|
399
|
+
defaultYes: false
|
|
400
|
+
})) {
|
|
401
|
+
console.log("");
|
|
402
|
+
const role = await promptRole();
|
|
403
|
+
const username = buildUsername({
|
|
404
|
+
identifier: await promptIdentifier({
|
|
405
|
+
accountName,
|
|
406
|
+
role
|
|
407
|
+
}),
|
|
408
|
+
accountName,
|
|
409
|
+
role
|
|
410
|
+
});
|
|
411
|
+
const group = roleToGroup(role);
|
|
412
|
+
console.log("");
|
|
413
|
+
console.log(` → user: ${username}`);
|
|
414
|
+
console.log(` → group: ${group}`);
|
|
415
|
+
console.log("");
|
|
416
|
+
ensureUser({
|
|
417
|
+
username,
|
|
418
|
+
profile,
|
|
419
|
+
verbose
|
|
420
|
+
});
|
|
421
|
+
ensureUserInGroup({
|
|
422
|
+
username,
|
|
423
|
+
group,
|
|
424
|
+
profile,
|
|
425
|
+
verbose
|
|
426
|
+
});
|
|
427
|
+
try {
|
|
428
|
+
createCliAccessKey({
|
|
429
|
+
username,
|
|
430
|
+
profile,
|
|
431
|
+
verbose
|
|
432
|
+
});
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.log(chalk.yellow(` Could not create access key: ${error instanceof Error ? error.message : String(error)}`));
|
|
435
|
+
}
|
|
436
|
+
console.log("");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
//#endregion
|
|
440
|
+
export { buildUsername, createCliAccessKey, createUsersInteractive, ensureAclGroupsExist, ensureUser, ensureUserInGroup, inferAccountName, isValidSlug, normalizeIdentifier, normalizeSlug, parseMdUsername, roleToGroup };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register the `aws` command group and its subcommands on the root program.
|
|
3
|
+
* @param {import('commander').Command} program
|
|
4
|
+
*/
|
|
5
|
+
export function registerAwsCommands(program: import("commander").Command): void;
|
|
6
|
+
export * from "./ec2/allow-ip/index.js";
|
|
7
|
+
export * from "./iam/break-glass/index.js";
|
|
8
|
+
export * from "./iam/check-admin/index.js";
|
|
9
|
+
export * from "./iam/remove-admin/index.js";
|
|
10
|
+
export * from "./iam/setup-acl/index.js";
|
|
11
|
+
export * from "./iam/users/index.js";
|
|
12
|
+
export * from "./route53/add-record/index.js";
|
|
13
|
+
export * from "./route53/remove-record/index.js";
|
|
14
|
+
export * from "./ses/send-email/index.js";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { sendEmailOptionsSchema } from "./ses/send-email/schema.js";
|
|
2
|
+
import { defaultBody, sendViaApi, sendViaSmtp } from "./ses/send-email/utils.js";
|
|
3
|
+
import { sendEmailCommand } from "./ses/send-email/handler.js";
|
|
4
|
+
import "./ses/send-email/index.js";
|
|
5
|
+
import { DEFAULT_TTL, RECORD_TYPES, addRecordOptionsSchema } from "./route53/add-record/schema.js";
|
|
6
|
+
import { bareHostedZoneId, deleteResourceRecordSet, findCnameConflicts, findHostedZone, formatValues, getRecordSetsAtName, getResourceRecordSet, hostedZoneLabel, listHostedZones, normalizeRecordValue, planRecordChange, resolveRecordName, upsertResourceRecordSet, withTrailingDot, withoutTrailingDot } from "./route53/add-record/utils.js";
|
|
7
|
+
import { addRecordCommand, resolveHostedZone, resolveName, resolveRecordType } from "./route53/add-record/handler.js";
|
|
8
|
+
import { removeRecordOptionsSchema } from "./route53/remove-record/schema.js";
|
|
9
|
+
import { removeRecordCommand } from "./route53/remove-record/handler.js";
|
|
10
|
+
import "./route53/remove-record/index.js";
|
|
11
|
+
import "./route53/add-record/index.js";
|
|
12
|
+
import { usersOptionsSchema } from "./iam/users/schema.js";
|
|
13
|
+
import { buildUsername, createCliAccessKey, createUsersInteractive, ensureAclGroupsExist, ensureUser, ensureUserInGroup, inferAccountName, isValidSlug, normalizeIdentifier, normalizeSlug, parseMdUsername, roleToGroup } from "./iam/users/utils.js";
|
|
14
|
+
import { usersCommand } from "./iam/users/handler.js";
|
|
15
|
+
import "./iam/users/index.js";
|
|
16
|
+
import { setupAclOptionsSchema } from "./iam/setup-acl/schema.js";
|
|
17
|
+
import { ACL_GROUPS, CUSTOM_POLICIES, attach, detach, ensureGroup, printFinalState, publishPolicy } from "./iam/setup-acl/utils.js";
|
|
18
|
+
import { setupAclCommand } from "./iam/setup-acl/handler.js";
|
|
19
|
+
import "./iam/setup-acl/index.js";
|
|
20
|
+
import { ADMIN_GROUP_NAME } from "./iam/admin-group.js";
|
|
21
|
+
import { removeAdminOptionsSchema } from "./iam/remove-admin/schema.js";
|
|
22
|
+
import { removeAdminCommand } from "./iam/remove-admin/handler.js";
|
|
23
|
+
import "./iam/remove-admin/index.js";
|
|
24
|
+
import { checkAdminOptionsSchema } from "./iam/check-admin/schema.js";
|
|
25
|
+
import { checkAdminCommand } from "./iam/check-admin/handler.js";
|
|
26
|
+
import "./iam/check-admin/index.js";
|
|
27
|
+
import { breakGlassOptionsSchema } from "./iam/break-glass/schema.js";
|
|
28
|
+
import { breakGlassCommand } from "./iam/break-glass/handler.js";
|
|
29
|
+
import "./iam/break-glass/index.js";
|
|
30
|
+
import { DEFAULT_REGION, DEFAULT_ROAMING_NAME, SSH_PORT, allowIpOptionsSchema } from "./ec2/allow-ip/schema.js";
|
|
31
|
+
import { authorizeRule, describeSecurityGroups, extractRoamingFirstName, getCurrentPublicIp, getSecurityGroupDetails, instanceLabel, listEnabledRegions, listInstances, listSshIngressRules, printSshRules, resolveRoamingRule, revokeRule, roamingRuleDescription, ruleAlreadyAllowsIp, securityGroupLabel, sshRuleLabel } from "./ec2/allow-ip/utils.js";
|
|
32
|
+
import { allowIpCommand } from "./ec2/allow-ip/handler.js";
|
|
33
|
+
import "./ec2/allow-ip/index.js";
|
|
34
|
+
//#region src/commands/aws/index.js
|
|
35
|
+
/**
|
|
36
|
+
* Commander collector for repeatable options.
|
|
37
|
+
* @param {string} value
|
|
38
|
+
* @param {string[]} previous
|
|
39
|
+
* @returns {string[]}
|
|
40
|
+
*/
|
|
41
|
+
function collect(value, previous) {
|
|
42
|
+
return [...previous, value];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Register the `aws` command group and its subcommands on the root program.
|
|
46
|
+
* @param {import('commander').Command} program
|
|
47
|
+
*/
|
|
48
|
+
function registerAwsCommands(program) {
|
|
49
|
+
const aws = program.command("aws").summary("AWS operational commands (IAM, EC2, SES, Route53)").description("AWS operational commands for IAM, EC2, SES, and Route53");
|
|
50
|
+
const iam = aws.command("iam").summary("IAM ACL group and user management").description("IAM ACL group and user management");
|
|
51
|
+
iam.command("setup-acl").summary("Create ACL groups, policies, and (optionally) users").description("Idempotently create the ACL groups and managed policies, attach them per group, and optionally create IAM users.").option("--restrict-regions <yes|no>", "Attach the region restriction to acl-power-users (skips the prompt)").option("--no-create-users", "Skip IAM user creation after configuring groups").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("-r, --region <region>", "AWS region").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
52
|
+
await setupAclCommand({
|
|
53
|
+
restrictRegions: options.restrictRegions,
|
|
54
|
+
createUsers: options.createUsers,
|
|
55
|
+
profile: options.profile,
|
|
56
|
+
region: options.region,
|
|
57
|
+
verbose: options.verbose ?? false
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
iam.command("users").summary("Interactively create IAM users and assign ACL groups").description("Interactively create md-{identifier}-{account}-{role} IAM users, assign ACL groups, and print CLI setup commands.").option("--account-name <slug>", "AWS account slug used in md-{identifier}-{account}-{role}").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
61
|
+
await usersCommand({
|
|
62
|
+
accountName: options.accountName,
|
|
63
|
+
profile: options.profile,
|
|
64
|
+
verbose: options.verbose ?? false
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
iam.command("check-admin").summary("Alert if anyone is in acl-admin-do-not-use").description("Scan one profile (--profile or AWS_PROFILE) or all aws-vault profiles (--all) for members of the break-glass acl-admin-do-not-use group.").option("-p, --profile <profile>", "Operate on this profile (defaults to AWS_PROFILE)").option("--all", "Scan every aws-vault profile (default when no profile is set)").option("-v, --verbose", "Show skipped profiles and detailed errors").action(async (options) => {
|
|
68
|
+
await checkAdminCommand({
|
|
69
|
+
profile: options.profile,
|
|
70
|
+
all: options.all ?? false,
|
|
71
|
+
verbose: options.verbose ?? false
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
iam.command("remove-admin").summary("Remove users from the acl-admin-do-not-use group").description("Remove break-glass admin access by removing users from acl-admin-do-not-use, for one profile (--profile or AWS_PROFILE) or every aws-vault profile (--all).").option("-p, --profile <profile>", "Operate on this profile (defaults to AWS_PROFILE)").option("--all", "Scan every aws-vault profile (default when no profile is set)").option("--user <name>", "Only remove this user (repeatable); default removes all", collect, []).option("-y, --yes", "Skip the per-profile confirmation prompt").option("--dry-run", "Preview removals without changing anything").option("-v, --verbose", "Show skipped profiles and detailed errors").action(async (options) => {
|
|
75
|
+
await removeAdminCommand({
|
|
76
|
+
profile: options.profile,
|
|
77
|
+
all: options.all ?? false,
|
|
78
|
+
user: options.user ?? [],
|
|
79
|
+
yes: options.yes ?? false,
|
|
80
|
+
dryRun: options.dryRun ?? false,
|
|
81
|
+
verbose: options.verbose ?? false
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
iam.command("break-glass").summary("Grant break-glass admin access (acl-admin-do-not-use)").description("Grant break-glass AdministratorAccess by adding a user to acl-admin-do-not-use for one profile. Defaults to your own IAM user.").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("--user <name>", "Grant to this user (repeatable); default is your own IAM user", collect, []).option("-y, --yes", "Skip the confirmation prompt").option("--dry-run", "Preview the grant without changing anything").option("-v, --verbose", "Show detailed errors").action(async (options) => {
|
|
85
|
+
await breakGlassCommand({
|
|
86
|
+
profile: options.profile,
|
|
87
|
+
user: options.user ?? [],
|
|
88
|
+
yes: options.yes ?? false,
|
|
89
|
+
dryRun: options.dryRun ?? false,
|
|
90
|
+
verbose: options.verbose ?? false
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
aws.command("ec2").summary("EC2 operational commands").description("EC2 operational commands").command("allow-ip").summary("Allow your current public IP for SSH on an EC2 security group").description("Update or create a named roaming SSH rule (port 22) for your current public IP on a chosen EC2 instance security group.").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("-r, --region <region>", "AWS region").option("--rule-name <name>", "SSH rule description (default: \"{FirstName} Roaming\")").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
94
|
+
await allowIpCommand({
|
|
95
|
+
profile: options.profile,
|
|
96
|
+
region: options.region,
|
|
97
|
+
ruleName: options.ruleName,
|
|
98
|
+
verbose: options.verbose ?? false
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
const route53 = aws.command("route53").summary("Route53 DNS commands").description("Route53 DNS commands");
|
|
102
|
+
route53.command("add-record").summary("Interactively add or update a DNS record").description("Add or update a Route53 resource record set. Prompts for hosted zone, type, name, and value when not passed as flags. Shows whether the change creates, overwrites, or appends to an existing record.").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("-z, --zone <name-or-id>", "Hosted zone name or ID (e.g. mountaindev.com)").option("-t, --type <type>", "Record type (A, AAAA, CNAME, TXT, MX, NS, SRV, CAA)").option("-n, --name <name>", "Record name relative to the zone (e.g. \"redline\") or FQDN").option("--value <value>", "Record value (e.g. CNAME target)").option("--ttl <seconds>", "TTL in seconds (default: 300)", (value) => Number.parseInt(value, 10)).option("-y, --yes", "Skip the confirmation prompt").option("--dry-run", "Preview the change without calling Route53").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
103
|
+
await addRecordCommand({
|
|
104
|
+
profile: options.profile,
|
|
105
|
+
zone: options.zone,
|
|
106
|
+
type: options.type,
|
|
107
|
+
name: options.name,
|
|
108
|
+
value: options.value,
|
|
109
|
+
ttl: options.ttl,
|
|
110
|
+
yes: options.yes ?? false,
|
|
111
|
+
dryRun: options.dryRun ?? false,
|
|
112
|
+
verbose: options.verbose ?? false
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
route53.command("remove-record").summary("Interactively delete a DNS record").description("Delete a Route53 resource record set. Prompts for hosted zone, type, and name when not passed as flags, shows the record it found, and confirms before deleting.").option("-p, --profile <profile>", "AWS profile (defaults to AWS_PROFILE)").option("-z, --zone <name-or-id>", "Hosted zone name or ID (e.g. mountaindev.com)").option("-t, --type <type>", "Record type (A, AAAA, CNAME, TXT, MX, NS, SRV, CAA)").option("-n, --name <name>", "Record name relative to the zone (e.g. \"redline\") or FQDN").option("-y, --yes", "Skip the confirmation prompt").option("--dry-run", "Preview the deletion without calling Route53").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
116
|
+
await removeRecordCommand({
|
|
117
|
+
profile: options.profile,
|
|
118
|
+
zone: options.zone,
|
|
119
|
+
type: options.type,
|
|
120
|
+
name: options.name,
|
|
121
|
+
yes: options.yes ?? false,
|
|
122
|
+
dryRun: options.dryRun ?? false,
|
|
123
|
+
verbose: options.verbose ?? false
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
aws.command("ses").summary("SES operational commands").description("SES operational commands").command("send-email").summary("Send a test email via SES (API or SMTP)").description("Send a single test email through the SES API (default) or the SES SMTP endpoint.").requiredOption("--to <address>", "Recipient email address").requiredOption("--from <address>", "Verified SES sender email address").option("--subject <subject>", "Email subject line").option("--body <body>", "Email body (defaults to a timestamp and token)").option("--transport <api|smtp>", "Send via SES API or SES SMTP", "api").option("-r, --region <region>", "AWS region for SES", "us-east-1").option("-p, --profile <profile>", "AWS profile for api transport").option("--access-key-id <id>", "IAM access key ID used as the SMTP username (smtp transport)").option("--dry-run", "Print the email payload without sending").option("-v, --verbose", "Enable verbose logging").action(async (options) => {
|
|
127
|
+
await sendEmailCommand({
|
|
128
|
+
to: options.to,
|
|
129
|
+
from: options.from,
|
|
130
|
+
subject: options.subject,
|
|
131
|
+
body: options.body,
|
|
132
|
+
transport: options.transport,
|
|
133
|
+
region: options.region,
|
|
134
|
+
profile: options.profile,
|
|
135
|
+
accessKeyId: options.accessKeyId,
|
|
136
|
+
dryRun: options.dryRun ?? false,
|
|
137
|
+
verbose: options.verbose ?? false
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { ACL_GROUPS, ADMIN_GROUP_NAME, CUSTOM_POLICIES, DEFAULT_REGION, DEFAULT_ROAMING_NAME, DEFAULT_TTL, RECORD_TYPES, SSH_PORT, addRecordCommand, addRecordOptionsSchema, allowIpCommand, allowIpOptionsSchema, attach, authorizeRule, bareHostedZoneId, breakGlassCommand, breakGlassOptionsSchema, buildUsername, checkAdminCommand, checkAdminOptionsSchema, createCliAccessKey, createUsersInteractive, defaultBody, deleteResourceRecordSet, describeSecurityGroups, detach, ensureAclGroupsExist, ensureGroup, ensureUser, ensureUserInGroup, extractRoamingFirstName, findCnameConflicts, findHostedZone, formatValues, getCurrentPublicIp, getRecordSetsAtName, getResourceRecordSet, getSecurityGroupDetails, hostedZoneLabel, inferAccountName, instanceLabel, isValidSlug, listEnabledRegions, listHostedZones, listInstances, listSshIngressRules, normalizeIdentifier, normalizeRecordValue, normalizeSlug, parseMdUsername, planRecordChange, printFinalState, printSshRules, publishPolicy, registerAwsCommands, removeAdminCommand, removeAdminOptionsSchema, removeRecordCommand, removeRecordOptionsSchema, resolveHostedZone, resolveName, resolveRecordName, resolveRecordType, resolveRoamingRule, revokeRule, roamingRuleDescription, roleToGroup, ruleAlreadyAllowsIp, securityGroupLabel, sendEmailCommand, sendEmailOptionsSchema, sendViaApi, sendViaSmtp, setupAclCommand, setupAclOptionsSchema, sshRuleLabel, upsertResourceRecordSet, usersCommand, usersOptionsSchema, withTrailingDot, withoutTrailingDot };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the hosted zone: from --zone, or interactive picker.
|
|
3
|
+
* @param {{
|
|
4
|
+
* profile?: string;
|
|
5
|
+
* zoneOption?: string;
|
|
6
|
+
* verbose: boolean;
|
|
7
|
+
* }} params
|
|
8
|
+
* @returns {Promise<import("./utils.js").HostedZone>}
|
|
9
|
+
*/
|
|
10
|
+
export function resolveHostedZone(params: {
|
|
11
|
+
profile?: string;
|
|
12
|
+
zoneOption?: string;
|
|
13
|
+
verbose: boolean;
|
|
14
|
+
}): Promise<import("./utils.js").HostedZone>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve record type from --type or interactive picker.
|
|
17
|
+
* @param {string | undefined} typeOption
|
|
18
|
+
* @returns {Promise<string>}
|
|
19
|
+
*/
|
|
20
|
+
export function resolveRecordType(typeOption: string | undefined): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Resolve record name from --name or prompt.
|
|
23
|
+
* @param {{ nameOption?: string; zoneName: string }} params
|
|
24
|
+
* @returns {Promise<string>} FQDN with trailing dot
|
|
25
|
+
*/
|
|
26
|
+
export function resolveName(params: {
|
|
27
|
+
nameOption?: string;
|
|
28
|
+
zoneName: string;
|
|
29
|
+
}): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* `aws route53 add-record` handler: interactively (or via flags) create or
|
|
32
|
+
* update a Route53 resource record set, showing whether the change adds or
|
|
33
|
+
* overwrites.
|
|
34
|
+
* @param {unknown} options
|
|
35
|
+
*/
|
|
36
|
+
export function addRecordCommand(options: unknown): Promise<void>;
|