@fishawack/lab-env 5.7.0-beta.2 → 5.7.0-beta.4
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 +51 -0
- package/_Test/provision.js +1 -8
- package/_Test/prune.js +8 -5
- package/bitbucket-pipelines.yml +2 -2
- package/cli.js +1 -0
- package/commands/create/cmds/dekey.js +13 -9
- package/commands/create/cmds/deprovision.js +29 -0
- package/commands/create/cmds/key.js +60 -41
- package/commands/create/cmds/provision.js +425 -55
- package/commands/create/cmds/rekey.js +218 -0
- package/commands/create/libs/output-credentials.js +59 -0
- package/commands/create/libs/parallel-runner.js +204 -0
- package/commands/create/libs/resolve-operator.js +59 -0
- package/commands/create/libs/utilities.js +71 -8
- package/commands/create/libs/vars.js +70 -86
- package/commands/create/services/aws/acm.js +4 -2
- package/commands/create/services/aws/cloudfront.js +51 -45
- package/commands/create/services/aws/ec2.js +132 -53
- package/commands/create/services/aws/elasticache.js +159 -0
- package/commands/create/services/aws/elasticbeanstalk.js +141 -60
- package/commands/create/services/aws/iam.js +178 -98
- package/commands/create/services/aws/index.js +1202 -466
- package/commands/create/services/aws/opensearch.js +25 -17
- package/commands/create/services/aws/rds.js +22 -34
- package/commands/create/services/aws/s3.js +14 -27
- package/commands/create/services/aws/secretsmanager.js +8 -15
- package/commands/create/services/aws/ses.js +195 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/cron.config +45 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/queue-worker.config +29 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/laravel/software.config +12 -0
- package/commands/create/templates/elasticbeanstalk/.ebextensions/misc/setvars.config +2 -16
- package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/rewrite-flush.sh +1 -1
- package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/setvars.sh +19 -0
- package/commands/create/templates/elasticbeanstalk/.platform/hooks/postdeploy/restart_queue.sh +4 -0
- package/commands/create/templates/elasticbeanstalk/.platform/hooks/prebuild/setvars.sh +19 -0
- package/commands/scan.js +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const crypto = require("crypto");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const { readFileSync, writeFileSync } = require("fs");
|
|
4
|
+
const { SESClient, ListIdentitiesCommand } = require("@aws-sdk/client-ses");
|
|
5
|
+
const {
|
|
6
|
+
IAMClient,
|
|
7
|
+
CreateUserCommand,
|
|
8
|
+
GetUserCommand,
|
|
9
|
+
CreateAccessKeyCommand,
|
|
10
|
+
ListAccessKeysCommand,
|
|
11
|
+
DeleteAccessKeyCommand,
|
|
12
|
+
AddUserToGroupCommand,
|
|
13
|
+
} = require("@aws-sdk/client-iam");
|
|
14
|
+
const { fromIni } = require("@aws-sdk/credential-providers");
|
|
15
|
+
const { Spinner } = require("../../libs/utilities");
|
|
16
|
+
|
|
17
|
+
const SES_REGION = "us-east-1";
|
|
18
|
+
const SES_PROFILE = "fishawack";
|
|
19
|
+
|
|
20
|
+
const getClient = () =>
|
|
21
|
+
new SESClient({
|
|
22
|
+
region: SES_REGION,
|
|
23
|
+
credentials: fromIni({ profile: SES_PROFILE }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
module.exports.listVerifiedIdentities = async () => {
|
|
27
|
+
const client = getClient();
|
|
28
|
+
|
|
29
|
+
const res = await Spinner.prototype.simple(
|
|
30
|
+
"Retrieving verified SES identities",
|
|
31
|
+
() => {
|
|
32
|
+
return client.send(
|
|
33
|
+
new ListIdentitiesCommand({ IdentityType: "Domain" }),
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return res.Identities || [];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Derives an SES SMTP password from an IAM secret access key.
|
|
43
|
+
* Uses the standard AWS SES SMTP credential derivation algorithm.
|
|
44
|
+
* https://docs.aws.amazon.com/ses/latest/dg/smtp-credentials.html
|
|
45
|
+
*/
|
|
46
|
+
module.exports.deriveSmtpPassword = (secretAccessKey, region = SES_REGION) => {
|
|
47
|
+
const VERSION = Buffer.from([0x04]);
|
|
48
|
+
const date = "11111111";
|
|
49
|
+
const service = "ses";
|
|
50
|
+
const terminal = "aws4_request";
|
|
51
|
+
const message = "SendRawEmail";
|
|
52
|
+
|
|
53
|
+
let signature = Buffer.from(`AWS4${secretAccessKey}`, "utf8");
|
|
54
|
+
for (const element of [date, region, service, terminal, message]) {
|
|
55
|
+
signature = crypto
|
|
56
|
+
.createHmac("sha256", signature)
|
|
57
|
+
.update(element)
|
|
58
|
+
.digest();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return Buffer.concat([VERSION, signature]).toString("base64");
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const SES_SMTP_HOST = "email-smtp.us-east-1.amazonaws.com";
|
|
65
|
+
const SES_GROUP = "AWSSESSendingGroupDoNotRename";
|
|
66
|
+
|
|
67
|
+
const getIAMClient = () =>
|
|
68
|
+
new IAMClient({
|
|
69
|
+
region: SES_REGION,
|
|
70
|
+
credentials: fromIni({ profile: SES_PROFILE }),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
module.exports.ensureOperatorSESCredentials = async (username) => {
|
|
74
|
+
const UserName = `fw-auto-${username}-operator`;
|
|
75
|
+
const client = getIAMClient();
|
|
76
|
+
|
|
77
|
+
// Create user (or confirm it already exists)
|
|
78
|
+
try {
|
|
79
|
+
await Spinner.prototype.simple(
|
|
80
|
+
`Creating SES IAM user ${UserName}`,
|
|
81
|
+
() => client.send(new CreateUserCommand({ UserName })),
|
|
82
|
+
);
|
|
83
|
+
} catch {
|
|
84
|
+
await Spinner.prototype.simple(
|
|
85
|
+
`Retrieving existing SES IAM user ${UserName}`,
|
|
86
|
+
() => client.send(new GetUserCommand({ UserName })),
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add user to SES sending group
|
|
91
|
+
await Spinner.prototype.simple(`Adding ${UserName} to ${SES_GROUP}`, () =>
|
|
92
|
+
client.send(
|
|
93
|
+
new AddUserToGroupCommand({ UserName, GroupName: SES_GROUP }),
|
|
94
|
+
),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Check for existing keys
|
|
98
|
+
const existing = await client.send(new ListAccessKeysCommand({ UserName }));
|
|
99
|
+
|
|
100
|
+
if (existing.AccessKeyMetadata.length) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Create access key and derive SMTP password
|
|
105
|
+
const keyRes = await Spinner.prototype.simple(
|
|
106
|
+
"Creating access key for SES user",
|
|
107
|
+
() => client.send(new CreateAccessKeyCommand({ UserName })),
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const smtpPassword = module.exports.deriveSmtpPassword(
|
|
111
|
+
keyRes.AccessKey.SecretAccessKey,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Wait for IAM credentials to propagate across AWS services
|
|
115
|
+
await Spinner.prototype.simple(
|
|
116
|
+
"Waiting for IAM credentials to propagate",
|
|
117
|
+
() => new Promise((r) => setTimeout(r, 10000)),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
username: keyRes.AccessKey.AccessKeyId,
|
|
122
|
+
password: smtpPassword,
|
|
123
|
+
host: SES_SMTP_HOST,
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
module.exports.rotateOperatorSESCredentials = async (username) => {
|
|
128
|
+
const UserName = `fw-auto-${username}-operator`;
|
|
129
|
+
const client = getIAMClient();
|
|
130
|
+
|
|
131
|
+
// List and delete existing keys
|
|
132
|
+
const existing = await Spinner.prototype.simple(
|
|
133
|
+
`Listing access keys for ${UserName}`,
|
|
134
|
+
() => client.send(new ListAccessKeysCommand({ UserName })),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
for (const key of existing.AccessKeyMetadata) {
|
|
138
|
+
await Spinner.prototype.simple(
|
|
139
|
+
`Removing old access key ${key.AccessKeyId}`,
|
|
140
|
+
() =>
|
|
141
|
+
client.send(
|
|
142
|
+
new DeleteAccessKeyCommand({
|
|
143
|
+
UserName,
|
|
144
|
+
AccessKeyId: key.AccessKeyId,
|
|
145
|
+
}),
|
|
146
|
+
),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Create new access key and derive SMTP password
|
|
151
|
+
const keyRes = await Spinner.prototype.simple(
|
|
152
|
+
"Creating new access key for SES user",
|
|
153
|
+
() => client.send(new CreateAccessKeyCommand({ UserName })),
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const smtpPassword = module.exports.deriveSmtpPassword(
|
|
157
|
+
keyRes.AccessKey.SecretAccessKey,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// Wait for IAM credentials to propagate across AWS services
|
|
161
|
+
await Spinner.prototype.simple(
|
|
162
|
+
"Waiting for IAM credentials to propagate",
|
|
163
|
+
() => new Promise((r) => setTimeout(r, 10000)),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
username: keyRes.AccessKey.AccessKeyId,
|
|
168
|
+
password: smtpPassword,
|
|
169
|
+
host: SES_SMTP_HOST,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const SES_FROM = "digitalautomation@avalerehealth.com";
|
|
174
|
+
const MISC_PATH = `${os.homedir()}/targets/misc.json`;
|
|
175
|
+
|
|
176
|
+
module.exports.updateMiscNodemailer = (creds) => {
|
|
177
|
+
const { misc } = require("../../libs/vars");
|
|
178
|
+
|
|
179
|
+
const file = JSON.parse(readFileSync(MISC_PATH, { encoding: "utf8" }));
|
|
180
|
+
|
|
181
|
+
file.nodemailer = {
|
|
182
|
+
driver: "AWSSES",
|
|
183
|
+
AWSSES: {
|
|
184
|
+
username: creds.username,
|
|
185
|
+
password: creds.password,
|
|
186
|
+
host: creds.host,
|
|
187
|
+
},
|
|
188
|
+
from: SES_FROM,
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
writeFileSync(MISC_PATH, JSON.stringify(file, null, 4));
|
|
192
|
+
|
|
193
|
+
// Mutate the in-memory misc object so email.js picks up fresh creds
|
|
194
|
+
misc.nodemailer = file.nodemailer;
|
|
195
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
files:
|
|
2
|
+
"/usr/local/bin/is_first_instance.sh":
|
|
3
|
+
mode: "000755"
|
|
4
|
+
owner: root
|
|
5
|
+
group: root
|
|
6
|
+
content: |
|
|
7
|
+
#!/bin/bash
|
|
8
|
+
# Get IMDSv2 token (required on Amazon Linux 2023)
|
|
9
|
+
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
|
|
10
|
+
|
|
11
|
+
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null)
|
|
12
|
+
REGION=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/dynamic/instance-identity/document 2>/dev/null | jq -r .region)
|
|
13
|
+
|
|
14
|
+
# Find the Auto Scaling Group name from the Elastic Beanstalk environment
|
|
15
|
+
ASG=$(aws ec2 describe-tags --filters "Name=resource-id,Values=$INSTANCE_ID" \
|
|
16
|
+
--region "$REGION" --output json | jq -r '.[][] | select(.Key=="aws:autoscaling:groupName") | .Value')
|
|
17
|
+
|
|
18
|
+
# Find the first instance in the Auto Scaling Group
|
|
19
|
+
FIRST=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$ASG" \
|
|
20
|
+
--region "$REGION" --output json | \
|
|
21
|
+
jq -r '.AutoScalingGroups[].Instances[] | select(.LifecycleState=="InService") | .InstanceId' | sort | head -1)
|
|
22
|
+
|
|
23
|
+
# If the instance ids are the same exit 0
|
|
24
|
+
[ "$FIRST" = "$INSTANCE_ID" ]
|
|
25
|
+
|
|
26
|
+
"/usr/local/bin/laravel_cron.sh":
|
|
27
|
+
mode: "000755"
|
|
28
|
+
owner: root
|
|
29
|
+
group: root
|
|
30
|
+
content: |
|
|
31
|
+
#!/bin/bash
|
|
32
|
+
/usr/local/bin/is_first_instance.sh || exit
|
|
33
|
+
cd /var/app/current && echo $(date -u) $(php artisan schedule:run)
|
|
34
|
+
|
|
35
|
+
"/etc/cron.d/laravel_cron":
|
|
36
|
+
mode: "000644"
|
|
37
|
+
owner: root
|
|
38
|
+
group: root
|
|
39
|
+
content: |
|
|
40
|
+
* * * * * root bash -l /usr/local/bin/laravel_cron.sh >> /var/log/laravel_cron.log 2>&1
|
|
41
|
+
|
|
42
|
+
commands:
|
|
43
|
+
rm_old_cron:
|
|
44
|
+
command: "rm -f /etc/cron.d/laravel_cron.bak"
|
|
45
|
+
ignoreErrors: true
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
files:
|
|
2
|
+
"/etc/systemd/system/queue-worker.service":
|
|
3
|
+
mode: "000644"
|
|
4
|
+
owner: root
|
|
5
|
+
group: root
|
|
6
|
+
content: |
|
|
7
|
+
[Unit]
|
|
8
|
+
Description=Laravel Queue Worker
|
|
9
|
+
After=network.target php-fpm.service
|
|
10
|
+
|
|
11
|
+
[Service]
|
|
12
|
+
User=webapp
|
|
13
|
+
Group=webapp
|
|
14
|
+
WorkingDirectory=/var/app/current
|
|
15
|
+
EnvironmentFile=/etc/sysconfig/env
|
|
16
|
+
ExecStart=/usr/bin/php /var/app/current/artisan queue:work database --queue=streaming,default --timeout=620 --tries=1
|
|
17
|
+
Restart=always
|
|
18
|
+
RestartSec=5
|
|
19
|
+
StandardOutput=append:/var/log/queue-worker.log
|
|
20
|
+
StandardError=append:/var/log/queue-worker.log
|
|
21
|
+
|
|
22
|
+
[Install]
|
|
23
|
+
WantedBy=multi-user.target
|
|
24
|
+
|
|
25
|
+
commands:
|
|
26
|
+
01_systemd_reload:
|
|
27
|
+
command: systemctl daemon-reload
|
|
28
|
+
02_enable_queue:
|
|
29
|
+
command: systemctl enable queue-worker
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
+
packages:
|
|
2
|
+
yum:
|
|
3
|
+
jq: []
|
|
4
|
+
mariadb105: []
|
|
5
|
+
|
|
6
|
+
commands:
|
|
7
|
+
01_install_phpredis:
|
|
8
|
+
command: "yum install -y php$(php -r 'echo PHP_MAJOR_VERSION.\".\".PHP_MINOR_VERSION;')-pecl-redis6"
|
|
9
|
+
test: "! php -m | grep -q redis"
|
|
10
|
+
|
|
1
11
|
option_settings:
|
|
12
|
+
aws:elasticbeanstalk:application:environment:
|
|
13
|
+
BASH_ENV: /etc/profile.d/sh.local
|
|
2
14
|
aws:elasticbeanstalk:container:php:phpini:
|
|
3
15
|
document_root: /public
|
|
4
16
|
aws:elasticbeanstalk:environment:proxy:
|
|
@@ -5,22 +5,8 @@ option_settings:
|
|
|
5
5
|
commands:
|
|
6
6
|
setvars:
|
|
7
7
|
command: |
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
> /etc/profile.d/sh.local
|
|
12
|
-
> /etc/sysconfig/env
|
|
13
|
-
|
|
14
|
-
for GROUP in app storage database; do
|
|
15
|
-
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
|
16
|
-
--secret-id "${ENV_NAME}-${GROUP}" \
|
|
17
|
-
--region "$REGION" \
|
|
18
|
-
--query SecretString \
|
|
19
|
-
--output text 2>/dev/null) || continue
|
|
20
|
-
|
|
21
|
-
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "export \(.key)=\u0027\(.value)\u0027"' >> /etc/profile.d/sh.local
|
|
22
|
-
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "\(.key)=\"\(.value)\""' >> /etc/sysconfig/env
|
|
23
|
-
done
|
|
8
|
+
/opt/elasticbeanstalk/bin/get-config environment | jq -r "to_entries | .[] | \"export \(.key)='\(.value)'\"" > /etc/profile.d/sh.local
|
|
9
|
+
/opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | .[] | "\(.key)=\"\(.value)\""' > /etc/sysconfig/env
|
|
24
10
|
|
|
25
11
|
packages:
|
|
26
12
|
yum:
|
package/commands/create/templates/elasticbeanstalk/.platform/confighooks/postdeploy/setvars.sh
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
ENV_NAME=$(/opt/elasticbeanstalk/bin/get-config container -k environment_name)
|
|
4
|
+
SECRET_ENV=${SECRET_MIRROR_ENV:-$ENV_NAME}
|
|
5
|
+
REGION=$(TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region)
|
|
6
|
+
|
|
7
|
+
> /etc/profile.d/sh.local
|
|
8
|
+
> /etc/sysconfig/env
|
|
9
|
+
|
|
10
|
+
for GROUP in <%= SECRET_GROUPS %>; do
|
|
11
|
+
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
|
12
|
+
--secret-id "${SECRET_ENV}-${GROUP}" \
|
|
13
|
+
--region "$REGION" \
|
|
14
|
+
--query SecretString \
|
|
15
|
+
--output text 2>/dev/null) || continue
|
|
16
|
+
|
|
17
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "export \(.key)='"'"'\(.value)'"'"'"' >> /etc/profile.d/sh.local
|
|
18
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "\(.key)=\"\(.value)\""' >> /etc/sysconfig/env
|
|
19
|
+
done
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
ENV_NAME=$(/opt/elasticbeanstalk/bin/get-config container -k environment_name)
|
|
4
|
+
SECRET_ENV=${SECRET_MIRROR_ENV:-$ENV_NAME}
|
|
5
|
+
REGION=$(TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600") && curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region)
|
|
6
|
+
|
|
7
|
+
> /etc/profile.d/sh.local
|
|
8
|
+
> /etc/sysconfig/env
|
|
9
|
+
|
|
10
|
+
for GROUP in <%= SECRET_GROUPS %>; do
|
|
11
|
+
SECRET_JSON=$(aws secretsmanager get-secret-value \
|
|
12
|
+
--secret-id "${SECRET_ENV}-${GROUP}" \
|
|
13
|
+
--region "$REGION" \
|
|
14
|
+
--query SecretString \
|
|
15
|
+
--output text 2>/dev/null) || continue
|
|
16
|
+
|
|
17
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "export \(.key)='"'"'\(.value)'"'"'"' >> /etc/profile.d/sh.local
|
|
18
|
+
echo "$SECRET_JSON" | jq -r 'to_entries | .[] | "\(.key)=\"\(.value)\""' >> /etc/sysconfig/env
|
|
19
|
+
done
|
package/commands/scan.js
CHANGED
|
@@ -32,7 +32,7 @@ const run = () => {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
if (failed) {
|
|
35
|
-
if (_.coreConfig
|
|
35
|
+
if (_.coreConfig?.attributes?.failOnScanErrors) {
|
|
36
36
|
throw new Error(
|
|
37
37
|
"Scan found issues. Failing the build as failOnScanErrors flag is set to true.",
|
|
38
38
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishawack/lab-env",
|
|
3
|
-
"version": "5.7.0-beta.
|
|
3
|
+
"version": "5.7.0-beta.4",
|
|
4
4
|
"description": "Docker manager for FW",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,12 +25,14 @@
|
|
|
25
25
|
"@aws-sdk/client-cloudfront": "^3.141.0",
|
|
26
26
|
"@aws-sdk/client-ec2": "^3.399.0",
|
|
27
27
|
"@aws-sdk/client-elastic-beanstalk": "^3.395.0",
|
|
28
|
+
"@aws-sdk/client-elasticache": "^3.1075.0",
|
|
28
29
|
"@aws-sdk/client-iam": "^3.150.0",
|
|
29
30
|
"@aws-sdk/client-opensearch": "^3.1065.0",
|
|
30
31
|
"@aws-sdk/client-rds": "^3.395.0",
|
|
31
32
|
"@aws-sdk/client-s3": "^3.141.0",
|
|
32
33
|
"@aws-sdk/client-secrets-manager": "^3.395.0",
|
|
33
|
-
"@aws-sdk/
|
|
34
|
+
"@aws-sdk/client-ses": "^3.1075.0",
|
|
35
|
+
"@aws-sdk/credential-providers": "^3.1075.0",
|
|
34
36
|
"apache-md5": "^1.1.8",
|
|
35
37
|
"axios": "^0.21.4",
|
|
36
38
|
"chalk": "4.1.0",
|
|
@@ -44,6 +46,7 @@
|
|
|
44
46
|
"glob": "^13.0.6",
|
|
45
47
|
"inquirer": "8.1.2",
|
|
46
48
|
"lodash": "^4.17.21",
|
|
49
|
+
"log-update": "^4.0.0",
|
|
47
50
|
"nodemailer": "^6.9.15",
|
|
48
51
|
"ora": "5.4.1",
|
|
49
52
|
"p-limit": "^7.3.0",
|