# CloudFormation Script

This page contains the CloudFormation template used to set up the cross-account IAM role, Lambda function, and S3 bucket policies required for connecting an **e6data Serverless workspace**.

{% hint style="info" %}
**Note:** This template should be deployed as-is. Do not modify the YAML unless you are familiar with AWS CloudFormation and IAM policies.
{% endhint %}

#### CloudFormation YAML Template

```
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template to create S3 buckets and a Lambda function to manage bucket policies.

Parameters:
  BucketNames:
    Type: CommaDelimitedList
    Description: List of S3 bucket names to create and manage policies.

  VPCEndpointId:
    Type: String
    Description: The ID of the VPC Endpoint.

  E6dataAccountId:
    Type: String
    Description: The e6data aws account.

  ExternalId:
    Type: String
    Description: The external ID for cross-account access.

Resources:
  CrossAccountRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName:  !Sub "e6data-cross-account-role-${AWS::StackName}-${AWS::Region}"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${E6dataAccountId}:root"
            Action: "sts:AssumeRole"
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${E6dataAccountId}:root"
            Action: "sts:TagSession"

  ChangeSetCrossAccountPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub "e6data-change-set-cross-account-policy-${AWS::StackName}-${AWS::Region}"
      Roles:
        - !Ref CrossAccountRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - "cloudformation:CreateChangeSet"
              - "cloudformation:DescribeChangeSet"
              - "cloudformation:DescribeStacks"
            Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*"

  LambdaS3ManagedPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub "e6data-LambdaS3Policy-${AWS::StackName}-${AWS::Region}"
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - ec2:DescribeVpcEndpoints
            Resource: "*"
          - Effect: Allow
            Action:
              - iam:CreatePolicy
              - iam:AttachRolePolicy
              - iam:DetachRolePolicy
              - iam:DeletePolicy
              - iam:ListAttachedRolePolicies
              - iam:GetPolicy
              - iam:GetPolicyVersion
              - iam:CreatePolicyVersion
              - iam:ListPolicyVersions
              - iam:DeletePolicyVersion
            Resource: ["arn:aws:iam::*:policy/*e6data*", "arn:aws:iam::*:role/*e6data*"]
          - Effect: Allow
            Action:
              - iam:ListPolicies
            Resource: "*"
          - Effect: Allow
            Action:
              - s3:ListAllMyBuckets
              - s3:GetBucketPolicy
              - s3:PutBucketPolicy
              - s3:DeleteBucketPolicy
            Resource: "*"   
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - !Ref LambdaS3ManagedPolicy

  ManageBucketPoliciesLambda:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          import json
          import boto3
          from botocore.exceptions import ClientError
          import cfnresponse
          
          # Lambda code here (full code omitted for brevity, use complete code in deployment)
      Runtime: python3.9
      Timeout: 300
      MemorySize: 128

  ManageBucketPoliciesCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt ManageBucketPoliciesLambda.Arn
      BucketNames: !Ref BucketNames
      VPCEndpointId: !Ref VPCEndpointId
      CrossAccountRole: !Ref CrossAccountRole
      Unique: !Sub "${AWS::StackName}-${AWS::Region}-bucket"

  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt ManageBucketPoliciesLambda.Arn
      Principal: cloudformation.amazonaws.com

Outputs:
  RoleArn:
    Description: ARN of the created IAM role
    Value: !GetAtt CrossAccountRole.Arn

  FailedBuckets:
    Description: List of any buckets where policy application failed
    Value: !GetAtt ManageBucketPoliciesCustomResource.FailedBuckets

```

### Notes

* **Parameters to update:**
  * `BucketNames`: S3 buckets to grant access
  * `VPCEndpointId`: VPC endpoint ID for S3
  * `e6dataAccountId` & `ExternalId`: Provided by e6data
* **Outputs:**
  * `RoleArn`: IAM role ARN to share with e6data
  * `FailedBuckets`: Any buckets where policy could not be applied
* Deploy via **AWS Console** or **AWS CLI**. For step-by-step deployment instructions, refer to the [**Configure Secure Access – Deployment Guide** **page.**](/product-documentation/setup/aws-setup/connect-to-e6data-serverless-compute-aws/configuring-secure-access/deployment-guide.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.e6data.com/product-documentation/setup/aws-setup/connect-to-e6data-serverless-compute-aws/configuring-secure-access/cloudformation-script.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
