Repository: SuperCowPowers/sageworks (language: python)

Issue:
Deploy SageWorks Sandbox: Python CDK
Okay our deployment to alpha customer (individual 1)... was 'suboptimal', it really underscored the need to have some nice built in AWS deployments.

Note: There are other tickets for separate deployments (namely Deploy Dashboard)

Lets Review:
- IMPORTANT: https://docs.aws.amazon.com/solutions/latest/constructs/welcome.html
- https://github.com/tinystacks/ops-cli
- https://github.com/tinystacks/aws-docker-templates-flask

References:
- https://www.tinystacks.com/blog-post/aws-cdk-vs-cloudformation/


Discussion / hints:
I've started this a tiny bit (basically just `cdk init`) in this directory `sageworks/aws_stuff/sageworks_sandbox`

What should this do:
- Create the SageWorks-ExecutionRole (also create a second AWSGlueServiceRoleSageWorks duplicate role*)
- Create a bucket for SageWorks to use (S3://\<com.company_name\>-sageworks-artifacts ?)
- Create S3 bucket subfolders
   - incoming-data
   - data-sources
   - feature-sets
   - athena-queries
- Create Glue Catalog Database
  - sageworks 

The initial `deploy_sageworks_sandbox` (Python CDK script) should do the above and also replace as much of this material as possible https://github.com/SuperCowPowers/sageworks/wiki/Initial-Setup-and-Installation

\* AWS Glue Jobs have a 'naming convention' they use where the role HAS to start with 'AWSGlueServiceRole*' at least from the console... I tried making an AWS Glue Job with just the `SageWorks-ExecutionRole` and I couldn't select it in the console dropdown box... it would only show up if the Role started with `AWSGlueServiceRole<blah>`

Fix patch:
diff --git a/aws_setup/sageworks_sandbox/README.md b/aws_setup/sageworks_sandbox/README.md
index 0c635ed8e..ca3919ce0 100644
--- a/aws_setup/sageworks_sandbox/README.md
+++ b/aws_setup/sageworks_sandbox/README.md
@@ -1,7 +1,7 @@
 # SageWorks Sandbox AWS Deployment
 
 To run/deploy the SageWorks AWS Sandbox you'll need install a couple of Python packages
-either in a Python VirtualENV of your choice (PyENV is good) or any Python3 will do
+either in a Python VirtualENV of your choice (PyENV is good) or any Python3 will do. You'll also need to use a newer version of node such as node v19.6
 
 ```
 $ pip install -r requirements.txt
diff --git a/aws_setup/sageworks_sandbox/app.py b/aws_setup/sageworks_sandbox/app.py
index d0fe5a18f..b0b8402b6 100644
--- a/aws_setup/sageworks_sandbox/app.py
+++ b/aws_setup/sageworks_sandbox/app.py
@@ -1,28 +1,34 @@
 #!/usr/bin/env python3
-import os
 
 import aws_cdk as cdk
 
-from sageworks_sandbox.sageworks_sandbox_stack import SageworksSandboxStack
+from sageworks_sandbox.sageworks_sandbox_stack import SageworksStack
+from sageworks.utils.sageworks_config import SageWorksConfig
 
+sageworks_config: SageWorksConfig = SageWorksConfig()
 
 app = cdk.App()
-SageworksSandboxStack(app, "SageworksSandbox",
+
+s3_bucket_name = sageworks_config.get_config_value("SAGEWORKS_AWS", "S3_BUCKET_NAME")
+sageworks_role_name = sageworks_config.get_config_value(
+    "SAGEWORKS_AWS", "SAGEWORKS_ROLE_NAME"
+)
+
+sandbox_stack = SageworksStack(
+    app,
+    "SageworksSandbox",
+    s3_bucket_name=s3_bucket_name,
+    sageworks_role_name=sageworks_role_name
     # If you don't specify 'env', this stack will be environment-agnostic.
     # Account/Region-dependent features and context lookups will not work,
     # but a single synthesized template can be deployed anywhere.
-
     # Uncomment the next line to specialize this stack for the AWS Account
     # and Region that are implied by the current CLI configuration.
-
-    #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
-
+    # env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
     # Uncomment the next line if you know exactly what Account and Region you
     # want to deploy the stack to. */
-
-    #env=cdk.Environment(account='123456789012', region='us-east-1'),
-
+    # env=cdk.Environment(account='123456789012', region='us-east-1'),
     # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
-    )
+)
 
 app.synth()
diff --git a/aws_setup/sageworks_sandbox/sageworks_sandbox/sageworks_sandbox_stack.py b/aws_setup/sageworks_sandbox/sageworks_sandbox/sageworks_sandbox_stack.py
index 43f00a2a3..b9ba55ed6 100644
--- a/aws_setup/sageworks_sandbox/sageworks_sandbox/sageworks_sandbox_stack.py
+++ b/aws_setup/sageworks_sandbox/sageworks_sandbox/sageworks_sandbox_stack.py
@@ -2,18 +2,59 @@
     # Duration,
     Stack,
     # aws_sqs as sqs,
+    aws_iam as iam,
+    aws_s3 as s3,
 )
 from constructs import Construct
+from sageworks.utils.sageworks_config import SageWorksConfig
 
-class SageworksSandboxStack(Stack):
 
-    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
+class SageworksStack(Stack):
+    def __init__(
+        self,
+        scope: Construct,
+        construct_id: str,
+        s3_bucket_name,
+        sageworks_role_name,
+        **kwargs,
+    ) -> None:
         super().__init__(scope, construct_id, **kwargs)
 
-        # The code that defines your stack goes here
+        self.sageworks_execution_role = self.create_execution_role(sageworks_role_name)
+        self.add_artifact_bucket(s3_bucket_name)
 
-        # example resource
-        # queue = sqs.Queue(
-        #     self, "SageworksSandboxQueue",
-        #     visibility_timeout=Duration.seconds(300),
-        # )
+        # TODO: Use this if we need a duplicate role
+        # self.glue_service_role = self.create_execution_role("AWSGlueServiceRole-Sageworks")
+
+    def create_execution_role(self, role_name) -> iam.Role:
+        execution_role = iam.Role(
+            self,
+            id=role_name,
+            assumed_by=iam.AnyPrincipal(),
+            managed_policies=[
+                iam.ManagedPolicy.from_aws_managed_policy_name(
+                    "service-role/AWSGlueServiceRole"
+                ),
+                iam.ManagedPolicy.from_aws_managed_policy_name(
+                    "AmazonSageMakerFullAccess"
+                ),
+            ],
+            role_name=role_name,
+        )
+        execution_role.add_to_policy(
+            iam.PolicyStatement(
+                actions=["s3:*", "s3-object-lambda:*"],
+                resources=[
+                    "arn:aws:s3:::aws-athena-query-results",
+                    "arn:aws:s3:::*sageworks-artifacts*",
+                ],
+            )
+        )
+        return execution_role
+
+    def add_artifact_bucket(self, bucket_name) -> None:
+        self.artifact_bucket = s3.CfnBucket(
+            self,
+            id=bucket_name,
+            bucket_name=bucket_name,
+        )
diff --git a/requirements.txt b/requirements.txt
index f77c516cd..f9a102c10 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,3 +10,4 @@ sagemaker >= 2.143
 dash >= 2.8
 dash-bootstrap-components
 dash-bootstrap-templates
+aws-cdk-lib >= 2.76.0
\ No newline at end of file


Test patch:
diff --git a/aws_setup/sageworks_sandbox/tests/unit/test_sageworks_sandbox_stack.py b/aws_setup/sageworks_sandbox/tests/unit/test_sageworks_sandbox_stack.py
index 3eb40bacf..02fdc34f5 100644
--- a/aws_setup/sageworks_sandbox/tests/unit/test_sageworks_sandbox_stack.py
+++ b/aws_setup/sageworks_sandbox/tests/unit/test_sageworks_sandbox_stack.py
@@ -1,15 +1,22 @@
 import aws_cdk as core
-import aws_cdk.assertions as assertions
+from aws_cdk.assertions import Template
+
+from sageworks_sandbox.sageworks_sandbox_stack import SageworksStack
 
-from sageworks_sandbox.sageworks_sandbox_stack import SageworksSandboxStack
 
 # example tests. To run these tests, uncomment this file along with the example
 # resource in sageworks_sandbox/sageworks_sandbox_stack.py
 def test_sqs_queue_created():
     app = core.App()
-    stack = SageworksSandboxStack(app, "sageworks-sandbox")
-    template = assertions.Template.from_stack(stack)
+    stack = SageworksStack(
+        app,
+        "sageworks-sandbox",
+        "test-scp-sageworks-artifacts",
+        "SageWorks-ExecutionRole",
+    )
+    template = Template.from_stack(stack)
 
-#     template.has_resource_properties("AWS::SQS::Queue", {
-#         "VisibilityTimeout": 300
-#     })
+    # TODO: Check for specific properties
+    template.resource_count_is("AWS::IAM::Role", 1)
+    template.resource_count_is("AWS::IAM::Policy", 1)
+    template.resource_count_is("AWS::S3::Bucket", 1)