AWSTemplateFormatVersion: '2010-09-09' Description: Git webhooks to clone and store a Git repository in S3. Used to integrate Git services with AWS services like AWS CodePipeline, AWS CodeBuild, and AWS CodeDeploy. (qs-1nfhrd9bh) Metadata: QuickStartDocumentation: EntrypointName: Parameters for deploying into your selected Region AWS::CloudFormation::Interface: ParameterGroups: - Label: default: General settings Parameters: - OutputBucketName - CustomDomainName - Label: default: Git pull settings Parameters: - ApiSecret - AllowedIps - ExcludeGit - Label: default: AWS Quick Start configuration Parameters: - QSS3BucketName - QSS3BucketRegion - QSS3KeyPrefix - Label: default: VPC configuration Parameters: - VPCId - VPCCidrRange - SubnetIds - ScmHostnameOverride ParameterLabels: AllowedIps: default: Allowed IP addresses ApiSecret: default: API secret CustomDomainName: default: Custom domain name OutputBucketName: default: Output S3 bucket name QSS3BucketName: default: Quick Start S3 bucket name QSS3BucketRegion: default: Quick Start S3 bucket Region QSS3KeyPrefix: default: Quick Start S3 key prefix VPCId: default: VPC ID VPCCidrRange: default: VPC CIDR SubnetIds: default: Subnet IDs ScmHostnameOverride: default: Hostname override ExcludeGit: default: Exclude .git directory Parameters: AllowedIps: Description: Comma-separated list of allowed IP CIDR blocks. The default addresses listed are BitBucket Cloud IP ranges. Type: String Default: 18.205.93.0/25,18.234.32.128/25,13.52.5.0/25 ApiSecret: Description: API secret used to authenticate access to webhooks in GitHub Enterprise, GitLab, and other Git services. If a webhook payload header contains a matching secret, IP address authentication is bypassed. API secrets cannot contain commas (,), backward slashes (\), or quotes ("). Type: String Default: '' NoEcho: 'true' CustomDomainName: Description: Domain name for the webhook endpoint. If left blank, API Gateway creates a domain name for you. Type: String Default: '' OutputBucketName: Description: (Optional) Name for the S3 bucket where the Git repository .zip file is stored. If left blank, the Quick Start creates one for you. Type: String Default: '' QSS3BucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: Quick Start S3 bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Default: aws-quickstart Description: S3 bucket name for Quick Start assets. It can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Type: String QSS3BucketRegion: Default: 'us-east-1' Description: AWS Region where the Quick Start assets S3 bucket (QSS3BucketName) is hosted. Required when using your own S3 bucket. Type: String QSS3KeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/]*$ ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). Default: quickstart-git2s3/ Description: Key prefix for the Quick Start assets S3 bucket. A key prefix is similar to a directory name that enables you to store similar data under the same directory in an S3 bucket. It can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slashes (/). Type: String VPCId: Description: ID of the VPC in which the Lambda function runs. Type: String Default: '' VPCCidrRange: Description: CIDR range of the VPC. Type: String Default: '' SubnetIds: Description: SubnetIDs in which the Lambda function runs. Type: CommaDelimitedList Default: '' ScmHostnameOverride: Description: Name to override the hostname in the header of a webhook JSON payload. Type: String Default: '' ExcludeGit: Description: Choose False to omit the .git directory from the Git repository .zip file. Type: String Default: 'True' AllowedValues: ['True', 'False'] Conditions: UseAllowedIps: !Not - !Equals - !Ref 'AllowedIps' - '' UseApiSecret: !Not - !Equals - !Ref 'ApiSecret' - '' UseCustomDomain: !Not - !Equals - !Ref 'CustomDomainName' - '' AutoGenOutputBucketName: !Not - !Equals - !Ref 'OutputBucketName' - '' ShouldRunInVPC: !Not - !Equals - !Ref 'VPCId' - '' UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] Resources: LambdaZipsBucket: Type: AWS::S3::Bucket Properties: Tags: [] CopyZips: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt 'CopyZipsFunction.Arn' DestBucket: !Ref 'LambdaZipsBucket' SourceBucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] Prefix: !Ref 'QSS3KeyPrefix' Objects: - functions/packages/CreateSSHKey/lambda.zip - functions/packages/DeleteBucketContents/lambda.zip - functions/packages/GitPullS3/lambda.zip CopyZipsRole: 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 Path: / Policies: - PolicyName: lambda-copier PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject Resource: !Sub - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] - Effect: Allow Action: - s3:PutObject - s3:DeleteObject Resource: - !Sub 'arn:aws:s3:::${LambdaZipsBucket}/${QSS3KeyPrefix}*' - Effect: Allow Action: - s3:* Resource: - '*' CopyZipsFunction: Type: AWS::Lambda::Function Properties: Description: Copies objects from a source S3 bucket to a destination. Handler: index.handler Runtime: python3.7 Role: !GetAtt 'CopyZipsRole.Arn' Timeout: 240 Code: ZipFile: !Join - "\n" - - import json - import logging - import threading - import boto3 - import cfnresponse - '' - '' - 'def copy_objects(source_bucket, dest_bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' for o in objects:' - ' key = prefix + o' - ' copy_source = {' - ' ''Bucket'': source_bucket,' - ' ''Key'': key' - ' }' - ' s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key)' - '' - '' - 'def delete_objects(bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' objects = {''Objects'': [{''Key'': prefix + o} for o in objects]}' - ' s3.delete_objects(Bucket=bucket, Delete=objects)' - '' - '' - 'def timeout(event, context):' - ' logging.error(''Execution is about to time out, sending failure response to CloudFormation'')' - ' cfnresponse.send(event, context, cfnresponse.FAILED, {}, None)' - '' - '' - 'def handler(event, context):' - ' # make sure we send a failure to CloudFormation if the function is going to timeout' - ' timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context])' - ' timer.start()' - '' - ' print(''Received event: %s'' % json.dumps(event))' - ' status = cfnresponse.SUCCESS' - ' try:' - ' source_bucket = event[''ResourceProperties''][''SourceBucket'']' - ' dest_bucket = event[''ResourceProperties''][''DestBucket'']' - ' prefix = event[''ResourceProperties''][''Prefix'']' - ' objects = event[''ResourceProperties''][''Objects'']' - ' if event[''RequestType''] == ''Delete'':' - ' delete_objects(dest_bucket, prefix, objects)' - ' else:' - ' copy_objects(source_bucket, dest_bucket, prefix, objects)' - ' except Exception as e:' - ' logging.error(''Exception: %s'' % e, exc_info=True)' - ' status = cfnresponse.FAILED' - ' finally:' - ' timer.cancel()' - ' cfnresponse.send(event, context, status, {}, None)' - '' KeyBucket: Type: AWS::S3::Bucket Properties: Tags: [] OutputBucket: Type: AWS::S3::Bucket Properties: BucketName: !If - AutoGenOutputBucketName - !Ref 'OutputBucketName' - !Ref 'AWS::NoValue' VersioningConfiguration: Status: Enabled Tags: [] KMSKey: Type: AWS::KMS::Key Properties: Description: AWS KWS key to encrypt and decrypt SSH keys stored in S3. KeyPolicy: Version: '2012-10-17' Statement: - Sid: Allow access for Key Administrators Effect: Allow Principal: AWS: - !Join - '' - - 'arn:aws:iam::' - !Ref '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: - !Join - '' - - 'arn:aws:iam::' - !Ref 'AWS::AccountId' - :root Action: - kms:Encrypt - kms:Decrypt - kms:ReEncrypt* - kms:GenerateDataKey* - kms:DescribeKey Resource: '*' - Sid: Allow attachment of persistent resources Effect: Allow Principal: AWS: - !Join - '' - - 'arn:aws:iam::' - !Ref 'AWS::AccountId' - :root Action: - kms:CreateGrant - kms:ListGrants - kms:RevokeGrant Resource: '*' Condition: Bool: kms:GrantIsForAWSResource: true CreateSSHKeyRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: git2cp-sshkeygen PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref 'KeyBucket' - /crypto.zip - Effect: Allow Action: - s3:PutObject Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref 'KeyBucket' - /enc_key - Effect: Allow Action: - kms:Encrypt Resource: - !GetAtt 'KMSKey.Arn' - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:*:*:* CreateSSHKeyLambda: DependsOn: CopyZips Type: AWS::Lambda::Function Properties: Handler: lambda_function.lambda_handler MemorySize: 128 Role: !GetAtt 'CreateSSHKeyRole.Arn' Runtime: python3.8 Timeout: 300 Code: S3Bucket: !Ref 'LambdaZipsBucket' S3Key: !Sub '${QSS3KeyPrefix}functions/packages/CreateSSHKey/lambda.zip' CreateSSHKey: Type: AWS::CloudFormation::CustomResource Version: '1.0' Properties: ServiceToken: !GetAtt 'CreateSSHKeyLambda.Arn' KeyBucket: !Ref 'KeyBucket' Region: !Ref 'AWS::Region' KMSKey: !Ref 'KMSKey' DeleteBucketContentsRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: git2cp-deletebucketcontents PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:* Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref 'KeyBucket' - /* - !Join - '' - - 'arn:aws:s3:::' - !Ref 'OutputBucket' - /* - !Join - '' - - 'arn:aws:s3:::' - !Ref 'KeyBucket' - !Join - '' - - 'arn:aws:s3:::' - !Ref 'OutputBucket' - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:*:*:* DeleteBucketContentsLambda: DependsOn: CopyZips Type: AWS::Lambda::Function Properties: Handler: lambda_function.lambda_handler MemorySize: 128 Role: !GetAtt 'DeleteBucketContentsRole.Arn' Runtime: python3.8 Timeout: 300 Code: S3Bucket: !Ref 'LambdaZipsBucket' S3Key: !Sub '${QSS3KeyPrefix}functions/packages/DeleteBucketContents/lambda.zip' DeleteBucketContents: Type: AWS::CloudFormation::CustomResource Version: '1.0' DependsOn: - KeyBucket - OutputBucket Properties: ServiceToken: !GetAtt 'DeleteBucketContentsLambda.Arn' KeyBucket: !Ref 'KeyBucket' OutputBucket: !Ref 'OutputBucket' CodeBuildServiceRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: "codebuild.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" ManagedPolicyArns: - !Ref CodeBuildEndpointPolicy Tags: - Key: 'tagging-policy' Value: !Join ['-', ["test", "ok"]] CodeBuildBasePolicy: Type: 'AWS::IAM::ManagedPolicy' Properties: Description: Policy with base permissions for CodeBuild. Path: / Roles: - !Ref CodeBuildServiceRole PolicyDocument: Version: 2012-10-17 Statement: - Effect: "Allow" Action: - "logs:CreateLogGroup" - "logs:PutLogEvents" - "logs:CreateLogStream" Resource: - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*" - Effect: "Allow" Action: - "s3:GetObject" - "s3:GetObjectVersion" - "s3:GetBucketAcl" - "s3:GetBucketLocation" Resource: - !GetAtt KeyBucket.Arn - !Sub "${KeyBucket.Arn}/*" - Effect: "Allow" Action: - "s3:PutObject" Resource: - !GetAtt OutputBucket.Arn - !Sub "${OutputBucket.Arn}/*" - Effect: "Allow" Action: - 'kms:Encrypt' - 'kms:Decrypt' - 'kms:ReEncrypt*' - 'kms:GenerateDataKey*' - 'kms:DescribeKey' Resource: - !GetAtt KMSKey.Arn CodeBuildEndpointPolicy: Type: 'AWS::IAM::ManagedPolicy' Properties: Description: Policy with permissions enabling CodeBuild to work with endpoints. Path: / PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - 'ec2:CreateNetworkInterface' - 'ec2:DescribeDhcpOptions' - 'ec2:DescribeNetworkInterfaces' - 'ec2:DeleteNetworkInterface' - 'ec2:DescribeSubnets' - 'ec2:DescribeSecurityGroups' - 'ec2:DescribeVpcs' Resource: '*' - Effect: Allow Action: - 'ec2:CreateNetworkInterfacePermission' Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*' GitPullRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: git2cp-gitpull PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - kms:Decrypt Resource: - !GetAtt 'KMSKey.Arn' - Effect: Allow Action: - s3:PutObject Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref 'OutputBucket' - !Join - '' - - 'arn:aws:s3:::' - !Ref 'OutputBucket' - /* - Effect: Allow Action: - s3:GetObject Resource: - !Join - '' - - 'arn:aws:s3:::' - !Ref 'KeyBucket' - /enc_key - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - arn:aws:logs:*:*:* - Effect: Allow Action: - codebuild:StartBuild - codebuild:BatchGetBuilds Resource: - !GetAtt GitPullCodeBuild.Arn - Effect: Allow Action: - 'ec2:CreateNetworkInterface' - 'ec2:DescribeDhcpOptions' - 'ec2:DescribeNetworkInterfaces' - 'ec2:DeleteNetworkInterface' - 'ec2:DescribeSubnets' - 'ec2:DescribeSecurityGroups' - 'ec2:DescribeVpcs' Resource: - '*' - Effect: Allow Action: - 'ec2:CreateNetworkInterfacePermission' Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:network-interface/*' GitPullCodeBuild: Type: AWS::CodeBuild::Project Properties: VpcConfig: !If - ShouldRunInVPC - SecurityGroupIds: - !Ref 'GitPullSecurityGroup' Subnets: !Ref 'SubnetIds' VpcId: !Ref VPCId - !Ref 'AWS::NoValue' Artifacts: Type: NO_ARTIFACTS Environment: Image: aws/codebuild/standard:2.0 Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL QueuedTimeoutInMinutes: 60 ServiceRole: !GetAtt CodeBuildServiceRole.Arn Source: BuildSpec: | version: 0.2 env: exported-variables: - GIT_COMMIT_ID - GIT_COMMIT_MSG phases: install: runtime-versions: python: 3.7 # commands: # - pip3 install boto3 build: commands: - echo "=======================Start-Deployment=============================" - echo "Getting the SSH Private Key" - | python3 - << "EOF" from boto3 import client import os s3 = client('s3') kms = client('kms') enckey = s3.get_object(Bucket=os.getenv('KeyBucket'), Key=os.getenv('KeyObject'))['Body'].read() privkey = kms.decrypt(CiphertextBlob=enckey)['Plaintext'] with open('enc_key.pem', 'w') as f: print(privkey.decode("utf-8"), file=f) EOF - mv ./enc_key.pem ~/.ssh/id_rsa - ls ~/.ssh/ - echo "Setting SSH config profile" - | cat > ~/.ssh/config <