@fishawack/lab-env 5.6.1 → 5.7.0-beta.2

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.
@@ -10,8 +10,11 @@ const {
10
10
  DescribeEnvironmentsCommand,
11
11
  DescribeApplicationsCommand,
12
12
  DescribeApplicationVersionsCommand,
13
+ DescribeConfigurationSettingsCommand,
14
+ UpdateEnvironmentCommand,
13
15
  CreateApplicationVersionCommand,
14
16
  ListAvailableSolutionStacksCommand,
17
+ RestartAppServerCommand,
15
18
  } = require("@aws-sdk/client-elastic-beanstalk");
16
19
  const { Spinner, poll } = require("../../libs/utilities");
17
20
  const { eb } = require("../../libs/vars");
@@ -31,6 +34,16 @@ module.exports.createElasticBeanstalkApplication = async (name) => {
31
34
  return res;
32
35
  };
33
36
 
37
+ module.exports.describeEnvironment = async (name) => {
38
+ const client = new ElasticBeanstalkClient({});
39
+
40
+ return client.send(
41
+ new DescribeEnvironmentsCommand({
42
+ EnvironmentNames: [name],
43
+ }),
44
+ );
45
+ };
46
+
34
47
  module.exports.createElasticBeanstalkEnvironment = async (
35
48
  name,
36
49
  { language },
@@ -230,3 +243,60 @@ module.exports.deleteApplicationVersion = async (
230
243
  }),
231
244
  );
232
245
  };
246
+
247
+ module.exports.describeConfigurationSettings = async (
248
+ applicationName,
249
+ environmentName,
250
+ ) => {
251
+ const client = new ElasticBeanstalkClient({});
252
+
253
+ const res = await Spinner.prototype.simple(
254
+ `Retrieving configuration for ${environmentName}`,
255
+ () => {
256
+ return client.send(
257
+ new DescribeConfigurationSettingsCommand({
258
+ ApplicationName: applicationName,
259
+ EnvironmentName: environmentName,
260
+ }),
261
+ );
262
+ },
263
+ );
264
+
265
+ return res.ConfigurationSettings[0]?.OptionSettings || [];
266
+ };
267
+
268
+ module.exports.updateEnvironmentSettings = async (
269
+ environmentName,
270
+ optionSettings = [],
271
+ optionsToRemove = [],
272
+ ) => {
273
+ const client = new ElasticBeanstalkClient({});
274
+
275
+ await Spinner.prototype.simple(
276
+ `Updating environment ${environmentName}`,
277
+ () => {
278
+ return client.send(
279
+ new UpdateEnvironmentCommand({
280
+ EnvironmentName: environmentName,
281
+ OptionSettings: optionSettings,
282
+ OptionsToRemove: optionsToRemove,
283
+ }),
284
+ );
285
+ },
286
+ );
287
+ };
288
+
289
+ module.exports.restartAppServer = async (environmentName) => {
290
+ const client = new ElasticBeanstalkClient({});
291
+
292
+ await Spinner.prototype.simple(
293
+ `Restarting app server for ${environmentName}`,
294
+ () => {
295
+ return client.send(
296
+ new RestartAppServerCommand({
297
+ EnvironmentName: environmentName,
298
+ }),
299
+ );
300
+ },
301
+ );
302
+ };
@@ -11,10 +11,18 @@ const {
11
11
  ListAccessKeysCommand,
12
12
  GetRoleCommand,
13
13
  CreateRoleCommand,
14
+ DeleteRoleCommand,
14
15
  AttachRolePolicyCommand,
16
+ DetachRolePolicyCommand,
17
+ ListAttachedRolePoliciesCommand,
15
18
  CreateInstanceProfileCommand,
19
+ DeleteInstanceProfileCommand,
16
20
  AddRoleToInstanceProfileCommand,
21
+ RemoveRoleFromInstanceProfileCommand,
17
22
  PutRolePolicyCommand,
23
+ DeleteRolePolicyCommand,
24
+ ListRolePoliciesCommand,
25
+ CreateServiceLinkedRoleCommand,
18
26
  } = require("@aws-sdk/client-iam");
19
27
  const { Spinner } = require("../../libs/utilities");
20
28
 
@@ -147,6 +155,11 @@ module.exports.syncFWIAMPolicies = async (
147
155
  UserName,
148
156
  "arn:aws:iam::aws:policy/AmazonElastiCacheFullAccess",
149
157
  );
158
+
159
+ await module.exports.attachIAMPolicy(
160
+ UserName,
161
+ "arn:aws:iam::aws:policy/SecretsManagerReadWrite",
162
+ );
150
163
  }
151
164
 
152
165
  if (permissions.includes("manage-users")) {
@@ -393,6 +406,14 @@ module.exports.ensureEBInstanceProfileExists = async () => {
393
406
  role,
394
407
  "arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker",
395
408
  );
409
+ await module.exports.attachRolePolicy(
410
+ role,
411
+ "arn:aws:iam::aws:policy/AWSSecretsManagerClientReadOnlyAccess",
412
+ );
413
+ await module.exports.attachRolePolicy(
414
+ role,
415
+ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
416
+ );
396
417
  await module.exports.attachInlineRolePolicy(
397
418
  role,
398
419
  "lab-env-elasticbeanstalk-describe-env",
@@ -465,6 +486,11 @@ module.exports.ensureEBManagedUpdateProfileExists = async () => {
465
486
  "arn:aws:iam::aws:policy/AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy",
466
487
  );
467
488
 
489
+ await module.exports.attachRolePolicy(
490
+ role,
491
+ "arn:aws:iam::aws:policy/AWSSecretsManagerClientReadOnlyAccess",
492
+ );
493
+
468
494
  await module.exports.attachInlineRolePolicy(
469
495
  role,
470
496
  "lab-env-elasticbeanstalk-custom-managed-updates-permissions",
@@ -490,3 +516,245 @@ module.exports.ensureEBManagedUpdateProfileExists = async () => {
490
516
  }),
491
517
  );
492
518
  };
519
+
520
+ module.exports.ensureEnvironmentInstanceProfile = async (
521
+ ebSlug,
522
+ s3BucketName,
523
+ opensearchDomainArn,
524
+ ) => {
525
+ const role = `${ebSlug}-role`;
526
+
527
+ try {
528
+ await module.exports.getRole(role);
529
+ } catch {
530
+ await module.exports.createRole(
531
+ role,
532
+ JSON.stringify({
533
+ Version: "2012-10-17",
534
+ Statement: [
535
+ {
536
+ Effect: "Allow",
537
+ Action: ["sts:AssumeRole"],
538
+ Principal: {
539
+ Service: ["ec2.amazonaws.com"],
540
+ },
541
+ },
542
+ ],
543
+ }),
544
+ );
545
+
546
+ await module.exports.createInstanceProfile(role);
547
+ await module.exports.attachRoleToInstanceProfile(role, role);
548
+ }
549
+
550
+ await module.exports.attachRolePolicy(
551
+ role,
552
+ "arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier",
553
+ );
554
+ await module.exports.attachRolePolicy(
555
+ role,
556
+ "arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier",
557
+ );
558
+ await module.exports.attachRolePolicy(
559
+ role,
560
+ "arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker",
561
+ );
562
+ await module.exports.attachRolePolicy(
563
+ role,
564
+ "arn:aws:iam::aws:policy/AWSSecretsManagerClientReadOnlyAccess",
565
+ );
566
+ await module.exports.attachRolePolicy(
567
+ role,
568
+ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
569
+ );
570
+ await module.exports.attachInlineRolePolicy(
571
+ role,
572
+ "lab-env-elasticbeanstalk-describe-env",
573
+ JSON.stringify({
574
+ Version: "2012-10-17",
575
+ Statement: [
576
+ {
577
+ Sid: "VisualEditor0",
578
+ Effect: "Allow",
579
+ Action: [
580
+ "ec2:DescribeTags",
581
+ "autoscaling:DescribeAutoScalingGroups",
582
+ "elasticbeanstalk:DescribeEvents",
583
+ ],
584
+ Resource: "*",
585
+ },
586
+ ],
587
+ }),
588
+ );
589
+
590
+ if (s3BucketName) {
591
+ await module.exports.attachInlineRolePolicy(
592
+ role,
593
+ "lab-env-s3-bucket-access",
594
+ JSON.stringify({
595
+ Version: "2012-10-17",
596
+ Statement: [
597
+ {
598
+ Sid: "S3BucketAccess",
599
+ Effect: "Allow",
600
+ Action: [
601
+ "s3:GetObject",
602
+ "s3:PutObject",
603
+ "s3:DeleteObject",
604
+ ],
605
+ Resource: `arn:aws:s3:::${s3BucketName}/*`,
606
+ },
607
+ {
608
+ Sid: "S3BucketList",
609
+ Effect: "Allow",
610
+ Action: ["s3:ListBucket", "s3:GetBucketLocation"],
611
+ Resource: `arn:aws:s3:::${s3BucketName}`,
612
+ },
613
+ ],
614
+ }),
615
+ );
616
+ }
617
+
618
+ if (opensearchDomainArn) {
619
+ await module.exports.attachInlineRolePolicy(
620
+ role,
621
+ "lab-env-opensearch-access",
622
+ JSON.stringify({
623
+ Version: "2012-10-17",
624
+ Statement: [
625
+ {
626
+ Sid: "OpenSearchAccess",
627
+ Effect: "Allow",
628
+ Action: [
629
+ "es:ESHttpGet",
630
+ "es:ESHttpPost",
631
+ "es:ESHttpPut",
632
+ "es:ESHttpDelete",
633
+ "es:ESHttpHead",
634
+ "es:ESHttpPatch",
635
+ ],
636
+ Resource: `${opensearchDomainArn}/*`,
637
+ },
638
+ ],
639
+ }),
640
+ );
641
+ }
642
+
643
+ return role;
644
+ };
645
+
646
+ module.exports.ensureOpenSearchServiceRole = async () => {
647
+ const client = new IAMClient({});
648
+
649
+ try {
650
+ await Spinner.prototype.simple(
651
+ "Ensuring OpenSearch service-linked role exists",
652
+ () => {
653
+ return client.send(
654
+ new CreateServiceLinkedRoleCommand({
655
+ AWSServiceName: "opensearchservice.amazonaws.com",
656
+ }),
657
+ );
658
+ },
659
+ );
660
+ } catch (e) {
661
+ if (
662
+ e.Code !== "InvalidInput" &&
663
+ e.name !== "InvalidInputException" &&
664
+ e.name !== "InvalidInput"
665
+ ) {
666
+ throw e;
667
+ }
668
+ // Role already exists — safe to continue
669
+ }
670
+ };
671
+
672
+ module.exports.removeEnvironmentInstanceProfile = async (ebSlug) => {
673
+ const client = new IAMClient({});
674
+ const role = `${ebSlug}-role`;
675
+
676
+ try {
677
+ await module.exports.getRole(role);
678
+ } catch {
679
+ return;
680
+ }
681
+
682
+ const attachedPolicies = await Spinner.prototype.simple(
683
+ `Listing attached policies for ${role}`,
684
+ () => {
685
+ return client.send(
686
+ new ListAttachedRolePoliciesCommand({ RoleName: role }),
687
+ );
688
+ },
689
+ );
690
+
691
+ for (const policy of attachedPolicies.AttachedPolicies || []) {
692
+ await Spinner.prototype.simple(
693
+ `Detaching policy ${policy.PolicyName}`,
694
+ () => {
695
+ return client.send(
696
+ new DetachRolePolicyCommand({
697
+ RoleName: role,
698
+ PolicyArn: policy.PolicyArn,
699
+ }),
700
+ );
701
+ },
702
+ );
703
+ }
704
+
705
+ const inlinePolicies = await Spinner.prototype.simple(
706
+ `Listing inline policies for ${role}`,
707
+ () => {
708
+ return client.send(new ListRolePoliciesCommand({ RoleName: role }));
709
+ },
710
+ );
711
+
712
+ for (const policyName of inlinePolicies.PolicyNames || []) {
713
+ await Spinner.prototype.simple(
714
+ `Deleting inline policy ${policyName}`,
715
+ () => {
716
+ return client.send(
717
+ new DeleteRolePolicyCommand({
718
+ RoleName: role,
719
+ PolicyName: policyName,
720
+ }),
721
+ );
722
+ },
723
+ );
724
+ }
725
+
726
+ try {
727
+ await Spinner.prototype.simple(
728
+ `Removing role from instance profile ${role}`,
729
+ () => {
730
+ return client.send(
731
+ new RemoveRoleFromInstanceProfileCommand({
732
+ RoleName: role,
733
+ InstanceProfileName: role,
734
+ }),
735
+ );
736
+ },
737
+ );
738
+ } catch {
739
+ /* empty */
740
+ }
741
+
742
+ try {
743
+ await Spinner.prototype.simple(
744
+ `Deleting instance profile ${role}`,
745
+ () => {
746
+ return client.send(
747
+ new DeleteInstanceProfileCommand({
748
+ InstanceProfileName: role,
749
+ }),
750
+ );
751
+ },
752
+ );
753
+ } catch {
754
+ /* empty */
755
+ }
756
+
757
+ await Spinner.prototype.simple(`Deleting role ${role}`, () => {
758
+ return client.send(new DeleteRoleCommand({ RoleName: role }));
759
+ });
760
+ };