# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Automates creation Snowflake integration object for S3 access. # - Creates an integration between any supplied Snowflake DB/Schema and Amazon S3 bucket # # # kmmahaj AWSTemplateFormatVersion: 2010-09-09 Description: >- AWS CloudFormation template to create Snowflake integration object for S3 access. Parameters: SourceBucket: Description: S3 Bucket that contains the Snowflake integration Lambda Type: String Default: 's3-snowflakeintegration-accountId-region' MinLength: '1' MaxLength: '255' snowaccount: Description: REQUIRED. Snowflake Account Identifier Type: String AllowedPattern: .+ ConstraintDescription: snowaccount is required snowuser: Description: REQUIRED. Snowflake account user Type: String NoEcho: true AllowedPattern: .+ ConstraintDescription: snowuser is required snowpass: Description: REQUIRED. Snowflake password for the Snowflake account user Type: String NoEcho: true AllowedPattern: .+ ConstraintDescription: snowpass is required snowdb: Description: REQUIRED. Snowflake Database Type: String AllowedPattern: .+ ConstraintDescription: snowdb is required snowschema: Description: REQUIRED. Snowflake Schema Type: String AllowedPattern: .+ ConstraintDescription: snowschema is required S3BUCKETNAME: Description: REQUIRED. Amazon S3 Bucket Name. This is the name of the S3 bucket that needs to be accessed by the Snowflake Integration object Type: String AllowedPattern: .+ ConstraintDescription: S3BUCKETNAME is required S3BUCKETPREFIX: Description: REQUIRED. Amazon S3 Bucket prefix. This is the S3 prefix/folder of the bucket that needs to be accessed by the Snowflake Integration object Type: String AllowedPattern: .+ ConstraintDescription: S3BUCKETPREFIX is required snowtable: Description: OPTIONAL. Name of a Snowflake table in the Snowflake DB Type: String AllowedPattern: .+ ConstraintDescription: snowtable is optional SnowflakeSecretArn: Description: >- Enter ARN of the Secrets Manager Secret that contains Snowflake connection information Type: String Default: '' SnowflakeLambdaArn: Description: >- Enter ARN of the Snowflake Lambda function if the product was already launched earlier Type: String Default: '' Conditions: CreateNewLambda: !Equals - !Ref SnowflakeLambdaArn - '' CreateNewSnowflakeSecret: !Equals - !Ref SnowflakeSecretArn - '' Resources: # Secrets Management - Snowflake Credentials SnowflakeKMSKey: Type: AWS::KMS::Key Condition: CreateNewSnowflakeSecret Properties: Description: "This is KMS Key Id used to encrypt/decrypt the Secret" EnableKeyRotation: true KeyPolicy: Version: '2012-10-17' Id: sf-key-default-1 Statement: - Sid: Allow administration of the key Effect: Allow Principal: AWS: !Sub arn:aws:iam::${AWS::AccountId}:root Action: - kms:Create* - kms:Describe* - kms:Enable* - kms:List* - kms:Put* - kms:Update* - kms:Revoke* - kms:Disable* - kms:Get* - kms:Delete* - kms:ScheduleKeyDeletion - kms:CancelKeyDeletion Resource: '*' - Sid: Allow use of the key Effect: Allow Principal: AWS: !Sub ${AWS::AccountId} Action: - kms:Encrypt - kms:Decrypt - kms:ReEncrypt - kms:GenerateDataKey - kms:CreateGrant - kms:DescribeKey Resource: '*' Condition: StringEquals: kms:ViaService: !Sub secretsmanager.${AWS::Region}.amazonaws.com kms:CallerAccount: !Sub ${AWS::AccountId} SnowflakeKMSAlias: Type: AWS::KMS::Alias Condition: CreateNewSnowflakeSecret Properties: AliasName: !Sub "alias/Snowflake-KMS-${AWS::StackName}" TargetKeyId: Ref: SnowflakeKMSKey #Create Secret SnowflakeSecretString: Type: AWS::SecretsManager::Secret Condition: CreateNewSnowflakeSecret Properties: Description: Credentials required for Snowflake KmsKeyId: !Ref SnowflakeKMSKey SecretString: Fn::Join: - '' - - '{"snowaccount":"' - Ref: snowaccount - '","snowuser": "' - Ref: snowuser - '","snowpass": "' - Ref: snowpass - '","snowdb": "' - Ref: snowdb - '","snowschema": "' - Ref: snowschema - '"}' #--------------------------------------------------------------------------------------------------- # 1- Lambda Function that creates the Snowflake integration with S3 # 2- Custom Lambda backed resource to create the Snowflake Storage integration # -------------------------------------------------------------------------------------------------- #Custom Lambda backed Resource for creating the Snowflake Storage Integration Object CreateSnowflakeStorageIntegration: Type: 'Custom::CreateSnowflakeStorageIntegration' DependsOn: - SnowflakeIntegrationLambdaExecutePermission Properties: ServiceToken: !If [CreateNewLambda, !GetAtt 'SnowflakeIntegrationLambda.Arn', !Ref SnowflakeLambdaArn] SourceAccountId: !Ref 'AWS::AccountId' SNOW_S3_BUCKETNAME: !Ref S3BUCKETNAME SNOW_S3_BUCKETPREFIX: !Ref S3BUCKETPREFIX SNOW_SECRET: !If [CreateNewSnowflakeSecret, !Ref SnowflakeSecretString, !Ref SnowflakeSecretArn] #Permission for CFN to invoke Snowflake Storage Integration lambda backed resource SnowflakeIntegrationLambdaExecutePermission: Type: 'AWS::Lambda::Permission' Properties: Action: 'lambda:InvokeFunction' FunctionName: !If [CreateNewLambda, !GetAtt 'SnowflakeIntegrationLambda.Arn', !Ref SnowflakeLambdaArn] Principal: 'cloudformation.amazonaws.com' SourceAccount: !Ref 'AWS::AccountId' #Lambda Function that creates the Snowflake integration with S3 SnowflakeIntegrationLambda: Type: 'AWS::Lambda::Function' Condition: CreateNewLambda Properties: FunctionName: !Join - '' - - SnowflakeIntegration_ - Lambda Role: !GetAtt SnowflakeIntegrationLambdaRole.Arn Code: S3Bucket: !Ref SourceBucket S3Key: !Join - '' - - SnowflakeIntegration_Lambda - / - SnowflakeIntegration_Lambda - .zip Description: SnowflakeIntegrationLambda Handler: SnowflakeIntegration_Lambda.lambda_handler MemorySize: '256' Runtime: python3.7 Layers: - !Ref SnowflakeLayer Environment: Variables: CURRENT_AWS_ACCOUNT: !Ref 'AWS::AccountId' SNOW_S3_BUCKETNAME: !Ref S3BUCKETNAME SNOW_S3_BUCKETPREFIX: !Ref S3BUCKETPREFIX SNOW_TABLE: !Ref snowtable SNOW_SECRET: !Ref SnowflakeSecretString Timeout: 500 #Lambda Layer for Snowflake Python Connector SnowflakeLayer: Type: AWS::Lambda::LayerVersion Condition: CreateNewLambda Properties: CompatibleRuntimes: - python3.6 - python3.7 - python3.8 Content: S3Bucket: !Ref SourceBucket S3Key: snowflakelayer.zip Description: Lambda layer for Snowflake Python Connector LayerName: snowflakelayer LicenseInfo: MIT #IAM Role for the SnowflakeIntegration Lambda SnowflakeIntegrationLambdaRole: Type: 'AWS::IAM::Role' Condition: CreateNewLambda Properties: RoleName: !Sub snowflakeintegrationlamdarole-${AWS::Region}-${S3BUCKETNAME}-${S3BUCKETPREFIX} AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowLambdaAssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Action: 'sts:AssumeRole' Policies: - PolicyName: SnowflakeIntegrationLambdaPolicy PolicyDocument: Version: 2012-10-17 Statement: - Sid: '1' Action: - 's3:*' Effect: Allow Resource: - !Sub arn:${AWS::Partition}:s3:::${SourceBucket} - !Sub arn:${AWS::Partition}:s3:::${SourceBucket}/* - Sid: '2' Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'logs:DescribeLogStreams' Effect: Allow Resource: '*' - Sid: '3' Action: - 'ssm:*' Effect: Allow Resource: '*' - Sid: '4' Action: - 'secretsmanager:GetSecretValue' - 'secretsmanager:ListSecrets' Effect: Allow Resource: !Join [':',['arn:aws:secretsmanager', !Ref 'AWS::Region', !Ref 'AWS::AccountId','secret','SnowflakeSecretString-*']] - Sid: '5' Action: - 'kms:Decrypt' Effect: Allow Resource: !GetAtt "SnowflakeKMSKey.Arn" ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/IAMFullAccess' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'