@fishawack/lab-env 5.7.0-beta.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.
@@ -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
 
@@ -402,6 +410,10 @@ module.exports.ensureEBInstanceProfileExists = async () => {
402
410
  role,
403
411
  "arn:aws:iam::aws:policy/AWSSecretsManagerClientReadOnlyAccess",
404
412
  );
413
+ await module.exports.attachRolePolicy(
414
+ role,
415
+ "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
416
+ );
405
417
  await module.exports.attachInlineRolePolicy(
406
418
  role,
407
419
  "lab-env-elasticbeanstalk-describe-env",
@@ -504,3 +516,245 @@ module.exports.ensureEBManagedUpdateProfileExists = async () => {
504
516
  }),
505
517
  );
506
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
+ };