Example #1
0
    def autoscaler_role(self, policy: iam.Policy) -> iam.Role:
        oidc_arn = cdk.CfnJson(
            self,
            "oidc-provider",
            value=self.cluster.open_id_connect_provider.
            open_id_connect_provider_arn,
        )

        oidc_arn_condition = cdk.CfnJson(
            self,
            "oidc-provider-condition",
            value={
                f"{self.cluster.open_id_connect_provider.open_id_connect_provider_issuer}:sub":
                "system:serviceaccount:kube-system:cluster-autoscaler"
            },
        )
        role = iam.Role(
            self,
            "ClusterAutoscalerRole",
            assumed_by=iam.FederatedPrincipal(
                oidc_arn.value.to_string(),
                {"StringEquals": oidc_arn_condition},
                "sts:AssumeRoleWithWebIdentity",
            ),
        )
        role.attach_inline_policy(policy)
        return role
Example #2
0
    def create_job_execution_role(self, role_name: str,
                                  namespace: str) -> None:
        job_role = iam.Role(
            self,
            "EMR_EKS_Job_Role",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AmazonS3FullAccess"),  # Yes, yes...I know. :)
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AmazonEC2FullAccess"),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AWSGlueConsoleFullAccess"),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "CloudWatchFullAccess"),
            ],
            role_name=role_name,
        )
        cdk.CfnOutput(self, "JobRoleArn", value=job_role.role_arn)

        # Modify trust policy
        string_like = cdk.CfnJson(
            self,
            "ConditionJson",
            value={
                f"{self.eks_cluster.cluster_open_id_connect_issuer}:sub":
                f"system:serviceaccount:{namespace}:emr-containers-sa-*-*-{cdk.Aws.ACCOUNT_ID}-*"
            },
        )
        job_role.assume_role_policy.add_statements(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["sts:AssumeRoleWithWebIdentity"],
                principals=[
                    iam.OpenIdConnectPrincipal(
                        self.eks_cluster.open_id_connect_provider,
                        conditions={"StringLike": string_like},
                    )
                ],
            ))
        string_aud = cdk.CfnJson(
            self,
            "ConditionJsonAud",
            value={
                f"{self.eks_cluster.cluster_open_id_connect_issuer}:aud":
                "sts.amazon.com"
            },
        )
        job_role.assume_role_policy.add_statements(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["sts:AssumeRoleWithWebIdentity"],
                principals=[
                    iam.OpenIdConnectPrincipal(
                        self.eks_cluster.open_id_connect_provider,
                        conditions={"StringEquals": string_aud},
                    )
                ],
            ))
    def add_service_account(self, cluster, name, namespace):
        """
        workaround to add helm role to service account
        
        """
        # create role
        conditions = core.CfnJson(
            self,
            'ConditionJson',
            value={
                "%s:aud" % cluster.cluster_open_id_connect_issuer:
                "sts.amazonaws.com",
                "%s:sub" % cluster.cluster_open_id_connect_issuer:
                "system:serviceaccount:%s:%s" % (namespace, name),
            },
        )
        principal = iam.OpenIdConnectPrincipal(
            cluster.open_id_connect_provider).with_conditions({
                "StringEquals":
                conditions,
            })
        role = iam.Role(self, 'ServiceAccountRole', assumed_by=principal)

        # create policy for the service account
        statements = []
        with open('backend/iam_policy.json') as f:
            data = json.load(f)
            for s in data["Statement"]:
                statements.append(iam.PolicyStatement.from_json(s))
        policy = iam.Policy(self, "LBControllerPolicy", statements=statements)
        policy.attach_to_role(role)

        return eks.KubernetesManifest(
            self,
            "ServiceAccount",
            cluster=cluster,
            manifest=[{
                "apiVersion": "v1",
                "kind": "ServiceAccount",
                "metadata": {
                    "name": name,
                    "namespace": namespace,
                    "labels": {
                        "app.kubernetes.io/name": name,
                        "app.kubernetes.io/managed-by": "Helm",
                    },
                    "annotations": {
                        "eks.amazonaws.com/role-arn": role.role_arn,
                        "meta.helm.sh/release-name": name,
                        "meta.helm.sh/release-namespace": namespace,
                    },
                },
            }],
        )
Example #4
0
 def create_athena_resources(self) -> None:
     self.athena_workgroup = aws_athena.CfnWorkGroup(
         self,
         'SlsBlogAthenaWorkgroup',
         name='sls-blog-athena-workgroup',
         description='Serverless Website demo project (by Dashbird)',
         recursive_delete_option=True,
         state='ENABLED',
         work_group_configuration_updates=core.CfnJson(
             self,
             'sls-blog-workgroup-config-updates',
             value={
                 'EnforceWorkGroupConfiguration': True,
                 'BytesScannedCutoffPerQuery': DataSize.gigabytes(1),
                 'PublishCloudWatchMetricsEnabled': True,
                 'ResultConfigurationUpdates': {
                     'OutputLocation':
                     f's3://{self.bucket_queries.bucket_name}/',
                 },
             },
         ),
     )
    def __init__(self, scope: core.Construct, construct_id: str,
                 subnets: List[str], vpc: ec2.IVpc, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        self.env_name = "MwaaForEmrOnEks"
        self.prefix_list_id = self.node.try_get_context("prefix")

        # Create S3 bucket for MWAA
        bucket = s3.Bucket(self,
                           "MwaaBucket",
                           encryption=s3.BucketEncryption.S3_MANAGED,
                           block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
                           versioned=True)
        core.CfnOutput(self, "BucketName", value=bucket.bucket_name)

        # Create MWAA role
        role = iam.Role(
            self,
            "MwaaRole",
            assumed_by=iam.ServicePrincipal("airflow-env.amazonaws.com"))
        role.add_to_policy(
            iam.PolicyStatement(resources=[
                f"arn:aws:airflow:{self.region}:{self.account}:environment/{self.env_name}"
            ],
                                actions=["airflow:PublishMetrics"],
                                effect=iam.Effect.ALLOW))
        role.add_to_policy(
            iam.PolicyStatement(resources=[
                f"arn:aws:s3:::{bucket.bucket_name}",
                f"arn:aws:s3:::{bucket.bucket_name}/*"
            ],
                                actions=["s3:ListAllMyBuckets"],
                                effect=iam.Effect.DENY))
        role.add_to_policy(
            iam.PolicyStatement(
                resources=[
                    f"arn:aws:s3:::{bucket.bucket_name}",
                    f"arn:aws:s3:::{bucket.bucket_name}/*"
                ],
                actions=["s3:GetObject*", "s3:GetBucket*", "s3:List*"],
                effect=iam.Effect.ALLOW))
        role.add_to_policy(
            iam.PolicyStatement(resources=[
                f"arn:aws:logs:{self.region}:{self.account}:log-group:airflow-{self.env_name}-*"
            ],
                                actions=[
                                    "logs:CreateLogStream",
                                    "logs:CreateLogGroup", "logs:PutLogEvents",
                                    "logs:GetLogEvents", "logs:GetLogRecord",
                                    "logs:GetLogGroupFields",
                                    "logs:GetQueryResults",
                                    "logs:DescribeLogGroups"
                                ],
                                effect=iam.Effect.ALLOW))
        role.add_to_policy(
            iam.PolicyStatement(resources=["*"],
                                actions=["cloudwatch:PutMetricData"],
                                effect=iam.Effect.ALLOW))
        role.add_to_policy(
            iam.PolicyStatement(resources=["*"],
                                actions=[
                                    "emr-containers:StartJobRun",
                                    "emr-containers:ListJobRuns",
                                    "emr-containers:DescribeJobRun",
                                    "emr-containers:CancelJobRun"
                                ],
                                effect=iam.Effect.ALLOW))
        role.add_to_policy(
            iam.PolicyStatement(
                resources=[f"arn:aws:sqs:{self.region}:*:airflow-celery-*"],
                actions=[
                    "sqs:ChangeMessageVisibility", "sqs:DeleteMessage",
                    "sqs:GetQueueAttributes", "sqs:GetQueueUrl",
                    "sqs:ReceiveMessage", "sqs:SendMessage"
                ],
                effect=iam.Effect.ALLOW))
        string_like = core.CfnJson(
            self,
            "ConditionJson",
            value={f"kms:ViaService": f"sqs.{self.region}.amazonaws.com"})
        role.add_to_policy(
            iam.PolicyStatement(
                not_resources=[f"arn:aws:kms:*:{self.account}:key/*"],
                actions=[
                    "kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey*",
                    "kms:Encrypt"
                ],
                effect=iam.Effect.ALLOW,
                conditions={"StringLike": string_like}))

        # Upload MWAA pre-reqs
        s3deploy.BucketDeployment(
            self,
            "DeployPlugin",
            sources=[
                s3deploy.Source.asset(
                    "./emr_eks_cdk/mwaa_plugins",
                    exclude=['**', '!emr_containers_airflow_plugin.zip'])
            ],
            destination_bucket=bucket,
            destination_key_prefix="plug-ins")
        s3req = s3deploy.BucketDeployment(
            self,
            "DeployReq",
            sources=[
                s3deploy.Source.asset("./emr_eks_cdk/mwaa_plugins",
                                      exclude=['**', '!requirements.txt'])
            ],
            destination_bucket=bucket,
            destination_key_prefix="Requirements")
        s3deploy.BucketDeployment(self,
                                  "DeployDag",
                                  sources=[
                                      s3deploy.Source.asset(
                                          "./emr_eks_cdk/mwaa_plugins",
                                          exclude=['**', '!emr_eks.py'])
                                  ],
                                  destination_bucket=bucket,
                                  destination_key_prefix="DAG")

        # Get object versions
        req_obj_version = custom.AwsCustomResource(
            self,
            "GetReqV",
            on_update={
                "service":
                "S3",
                "action":
                "headObject",
                "parameters": {
                    "Bucket": bucket.bucket_name,
                    "Key": "Requirements/requirements.txt"
                },
                "physical_resource_id":
                custom.PhysicalResourceId.from_response("VersionId")
            },
            policy=custom.AwsCustomResourcePolicy.from_sdk_calls(
                resources=custom.AwsCustomResourcePolicy.ANY_RESOURCE),
            role=iam.Role(
                scope=self,
                id=f'{construct_id}-LambdaRole',
                assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
                managed_policies=[
                    iam.ManagedPolicy.from_aws_managed_policy_name(
                        "service-role/AWSLambdaBasicExecutionRole"),
                    iam.ManagedPolicy.from_aws_managed_policy_name(
                        "AmazonS3FullAccess")
                ]))
        core.CfnOutput(self,
                       "ReqObjVersion",
                       value=req_obj_version.get_response_field("VersionId"))
        plugin_obj_version = custom.AwsCustomResource(
            self,
            "GetPluginV",
            on_update={
                "service":
                "S3",
                "action":
                "headObject",
                "parameters": {
                    "Bucket": bucket.bucket_name,
                    "Key": "plug-ins/emr_containers_airflow_plugin.zip"
                },
                "physical_resource_id":
                custom.PhysicalResourceId.from_response("VersionId")
            },
            policy=custom.AwsCustomResourcePolicy.from_sdk_calls(
                resources=custom.AwsCustomResourcePolicy.ANY_RESOURCE),
            role=iam.Role(
                scope=self,
                id=f'{construct_id}-LambdaRole-2',
                assumed_by=iam.ServicePrincipal('lambda.amazonaws.com'),
                managed_policies=[
                    iam.ManagedPolicy.from_aws_managed_policy_name(
                        "service-role/AWSLambdaBasicExecutionRole"),
                    iam.ManagedPolicy.from_aws_managed_policy_name(
                        "AmazonS3FullAccess")
                ]))
        core.CfnOutput(
            self,
            "PluginObjVersion",
            value=plugin_obj_version.get_response_field("VersionId"))

        # Create security group
        mwaa_sg = ec2.SecurityGroup(self,
                                    "SecurityGroup",
                                    vpc=vpc,
                                    description="Allow inbound access to MWAA",
                                    allow_all_outbound=True)
        mwaa_sg.add_ingress_rule(ec2.Peer.prefix_list(self.prefix_list_id),
                                 ec2.Port.all_tcp(),
                                 "allow inbound access from the prefix list")
        mwaa_sg.add_ingress_rule(mwaa_sg, ec2.Port.all_traffic(),
                                 "allow inbound access from the SG")

        mwaa_env = mwaa.CfnEnvironment(
            self,
            "MWAAEnv",
            name=self.env_name,
            dag_s3_path="DAG",
            environment_class="mw1.small",
            execution_role_arn=role.role_arn,
            logging_configuration=mwaa.CfnEnvironment.
            LoggingConfigurationProperty(
                dag_processing_logs=mwaa.CfnEnvironment.
                ModuleLoggingConfigurationProperty(enabled=True,
                                                   log_level='INFO'),
                scheduler_logs=mwaa.CfnEnvironment.
                ModuleLoggingConfigurationProperty(enabled=True,
                                                   log_level='INFO'),
                task_logs=mwaa.CfnEnvironment.
                ModuleLoggingConfigurationProperty(enabled=True,
                                                   log_level='INFO'),
                webserver_logs=mwaa.CfnEnvironment.
                ModuleLoggingConfigurationProperty(enabled=True,
                                                   log_level='INFO'),
                worker_logs=mwaa.CfnEnvironment.
                ModuleLoggingConfigurationProperty(enabled=True,
                                                   log_level='INFO')),
            network_configuration=mwaa.CfnEnvironment.
            NetworkConfigurationProperty(
                security_group_ids=[mwaa_sg.security_group_id],
                subnet_ids=subnets),
            plugins_s3_path="plug-ins/emr_containers_airflow_plugin.zip",
            plugins_s3_object_version=plugin_obj_version.get_response_field(
                "VersionId"),
            requirements_s3_path="Requirements/requirements.txt",
            requirements_s3_object_version=req_obj_version.get_response_field(
                "VersionId"),
            source_bucket_arn=bucket.bucket_arn,
            webserver_access_mode='PUBLIC_ONLY')
        core.CfnOutput(self, "MWAA_NAME", value=self.env_name)
    def __init__(self, scope: core.Construct, id: str, vpc: VpcStack,
                 s3: S3Stack, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)

        mwaa_env_name = "mwaa_codeartifact_env"
        airflow_version = os.environ.get("AIRFLOW_VERSION", "2.0.2")

        # Create MWAA role
        mwaa_role = iam.Role(
            self,
            "MWAARole",
            assumed_by=iam.ServicePrincipal("airflow-env.amazonaws.com"),
        )
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=[(f"arn:aws:airflow:{self.region}:"
                            f"{self.account}:environment/{mwaa_env_name}")],
                actions=["airflow:PublishMetrics"],
                effect=iam.Effect.ALLOW,
            ))
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=[
                    f"arn:aws:s3:::{s3.instance.bucket_name}",
                    f"arn:aws:s3:::{s3.instance.bucket_name}/*",
                ],
                actions=["s3:ListAllMyBuckets"],
                effect=iam.Effect.DENY,
            ))
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=[
                    f"arn:aws:s3:::{s3.instance.bucket_name}",
                    f"arn:aws:s3:::{s3.instance.bucket_name}/*",
                ],
                actions=["s3:*"],
                effect=iam.Effect.ALLOW,
            ))
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=[(f"arn:aws:logs:{self.region}:{self.account}:"
                            f"log-group:airflow-{mwaa_env_name}-*")],
                actions=[
                    "logs:CreateLogStream",
                    "logs:CreateLogGroup",
                    "logs:PutLogEvents",
                    "logs:GetLogEvents",
                    "logs:GetLogRecord",
                    "logs:GetLogGroupFields",
                    "logs:GetQueryResults",
                    "logs:DescribeLogGroups",
                ],
                effect=iam.Effect.ALLOW,
            ))
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=["*"],
                actions=["cloudwatch:PutMetricData"],
                effect=iam.Effect.ALLOW,
            ))
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                resources=[f"arn:aws:sqs:{self.region}:*:airflow-celery-*"],
                actions=[
                    "sqs:ChangeMessageVisibility",
                    "sqs:DeleteMessage",
                    "sqs:GetQueueAttributes",
                    "sqs:GetQueueUrl",
                    "sqs:ReceiveMessage",
                    "sqs:SendMessage",
                ],
                effect=iam.Effect.ALLOW,
            ))
        string_like = core.CfnJson(
            self,
            "ConditionJson",
            value={f"kms:ViaService": f"sqs.{self.region}.amazonaws.com"},
        )
        mwaa_role.add_to_policy(
            iam.PolicyStatement(
                not_resources=[f"arn:aws:kms:*:{self.account}:key/*"],
                actions=[
                    "kms:Decrypt",
                    "kms:DescribeKey",
                    "kms:GenerateDataKey*",
                    "kms:Encrypt",
                ],
                effect=iam.Effect.ALLOW,
                conditions={"StringLike": string_like},
            ))

        # Define MWAA configuration
        security_group_ids = mwaa.CfnEnvironment.NetworkConfigurationProperty(
            security_group_ids=[vpc.mwaa_sg.security_group_id],
            subnet_ids=vpc.get_vpc_private_subnet_ids,
        )

        logs_conf = mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
            enabled=True, log_level="INFO")

        logging_configuration = mwaa.CfnEnvironment.LoggingConfigurationProperty(
            scheduler_logs=logs_conf,
            webserver_logs=logs_conf,
            dag_processing_logs=logs_conf,
        )

        # Create MWAA environment
        mwaa_ca_env = mwaa.CfnEnvironment(
            self,
            "mwaa_ca_env",
            name=mwaa_env_name,
            environment_class="mw1.small",
            airflow_version=airflow_version,
            execution_role_arn=mwaa_role.role_arn,
            source_bucket_arn=s3.instance.bucket_arn,
            dag_s3_path="dags",
            requirements_s3_path="requirements.txt",
            max_workers=2,
            webserver_access_mode="PUBLIC_ONLY",
            network_configuration=security_group_ids,
            logging_configuration=logging_configuration,
        )
        mwaa_ca_env.node.add_dependency(mwaa_role)
Example #7
0
    def __init__(self, scope: core.Construct, construct_id: str,
                 **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        self.emr_namespace = "sparkns"
        self.emr_namespace_fg = "sparkfg"
        self.emrsvcrolearn = f"arn:aws:iam::{self.account}:role/AWSServiceRoleForAmazonEMRContainers"
        self.instance_type = self.node.try_get_context("instance")

        # VPC
        self.vpc = ec2.Vpc(self, "EMR-EKS-VPC", max_azs=3)
        core.Tags.of(self.vpc).add("for-use-with-amazon-emr-managed-policies",
                                   "true")

        # EKS cluster
        self.cluster = eks.Cluster(
            self,
            "EksForSpark",
            version=eks.KubernetesVersion.V1_18,
            default_capacity=0,
            endpoint_access=eks.EndpointAccess.PUBLIC_AND_PRIVATE,
            vpc=self.vpc,
            vpc_subnets=[
                ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE)
            ])

        # Default node group
        self.ng = self.cluster.add_nodegroup_capacity(
            "base-node-group",
            instance_types=[ec2.InstanceType(self.instance_type)],
            min_size=3,
            max_size=10,
            disk_size=50)

        # Create namespaces for EMR to use
        namespace = self.cluster.add_manifest(
            self.emr_namespace, {
                "apiVersion": "v1",
                "kind": "Namespace",
                "metadata": {
                    "name": self.emr_namespace
                },
            })
        namespace_fg = self.cluster.add_manifest(
            self.emr_namespace_fg, {
                "apiVersion": "v1",
                "kind": "Namespace",
                "metadata": {
                    "name": self.emr_namespace_fg
                },
            })

        # Fargate profile
        fgprofile = eks.FargateProfile(self,
                                       "SparkFargateProfile",
                                       cluster=self.cluster,
                                       selectors=[{
                                           "namespace":
                                           self.emr_namespace_fg
                                       }])

        # Create k8s cluster role for EMR
        emrrole = self.cluster.add_manifest(
            "emrrole", {
                "apiVersion":
                "rbac.authorization.k8s.io/v1",
                "kind":
                "Role",
                "metadata": {
                    "name": "emr-containers",
                    "namespace": self.emr_namespace
                },
                "rules": [{
                    "apiGroups": [""],
                    "resources": ["namespaces"],
                    "verbs": ["get"]
                }, {
                    "apiGroups": [""],
                    "resources": [
                        "serviceaccounts", "services", "configmaps", "events",
                        "pods", "pods/log"
                    ],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "deletecollection", "annotate", "patch",
                        "label"
                    ]
                }, {
                    "apiGroups": [""],
                    "resources": ["secrets"],
                    "verbs": ["create", "patch", "delete", "watch"]
                }, {
                    "apiGroups": ["apps"],
                    "resources": ["statefulsets", "deployments"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["batch"],
                    "resources": ["jobs"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["extensions"],
                    "resources": ["ingresses"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["rbac.authorization.k8s.io"],
                    "resources": ["roles", "rolebindings"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "deletecollection", "annotate", "patch",
                        "label"
                    ]
                }]
            })
        emrrole.node.add_dependency(namespace)
        emrrole_fg = self.cluster.add_manifest(
            "emrrole_fg", {
                "apiVersion":
                "rbac.authorization.k8s.io/v1",
                "kind":
                "Role",
                "metadata": {
                    "name": "emr-containers",
                    "namespace": self.emr_namespace_fg
                },
                "rules": [{
                    "apiGroups": [""],
                    "resources": ["namespaces"],
                    "verbs": ["get"]
                }, {
                    "apiGroups": [""],
                    "resources": [
                        "serviceaccounts", "services", "configmaps", "events",
                        "pods", "pods/log"
                    ],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "deletecollection", "annotate", "patch",
                        "label"
                    ]
                }, {
                    "apiGroups": [""],
                    "resources": ["secrets"],
                    "verbs": ["create", "patch", "delete", "watch"]
                }, {
                    "apiGroups": ["apps"],
                    "resources": ["statefulsets", "deployments"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["batch"],
                    "resources": ["jobs"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["extensions"],
                    "resources": ["ingresses"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "annotate", "patch", "label"
                    ]
                }, {
                    "apiGroups": ["rbac.authorization.k8s.io"],
                    "resources": ["roles", "rolebindings"],
                    "verbs": [
                        "get", "list", "watch", "describe", "create", "edit",
                        "delete", "deletecollection", "annotate", "patch",
                        "label"
                    ]
                }]
            })
        emrrole_fg.node.add_dependency(namespace_fg)

        # Bind cluster role to user
        emrrolebind = self.cluster.add_manifest(
            "emrrolebind", {
                "apiVersion":
                "rbac.authorization.k8s.io/v1",
                "kind":
                "RoleBinding",
                "metadata": {
                    "name": "emr-containers",
                    "namespace": self.emr_namespace
                },
                "subjects": [{
                    "kind": "User",
                    "name": "emr-containers",
                    "apiGroup": "rbac.authorization.k8s.io"
                }],
                "roleRef": {
                    "kind": "Role",
                    "name": "emr-containers",
                    "apiGroup": "rbac.authorization.k8s.io"
                }
            })
        emrrolebind.node.add_dependency(emrrole)
        emrrolebind_fg = self.cluster.add_manifest(
            "emrrolebind_fg", {
                "apiVersion":
                "rbac.authorization.k8s.io/v1",
                "kind":
                "RoleBinding",
                "metadata": {
                    "name": "emr-containers",
                    "namespace": self.emr_namespace_fg
                },
                "subjects": [{
                    "kind": "User",
                    "name": "emr-containers",
                    "apiGroup": "rbac.authorization.k8s.io"
                }],
                "roleRef": {
                    "kind": "Role",
                    "name": "emr-containers",
                    "apiGroup": "rbac.authorization.k8s.io"
                }
            })
        emrrolebind_fg.node.add_dependency(emrrole_fg)

        # Map user to IAM role
        emrsvcrole = iam.Role.from_role_arn(self,
                                            "EmrSvcRole",
                                            self.emrsvcrolearn,
                                            mutable=False)
        self.cluster.aws_auth.add_role_mapping(emrsvcrole,
                                               groups=[],
                                               username="******")

        # Job execution role
        self.job_role = iam.Role(
            self,
            "EMR_EKS_Job_Role",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            managed_policies=[
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AmazonS3FullAccess"),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AmazonEC2FullAccess"),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "AWSGlueConsoleFullAccess"),
                iam.ManagedPolicy.from_aws_managed_policy_name(
                    "CloudWatchFullAccess")
            ])
        core.CfnOutput(self, "JobRoleArn", value=self.job_role.role_arn)

        # Modify trust policy
        string_like = core.CfnJson(
            self,
            "ConditionJson",
            value={
                f"{self.cluster.cluster_open_id_connect_issuer}:sub":
                f"system:serviceaccount:emr:emr-containers-sa-*-*-{self.account}-*"
            })
        self.job_role.assume_role_policy.add_statements(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["sts:AssumeRoleWithWebIdentity"],
                                principals=[
                                    iam.OpenIdConnectPrincipal(
                                        self.cluster.open_id_connect_provider,
                                        conditions={"StringLike": string_like})
                                ]))
        string_aud = core.CfnJson(
            self,
            "ConditionJsonAud",
            value={
                f"{self.cluster.cluster_open_id_connect_issuer}:aud":
                "sts.amazon.com"
            })
        self.job_role.assume_role_policy.add_statements(
            iam.PolicyStatement(
                effect=iam.Effect.ALLOW,
                actions=["sts:AssumeRoleWithWebIdentity"],
                principals=[
                    iam.OpenIdConnectPrincipal(
                        self.cluster.open_id_connect_provider,
                        conditions={"StringEquals": string_aud})
                ]))

        # EMR virtual cluster
        self.emr_vc = emrc.CfnVirtualCluster(
            scope=self,
            id="EMRCluster",
            container_provider=emrc.CfnVirtualCluster.
            ContainerProviderProperty(
                id=self.cluster.cluster_name,
                info=emrc.CfnVirtualCluster.ContainerInfoProperty(
                    eks_info=emrc.CfnVirtualCluster.EksInfoProperty(
                        namespace=self.emr_namespace)),
                type="EKS"),
            name="EMRCluster")
        self.emr_vc.node.add_dependency(namespace)
        self.emr_vc.node.add_dependency(emrrolebind)
        emr_vc_fg = emrc.CfnVirtualCluster(
            scope=self,
            id="EMRClusterFG",
            container_provider=emrc.CfnVirtualCluster.
            ContainerProviderProperty(
                id=self.cluster.cluster_name,
                info=emrc.CfnVirtualCluster.ContainerInfoProperty(
                    eks_info=emrc.CfnVirtualCluster.EksInfoProperty(
                        namespace=self.emr_namespace_fg)),
                type="EKS"),
            name="EMRClusterFG")
        emr_vc_fg.node.add_dependency(namespace_fg)
        emr_vc_fg.node.add_dependency(emrrolebind_fg)
        core.CfnOutput(self, "VirtualClusterId", value=self.emr_vc.attr_id)
        core.CfnOutput(self, "FgVirtualClusterId", value=emr_vc_fg.attr_id)

        # Create log group
        log_group = logs.LogGroup(self, "LogGroup")
        core.CfnOutput(self, "LogGroupName", value=log_group.log_group_name)

        # LB Controller role
        self.policy_document = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Effect":
                "Allow",
                "Action": [
                    "iam:CreateServiceLinkedRole",
                    "ec2:DescribeAccountAttributes", "ec2:DescribeAddresses",
                    "ec2:DescribeAvailabilityZones",
                    "ec2:DescribeInternetGateways", "ec2:DescribeVpcs",
                    "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups",
                    "ec2:DescribeInstances", "ec2:DescribeNetworkInterfaces",
                    "ec2:DescribeTags", "ec2:GetCoipPoolUsage",
                    "ec2:DescribeCoipPools",
                    "elasticloadbalancing:DescribeLoadBalancers",
                    "elasticloadbalancing:DescribeLoadBalancerAttributes",
                    "elasticloadbalancing:DescribeListeners",
                    "elasticloadbalancing:DescribeListenerCertificates",
                    "elasticloadbalancing:DescribeSSLPolicies",
                    "elasticloadbalancing:DescribeRules",
                    "elasticloadbalancing:DescribeTargetGroups",
                    "elasticloadbalancing:DescribeTargetGroupAttributes",
                    "elasticloadbalancing:DescribeTargetHealth",
                    "elasticloadbalancing:DescribeTags"
                ],
                "Resource":
                "*"
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "cognito-idp:DescribeUserPoolClient",
                    "acm:ListCertificates", "acm:DescribeCertificate",
                    "iam:ListServerCertificates", "iam:GetServerCertificate",
                    "waf-regional:GetWebACL",
                    "waf-regional:GetWebACLForResource",
                    "waf-regional:AssociateWebACL",
                    "waf-regional:DisassociateWebACL", "wafv2:GetWebACL",
                    "wafv2:GetWebACLForResource", "wafv2:AssociateWebACL",
                    "wafv2:DisassociateWebACL", "shield:GetSubscriptionState",
                    "shield:DescribeProtection", "shield:CreateProtection",
                    "shield:DeleteProtection"
                ],
                "Resource":
                "*"
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "ec2:AuthorizeSecurityGroupIngress",
                    "ec2:RevokeSecurityGroupIngress"
                ],
                "Resource":
                "*"
            }, {
                "Effect": "Allow",
                "Action": ["ec2:CreateSecurityGroup"],
                "Resource": "*"
            }, {
                "Effect": "Allow",
                "Action": ["ec2:CreateTags"],
                "Resource": "arn:aws:ec2:*:*:security-group/*",
                "Condition": {
                    "StringEquals": {
                        "ec2:CreateAction": "CreateSecurityGroup"
                    },
                    "Null": {
                        "aws:RequestTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect": "Allow",
                "Action": ["ec2:CreateTags", "ec2:DeleteTags"],
                "Resource": "arn:aws:ec2:*:*:security-group/*",
                "Condition": {
                    "Null": {
                        "aws:RequestTag/elbv2.k8s.aws/cluster": True,
                        "aws:ResourceTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "ec2:AuthorizeSecurityGroupIngress",
                    "ec2:RevokeSecurityGroupIngress", "ec2:DeleteSecurityGroup"
                ],
                "Resource":
                "*",
                "Condition": {
                    "Null": {
                        "aws:ResourceTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:CreateLoadBalancer",
                    "elasticloadbalancing:CreateTargetGroup"
                ],
                "Resource":
                "*",
                "Condition": {
                    "Null": {
                        "aws:RequestTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:CreateListener",
                    "elasticloadbalancing:DeleteListener",
                    "elasticloadbalancing:CreateRule",
                    "elasticloadbalancing:DeleteRule"
                ],
                "Resource":
                "*"
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:AddTags",
                    "elasticloadbalancing:RemoveTags"
                ],
                "Resource": [
                    "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*",
                    "arn:aws:elasticloadbalancing:*:*:loadbalancer/net/*/*",
                    "arn:aws:elasticloadbalancing:*:*:loadbalancer/app/*/*"
                ],
                "Condition": {
                    "Null": {
                        "aws:RequestTag/elbv2.k8s.aws/cluster": True,
                        "aws:ResourceTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:AddTags",
                    "elasticloadbalancing:RemoveTags"
                ],
                "Resource": [
                    "arn:aws:elasticloadbalancing:*:*:listener/net/*/*/*",
                    "arn:aws:elasticloadbalancing:*:*:listener/app/*/*/*",
                    "arn:aws:elasticloadbalancing:*:*:listener-rule/net/*/*/*",
                    "arn:aws:elasticloadbalancing:*:*:listener-rule/app/*/*/*"
                ]
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:ModifyLoadBalancerAttributes",
                    "elasticloadbalancing:SetIpAddressType",
                    "elasticloadbalancing:SetSecurityGroups",
                    "elasticloadbalancing:SetSubnets",
                    "elasticloadbalancing:DeleteLoadBalancer",
                    "elasticloadbalancing:ModifyTargetGroup",
                    "elasticloadbalancing:ModifyTargetGroupAttributes",
                    "elasticloadbalancing:DeleteTargetGroup"
                ],
                "Resource":
                "*",
                "Condition": {
                    "Null": {
                        "aws:ResourceTag/elbv2.k8s.aws/cluster": False
                    }
                }
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:RegisterTargets",
                    "elasticloadbalancing:DeregisterTargets"
                ],
                "Resource":
                "arn:aws:elasticloadbalancing:*:*:targetgroup/*/*"
            }, {
                "Effect":
                "Allow",
                "Action": [
                    "elasticloadbalancing:SetWebAcl",
                    "elasticloadbalancing:ModifyListener",
                    "elasticloadbalancing:AddListenerCertificates",
                    "elasticloadbalancing:RemoveListenerCertificates",
                    "elasticloadbalancing:ModifyRule"
                ],
                "Resource":
                "*"
            }]
        }
        self.custom_policy_document = iam.PolicyDocument.from_json(
            self.policy_document)
        self.new_managed_policy = iam.ManagedPolicy(
            self, "LBControlPolicy", document=self.custom_policy_document)
        self.lb_role = iam.Role(
            self,
            "LBControllerRole",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
            managed_policies=[self.new_managed_policy])
        string_eq = core.CfnJson(
            self,
            "ConditionJsonEq",
            value={
                f"{self.cluster.cluster_open_id_connect_issuer}:sub":
                f"system:serviceaccount:kube-system:aws-load-balancer-controller"
            })
        self.lb_role.assume_role_policy.add_statements(
            iam.PolicyStatement(effect=iam.Effect.ALLOW,
                                actions=["sts:AssumeRoleWithWebIdentity"],
                                principals=[
                                    iam.OpenIdConnectPrincipal(
                                        self.cluster.open_id_connect_provider,
                                        conditions={"StringEquals": string_eq})
                                ]))

        # Service Account
        self.lb_svc_acct = self.cluster.add_manifest(
            "lb_svc_acct", {
                "apiVersion": "v1",
                "kind": "ServiceAccount",
                "metadata": {
                    "labels": {
                        "app.kubernetes.io/component": "controller",
                        "app.kubernetes.io/name":
                        "aws-load-balancer-controller"
                    },
                    "name": "aws-load-balancer-controller",
                    "namespace": "kube-system",
                    "annotations": {
                        "eks.amazonaws.com/role-arn": self.lb_role.role_arn
                    }
                },
            })

        # Helm chart
        self.cluster.add_helm_chart(
            "lbcontroller",
            chart="aws-load-balancer-controller",
            repository="https://aws.github.io/eks-charts",
            release="aws-load-balancer-controller",
            namespace="kube-system",
            values={
                "clusterName": self.cluster.cluster_name,
                "region": self.region,
                "vpcId": self.vpc.vpc_id,
                "serviceAccount": {
                    "create": False,
                    "name": "aws-load-balancer-controller"
                }
            },
            wait=True)