├── setup-time-window-resources-stack-parameters.json ├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── time-window-demo-resources-parameters.json ├── register-time-window.js ├── CONTRIBUTING.md ├── README.md ├── setup-time-window-resources-stack.yml ├── process-time-windows.js ├── LICENSE └── time-window-demo-resources.yml /setup-time-window-resources-stack-parameters.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ParameterKey": "DemoResourcesCodeCommitRepo", 4 | "ParameterValue": "aws-codepipeline-time-windows" 5 | } 6 | ] -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /time-window-demo-resources-parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters" : { 3 | "CodeDeploySampleAppS3BucketName" : "", 4 | "CodeDeploySampleAppS3ObjectKey" : "", 6 | "TimeWindowConfiguration":"{\"window\":{\"opens\" : \"9\",\"closes\" : \"17\"},\"blackDayDates\" : [\"2016-01-01\",\"2016-12-25\"]}", 7 | "YourIP" : "IP address to connect to SSH from. Check http://checkip.amazonaws.com/ to find yours." 8 | } 9 | } -------------------------------------------------------------------------------- /register-time-window.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | var aws = require("aws-sdk"); 16 | var ddb = new aws.DynamoDB.DocumentClient(); 17 | const PIPELINE_APPROVAL_DDB_TABLE = "TimeWindowDemo-PipelineApprovals"; 18 | 19 | exports.handler = (event, context, callback) => { 20 | 21 | if (event.Records[0]) { 22 | var notificationData = JSON.parse(event.Records[0].Sns.Message); 23 | ddb.put({ 24 | TableName: PIPELINE_APPROVAL_DDB_TABLE, 25 | Item: { 26 | ApprovalToken: notificationData.approval.token, 27 | ApprovalContent: notificationData.approval 28 | } 29 | }, function(err, data) { 30 | if (err) { 31 | var message = "Unable to register pipeline approval request. Error JSON:" + JSON.stringify(err); 32 | console.error(message); 33 | callback(error, message); 34 | } else { 35 | var message = "Successfully registered pipeline approval request: " + JSON.stringify(notificationData.approval); 36 | console.log(message); 37 | callback(null, message); 38 | } 39 | }); 40 | } else { 41 | callback(null); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/${GITHUB_ORG}/${GITHUB_REPO}/issues), or [recently closed](https://github.com/${GITHUB_ORG}/${GITHUB_REPO}/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/${GITHUB_ORG}/${GITHUB_REPO}/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/${GITHUB_ORG}/${GITHUB_REPO}/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS CodePipeline Time Windows 2 | 3 | The resources in this repository will help you setup required AWS resources for building time window and black days based approvals in AWS CodePipeline. 4 | 5 | ## Prerequisites 6 | 7 | 1. Create an AWS CodeCommit repository with any name of your preference using AWS console or CLI. This document assumes that the name you chose is `aws-codepipeline-time-windows`. 8 | 2. Clone the content of this repository to AWS CodeCommit repository created in the above step. See this [article](http://docs.aws.amazon.com/codecommit/latest/userguide/how-to-migrate-repository-existing.html) for the details on closing an existing GitHub repositories to AWS CodeCommit. 9 | 3. Download AWS CodeDeploy sample application for Linux using this [link](https://s3.amazonaws.com/aws-codedeploy-us-east-1/samples/latest/SampleApp_Linux.zip). 10 | 4. Upload this application in a version enabled Amazon S3 bucket you own. Note down both the bucket name and object key. You will need in later steps. 11 | 5. Create an Amazon EC2 key pair if you don't have one already. 12 | 13 | ## Steps 14 | Run following steps in the local workspace where GitHub repository was cloned: 15 | 16 | 1. If you chose a different AWS CodeCommit repository name, replace `ParameterValue` in `setup-time-window-resources-stack-parameters.json` file with the name you chose. 17 | 2. Update `time-window-demo-resources-parameters.json` file to replace parameter values: 18 | * `CodeDeploySampleAppS3BucketName`: Amazon S3 bucket name from step 4 in Prerequisites section. 19 | * `CodeDeploySampleAppS3ObjectKey` : The object key from step 4 in Prerequisites section. 20 | * `TimeWindowConfiguration` : Time window configuration which specifies window opening and closing times (UTC), and black day dates in JSON string format. It contains following properties: 21 | * `window` : Specifies opening (`opens`) and closing time (`closes`) of the time window in hours (0-24) in UTC. 22 | * `blackDayDates` : A list of dates in ISO format where no approvals are to be given for the full day. This can include important days where peak traffic is expected. 23 | * `KeyPairName`: Amazon EC2 key pair name. 24 | * `YourIP` : IP address to connect to SSH from. Check http://checkip.amazonaws.com/ to find yours. 25 | 3. Create a new CloudFormation stack using AWS CloudFormation template `setup-time-window-resources-stack.yml` 26 | and parameter file `setup-time-window-resources-stack-parameters.json`. See this [article](https://aws.amazon.com/blogs/devops/passing-parameters-to-cloudformation-stacks-with-the-aws-cli-and-powershell/) for the details on how to pass parameters file using CLI. 27 | 28 | ``` 29 | aws cloudformation create-stack --stack-name SetupTimeWindowDemoResourcesStack --template-body file:///aws-codepipeline-time-windows/setup-time-window-resources-stack.yml --capabilities CAPABILITY_IAM --parameters file:///aws-codepipeline-time-windows/setup-time-window-resources-stack-parameters.json 30 | ``` 31 | 4. Step 3 will create an AWS CodePipeline named `SetupTimeWindowsDemoResources-Pipeline`. This pipeline will use AWS CloudFormation integration with AWS CodePipeline to publish AWS Lambda functions to Amazon S3 and create a new stack using template `time-window-demo-resources.yml` that contains actual AWS resources used in demo including a new AWS CodePipeline named `TimeWindowsDemoPipeline`. 32 | 5. Above step will set up following things: 33 | * A new AWS CodePipeline named `TimeWindowsDemoPipeline` with a stage that contains Approval and AWS CodeDeploy actions. Approval action specifies an Amazon SNS topic to which notifications are sent when this action runs. 34 | * An AWS Lambda function (`register-time-window.js`) is subscribed to this topic which registers this request in an Amazon DynamoDB table. 35 | * AWS Lambda function (`process-time-windows.js`) that runs periodically and scans the table for open approval requests. If the current time is open as per time window configuration specified in `TimeWindowsDemoPipeline` pipeline, it approves the request using AWS CodePipeline API `PutApprovalResult` which allows the pipeline run to proceed to the next AWS CodeDeploy stage. 36 | 37 | ## Cleanup 38 | When no longer required, please remember to delete the stacks using AWS CloudFormation console or CLI to avoid getting charged. 39 | 40 | ## License 41 | This plugin is open sourced and licensed under Apache 2.0. See the LICENSE file for more information. -------------------------------------------------------------------------------- /setup-time-window-resources-stack.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: 'AWS Cloudformation template to help create synthetic tests demo resources.' 3 | 4 | Parameters: 5 | DemoResourcesCodeCommitRepo: 6 | Description: AWS CodeCommit repository name which contains the demo resources 7 | Type: String 8 | Default: "aws-codepipeline-synthetic-tests" 9 | DemoResourcesCodeCommitRepoBranch: 10 | Description: AWS CodeCommit repository branch 11 | Type: String 12 | Default: "master" 13 | 14 | Resources: 15 | 16 | ArtifactStoreBucket: 17 | Type: AWS::S3::Bucket 18 | 19 | Pipeline: 20 | Type: AWS::CodePipeline::Pipeline 21 | Properties: 22 | ArtifactStore: 23 | Location: !Ref 'ArtifactStoreBucket' 24 | Type: S3 25 | Name: "SetupTimeWindowsDemoResources-Pipeline" 26 | RoleArn: !GetAtt [PipelineRole, Arn] 27 | Stages: 28 | - Name: Source 29 | Actions: 30 | - Name: DemoResourcesSource 31 | ActionTypeId: 32 | Category: Source 33 | Owner: AWS 34 | Provider: CodeCommit 35 | Version: '1' 36 | Configuration: 37 | RepositoryName: !Ref DemoResourcesCodeCommitRepo 38 | BranchName: !Ref DemoResourcesCodeCommitRepoBranch 39 | OutputArtifacts: 40 | - Name: DemoArtifacts 41 | RunOrder: '1' 42 | - Name: CloudformationDeploy 43 | Actions: 44 | - Name: CreateDemoResources 45 | ActionTypeId: 46 | Category: Deploy 47 | Owner: AWS 48 | Provider: CloudFormation 49 | Version: '1' 50 | InputArtifacts: 51 | - Name: DemoArtifacts 52 | Configuration: 53 | ActionMode: CREATE_UPDATE 54 | RoleArn: !GetAtt [CloudFormationRole, Arn] 55 | StackName: TimeWindowDemoResources 56 | TemplatePath: DemoArtifacts::time-window-demo-resources.yml 57 | TemplateConfiguration: DemoArtifacts::time-window-demo-resources-parameters.json 58 | ParameterOverrides: | 59 | { 60 | "DemoResourcesS3BucketName" : { "Fn::GetArtifactAtt" : ["DemoArtifacts", "BucketName"]}, 61 | "DemoResourcesS3ObjectKey" : { "Fn::GetArtifactAtt" : ["DemoArtifacts", "ObjectKey"]} 62 | } 63 | Capabilities: CAPABILITY_IAM 64 | RunOrder: '1' 65 | 66 | CloudFormationRole: 67 | Type: AWS::IAM::Role 68 | Properties: 69 | AssumeRolePolicyDocument: 70 | Statement: 71 | - Action: ['sts:AssumeRole'] 72 | Effect: Allow 73 | Principal: 74 | Service: [cloudformation.amazonaws.com] 75 | Version: '2012-10-17' 76 | Path: / 77 | Policies: 78 | - PolicyName: CloudFormationRole 79 | PolicyDocument: 80 | Version: '2012-10-17' 81 | Statement: 82 | - Action: '*' 83 | Effect: Allow 84 | Resource: '*' 85 | 86 | PipelineRole: 87 | Type: AWS::IAM::Role 88 | Properties: 89 | AssumeRolePolicyDocument: 90 | Statement: 91 | - Action: ['sts:AssumeRole'] 92 | Effect: Allow 93 | Principal: 94 | Service: [codepipeline.amazonaws.com] 95 | Version: '2012-10-17' 96 | Path: / 97 | Policies: 98 | - PolicyName: CodePipelineAccess 99 | PolicyDocument: 100 | Version: '2012-10-17' 101 | Statement: 102 | - Effect: Allow 103 | Action: 104 | - "s3:GetObject" 105 | - "s3:PutObject" 106 | Resource: 107 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "ArtifactStoreBucket"}, "/*" ]] 108 | - Effect: "Allow" 109 | Action: 110 | - "s3:ListBucket" 111 | Resource: 112 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "ArtifactStoreBucket"}]] 113 | - Effect: Allow 114 | Action: 115 | - 'cloudformation:CreateStack' 116 | - 'cloudformation:DescribeStacks' 117 | - 'cloudformation:DeleteStack' 118 | - 'cloudformation:UpdateStack' 119 | - 'cloudformation:CreateChangeSet' 120 | - 'cloudformation:ExecuteChangeSet' 121 | - 'cloudformation:DeleteChangeSet' 122 | - 'cloudformation:DescribeChangeSet' 123 | - 'cloudformation:SetStackPolicy' 124 | Resource: 125 | - "Fn::Join": ["", ["arn:aws:cloudformation:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":stack/", "TimeWindowDemoResources"]] 126 | - "Fn::Join": ["", ["arn:aws:cloudformation:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":stack/", "TimeWindowDemoResources/*"]] 127 | - Effect: Allow 128 | Action: 129 | - 'iam:PassRole' 130 | Resource: 131 | - !GetAtt [CloudFormationRole, Arn] 132 | - Effect: Allow 133 | Action: 134 | - "codecommit:GetBranch" 135 | - "codecommit:GetCommit" 136 | - "codecommit:UploadArchive" 137 | - "codecommit:GetUploadArchiveStatus" 138 | - "codecommit:CancelUploadArchive" 139 | Resource: 140 | - "Fn::Join": ["", ["arn:aws:codecommit:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":", {"Ref" : "DemoResourcesCodeCommitRepo"}]] 141 | -------------------------------------------------------------------------------- /process-time-windows.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | var aws = require('aws-sdk'); 16 | var codepipeline = new aws.CodePipeline(); 17 | var ddb = new aws.DynamoDB.DocumentClient(); 18 | 19 | const PIPELINE_APPROVAL_DDB_TABLE = "TimeWindowDemo-PipelineApprovals"; 20 | 21 | exports.handler = (event, context, callback) => { 22 | 23 | var cleanUpFinishedApproval = function(approvalToken) { 24 | return ddb.delete({ 25 | TableName: PIPELINE_APPROVAL_DDB_TABLE, 26 | Key: { 27 | ApprovalToken: approvalToken 28 | } 29 | }).promise(); 30 | }; 31 | 32 | var approveTimeWindow = function(approval) { 33 | var approvalResult = { 34 | pipelineName: approval.pipelineName, 35 | stageName: approval.stageName, 36 | actionName: approval.actionName, 37 | token: approval.token, 38 | result: { 39 | status: 'Approved', 40 | summary: 'Time window open. Action approved at ' + new Date() 41 | } 42 | }; 43 | 44 | console.log("Approving time window for approval token: " + approvalResult.token); 45 | 46 | return new Promise(function(resolve, reject) { 47 | codepipeline.putApprovalResult(approvalResult, function(err, data) { 48 | cleanUpFinishedApproval(approvalResult.token).then(function() { 49 | if (err) { 50 | console.log("Error putting approval result: " + JSON.stringify(err)); 51 | reject(err); 52 | } else { 53 | resolve(data); 54 | } 55 | }, function(err) { 56 | console.log("Error deleting the record: " + JSON.stringify(err)); 57 | reject(err); 58 | }); 59 | }); 60 | }); 61 | }; 62 | 63 | var evaluateTimeWindow = function(approvalToken, timeWindowConfig) { 64 | console.log("Evaluating time window configuration: " + JSON.stringify(timeWindowConfig) + " for approval token: " + approvalToken); 65 | 66 | // All dates and time configurations are assumed to be in UTC. 67 | var now = new Date(); 68 | var isTimeWindowOpen = true; 69 | 70 | // In real world scenarios, this data should be retrieved 71 | // dynamically from a central service. 72 | if (timeWindowConfig.blackDayDates) { 73 | timeWindowConfig.blackDayDates.forEach(function(date) { 74 | var blackDayDate = new Date(date); 75 | if (blackDayDate.getUTCFullYear() === now.getUTCFullYear() && blackDayDate.getUTCMonth() === now.getUTCMonth() && blackDayDate.getUTCDate() === now.getUTCDate()) { 76 | console.log("Time window is closed due to black day: " + date + " for approval token: " + approvalToken); 77 | isTimeWindowOpen = false; 78 | } 79 | }); 80 | } else if (timeWindowConfig.window) { 81 | if (now.getUTCHours() < timeWindowConfig.window.opens || now.getUTCHours() > timeWindowConfig.window.closes) { 82 | console.log("Time window is closed because current time falls outside of open window configuration for approval token: " + approvalToken); 83 | isTimeWindowOpen = false; 84 | } 85 | } else { 86 | console.log("There is no time window configuration specified. Considering current time window as open."); 87 | } 88 | return isTimeWindowOpen; 89 | }; 90 | 91 | var processRecord = function(record) { 92 | var timeWindowConfig = JSON.parse(record.ApprovalContent.customData); 93 | if (evaluateTimeWindow(record.ApprovalToken, timeWindowConfig)) { 94 | return approveTimeWindow(record.ApprovalContent); 95 | } else { 96 | return Promise.resolve(); 97 | }; 98 | }; 99 | 100 | var processRecords = function(data) { 101 | var processRecordPromises = []; 102 | 103 | data.Items.forEach(function(record) { 104 | processRecordPromises.push(processRecord(record)); 105 | }); 106 | 107 | return new Promise(function(resolve, reject) { 108 | Promise.all(processRecordPromises) 109 | .then(function() { 110 | // continue scanning if we have more records 111 | if (typeof data.LastEvaluatedKey != "undefined") { 112 | console.log("Scanning for more..."); 113 | resolve(scanAndProcessRecords(data.lastEvaluatedKey)); 114 | } else { 115 | resolve(); 116 | } 117 | }).catch(function(err) { 118 | reject(err); 119 | }); 120 | }); 121 | }; 122 | 123 | var scanAndProcessRecords = function (lastEvaluatedKey) { 124 | return ddb.scan({ 125 | TableName: PIPELINE_APPROVAL_DDB_TABLE, 126 | ExclusiveStartKey: lastEvaluatedKey 127 | }).promise() 128 | .then(function(data) { 129 | return processRecords(data); 130 | }).catch(function(err) { 131 | console.log("Error processing time window approval requests. Error: " + JSON.stringify(err)); 132 | return Promise.reject(err); 133 | }); 134 | } 135 | 136 | scanAndProcessRecords() 137 | .then(function() { 138 | callback(null, "Successfully processed all time window approval requests."); 139 | }).catch(function(err) { 140 | console.log("Error processing time window approval requests. Error: " + JSON.stringify(err)); 141 | callback(err, "Error processing time window approval requests."); 142 | }); 143 | }; 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /time-window-demo-resources.yml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | Description: "AWS CloudFormation template which contains the resources for synthetic tests demo." 3 | Parameters: 4 | DemoResourcesS3BucketName: 5 | Description: Amazon S3 bucket where demo resources zip file was uploaded to. 6 | Type: String 7 | DemoResourcesS3ObjectKey: 8 | Description: Amazon S3 object key with which demo resources zip file was uploaded to. 9 | Type: String 10 | CodeDeploySampleAppS3BucketName: 11 | Description: Amazon S3 bucket which contains AWS CodeDeploy sample app. 12 | Type: String 13 | CodeDeploySampleAppS3ObjectKey: 14 | Description: Amazon S3 object key of AWS CodeDeploy sample app. 15 | Type: String 16 | TimeWindowConfiguration: 17 | Description: JSON object which contains time windows configuration. 18 | Type: String 19 | KeyPairName: 20 | Description: The name of an existing Amazon EC2 key pair to enable SSH or RDP access. 21 | to the instances. 22 | Type: String 23 | MinLength: '1' 24 | MaxLength: '255' 25 | AllowedPattern: "[\\x20-\\x7E]*" 26 | ConstraintDescription: Can contain only ASCII characters. 27 | YourIP: 28 | Description: "IP address to connect to SSH from. Check http://checkip.amazonaws.com/ to find yours." 29 | Type: String 30 | Default: "0.0.0.0/0" 31 | 32 | Mappings: 33 | RegionOS2AMI: 34 | us-east-1: 35 | Linux: ami-7c807d14 36 | us-west-2: 37 | Linux: ami-1b3b462b 38 | eu-west-1: 39 | Linux: ami-672ce210 40 | ap-southeast-2: 41 | Linux: ami-6bf99c51 42 | ap-southeast-1: 43 | Linux: ami-c9b572aa 44 | us-west-1: 45 | Linux: ami-d5ea86b5 46 | eu-central-1: 47 | Linux: ami-ae221fb3 48 | ap-northeast-1: 49 | Linux: ami-25dd9324 50 | sa-east-1: 51 | Linux: ami-d412aab8 52 | 53 | 54 | Resources: 55 | # The instance will use this role to access S3 56 | EC2Role: 57 | Type: "AWS::IAM::Role" 58 | Properties: 59 | AssumeRolePolicyDocument: 60 | Statement: 61 | - Sid: "" 62 | Effect: "Allow" 63 | Principal: 64 | Service: 65 | - "ec2.amazonaws.com" 66 | Action: "sts:AssumeRole" 67 | Policies: 68 | - PolicyName: "s3" 69 | PolicyDocument: 70 | Version: "2012-10-17" 71 | Statement: 72 | - Effect: "Allow" 73 | Action: 74 | - "s3:GetObject" 75 | Resource: 76 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "ArtifactStoreBucket"}, "/*" ]] 77 | - "Fn::Join": ["", ["arn:aws:s3:::aws-codedeploy-", {"Ref": "AWS::Region"}, "/*" ]] 78 | 79 | EC2InstanceProfile: 80 | Type: "AWS::IAM::InstanceProfile" 81 | Properties: 82 | Roles: 83 | - Ref: "EC2Role" 84 | 85 | SecurityGroup: 86 | Type: AWS::EC2::SecurityGroup 87 | Properties: 88 | GroupDescription: "Enable HTTP access via port 80 and SSH access." 89 | SecurityGroupIngress: 90 | - IpProtocol: tcp 91 | FromPort: "80" 92 | ToPort: "80" 93 | CidrIp: "0.0.0.0/0" 94 | - IpProtocol: tcp 95 | FromPort: 22 96 | ToPort: 22 97 | CidrIp: !Ref YourIP 98 | 99 | LaunchConfiguration: 100 | Type: "AWS::AutoScaling::LaunchConfiguration" 101 | Properties: 102 | IamInstanceProfile: {"Ref": "EC2InstanceProfile"} 103 | ImageId: { "Fn::FindInMap": ["RegionOS2AMI", { "Ref" : "AWS::Region" } , "Linux"]} 104 | InstanceType: t1.micro 105 | KeyName: 106 | !Ref KeyPairName 107 | SecurityGroups: 108 | - {"Ref": "SecurityGroup"} 109 | UserData: 110 | "Fn::Base64": 111 | "Fn::Join": 112 | - "" 113 | - [ 114 | "#!/bin/bash -xe\n", 115 | 116 | "/usr/bin/yum -y update\n", 117 | "/usr/bin/yum install -y ruby\n", 118 | "/usr/bin/yum install -y aws-cli\n", 119 | "cd /home/ec2-user/\n", 120 | "/usr/bin/aws s3 cp s3://aws-codedeploy-", { "Ref" : "AWS::Region" }, "/latest/install . --region ", { "Ref" : "AWS::Region" }, "\n", 121 | "/bin/chmod +x ./install\n", 122 | "./install auto\n" 123 | ] 124 | 125 | CodeDeployTrustRole: 126 | Type: "AWS::IAM::Role" 127 | Properties: 128 | AssumeRolePolicyDocument: 129 | Statement: 130 | - Sid: "" 131 | Effect: "Allow" 132 | Principal: 133 | Service: 134 | - "codedeploy.amazonaws.com" 135 | Action: "sts:AssumeRole" 136 | ManagedPolicyArns: 137 | - "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole" 138 | Path: "/" 139 | 140 | 141 | CodeDeployApplication: 142 | Type: "AWS::CodeDeploy::Application" 143 | 144 | # Autoscaling groups 145 | AutoscalingGroup: 146 | Type: "AWS::AutoScaling::AutoScalingGroup" 147 | Properties: 148 | AvailabilityZones: { "Fn::GetAZs" : { "Ref" : "AWS::Region" } } 149 | DesiredCapacity: 1 150 | LaunchConfigurationName: {"Ref": "LaunchConfiguration"} 151 | MinSize: "1" 152 | MaxSize: "3" 153 | 154 | CodeDeployDeploymentGroup: 155 | Type: "AWS::CodeDeploy::DeploymentGroup" 156 | Properties: 157 | ApplicationName: {"Ref": "CodeDeployApplication"} 158 | ServiceRoleArn: {"Fn::GetAtt" : ["CodeDeployTrustRole","Arn"]} 159 | AutoScalingGroups: 160 | - {"Ref": "AutoscalingGroup"} 161 | 162 | ArtifactStoreBucket: 163 | Type: AWS::S3::Bucket 164 | 165 | Pipeline: 166 | Type: AWS::CodePipeline::Pipeline 167 | Properties: 168 | ArtifactStore: 169 | Location: !Ref 'ArtifactStoreBucket' 170 | Type: S3 171 | Name: "TimeWindowsDemoPipeline" 172 | RoleArn: !GetAtt [PipelineRole, Arn] 173 | Stages: 174 | - Name: Source 175 | Actions: 176 | - Name: S3Source 177 | ActionTypeId: 178 | Category: Source 179 | Owner: AWS 180 | Provider: S3 181 | Version: '1' 182 | Configuration: 183 | S3Bucket: !Ref CodeDeploySampleAppS3BucketName 184 | S3ObjectKey: !Ref CodeDeploySampleAppS3ObjectKey 185 | OutputArtifacts: 186 | - Name: CodeDeployArtifacts 187 | RunOrder: '1' 188 | - Name: Deploy 189 | Actions: 190 | - Name: TimeWindowApproval 191 | ActionTypeId: 192 | Category: Approval 193 | Owner: AWS 194 | Provider: Manual 195 | Version: '1' 196 | Configuration: 197 | CustomData: !Ref TimeWindowConfiguration 198 | NotificationArn: !Ref PipelineApprovalSNSNotification 199 | RunOrder: '1' 200 | - Name: CodeDeployApp 201 | ActionTypeId: 202 | Category: Deploy 203 | Owner: AWS 204 | Provider: CodeDeploy 205 | Version: '1' 206 | InputArtifacts: 207 | - Name: CodeDeployArtifacts 208 | Configuration: 209 | ApplicationName: !Ref CodeDeployApplication 210 | DeploymentGroupName: !Ref CodeDeployDeploymentGroup 211 | RunOrder: '2' 212 | 213 | PipelineRole: 214 | Type: AWS::IAM::Role 215 | Properties: 216 | AssumeRolePolicyDocument: 217 | Statement: 218 | - Action: ['sts:AssumeRole'] 219 | Effect: Allow 220 | Principal: 221 | Service: [codepipeline.amazonaws.com] 222 | Version: '2012-10-17' 223 | Path: / 224 | Policies: 225 | - PolicyName: CodePipelineAccess 226 | PolicyDocument: 227 | Version: '2012-10-17' 228 | Statement: 229 | - Effect: Allow 230 | Action: 231 | - "s3:GetObject" 232 | - "s3:GetObjectVersion" 233 | Resource: 234 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "CodeDeploySampleAppS3BucketName"}, "/*"]] 235 | - Effect: Allow 236 | Action: 237 | - "s3:GetBucketVersioning" 238 | Resource: 239 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "CodeDeploySampleAppS3BucketName"}]] 240 | - Effect: Allow 241 | Action: 242 | - "s3:GetObject" 243 | - "s3:PutObject" 244 | Resource: 245 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "ArtifactStoreBucket"}, "/*" ]] 246 | - Effect: "Allow" 247 | Action: 248 | - "s3:ListBucket" 249 | Resource: 250 | - "Fn::Join": ["", ["arn:aws:s3:::", {"Ref": "ArtifactStoreBucket"}]] 251 | - Effect: Allow 252 | Action: 253 | - "codedeploy:CreateDeployment" 254 | - "codedeploy:GetApplicationRevision" 255 | - "codedeploy:GetDeployment" 256 | - "codedeploy:GetDeploymentConfig" 257 | - "codedeploy:RegisterApplicationRevision" 258 | Resource: "*" 259 | - Effect: Allow 260 | Action: 261 | - "sns:Publish" 262 | Resource: !Ref PipelineApprovalSNSNotification 263 | 264 | PipelineAppovalTable: 265 | Type: "AWS::DynamoDB::Table" 266 | Properties: 267 | AttributeDefinitions: 268 | - AttributeName: "ApprovalToken" 269 | AttributeType: "S" 270 | KeySchema: 271 | - AttributeName: "ApprovalToken" 272 | KeyType: "HASH" 273 | ProvisionedThroughput: 274 | ReadCapacityUnits: "5" 275 | WriteCapacityUnits: "5" 276 | TableName: "TimeWindowDemo-PipelineApprovals" 277 | 278 | RegisterTimeWindowLambdaFunctionExecutionRole: 279 | Type: AWS::IAM::Role 280 | Properties: 281 | AssumeRolePolicyDocument: 282 | Statement: 283 | - Action: ['sts:AssumeRole'] 284 | Effect: Allow 285 | Principal: 286 | Service: [lambda.amazonaws.com] 287 | Version: '2012-10-17' 288 | Path: / 289 | Policies: 290 | - PolicyName: CodePipelineAccess 291 | PolicyDocument: 292 | Version: '2012-10-17' 293 | Statement: 294 | - Effect: Allow 295 | Action: 296 | - "dynamodb:PutItem" 297 | Resource: 298 | - "Fn::Join": ["", ["arn:aws:dynamodb:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":table/", {"Ref": "PipelineAppovalTable"}]] 299 | - Effect: Allow 300 | Action: 301 | - "logs:CreateLogGroup" 302 | - "logs:CreateLogStream" 303 | - "logs:PutLogEvents" 304 | Resource: "arn:aws:logs:*:*:*" 305 | 306 | RegisterTimeWindowLambdaFunction: 307 | Type: AWS::Lambda::Function 308 | Properties: 309 | Handler: "register-time-window.handler" 310 | Role: !GetAtt [RegisterTimeWindowLambdaFunctionExecutionRole, Arn] 311 | Code: 312 | S3Bucket: !Ref DemoResourcesS3BucketName 313 | S3Key: !Ref DemoResourcesS3ObjectKey 314 | Runtime: nodejs4.3 315 | Timeout: 25 316 | 317 | RegisterTimeWindowInvokePermission: 318 | Type: AWS::Lambda::Permission 319 | Properties: 320 | FunctionName: { "Fn::GetAtt" : ["RegisterTimeWindowLambdaFunction", "Arn"]} 321 | Action: "lambda:InvokeFunction" 322 | Principal: "sns.amazonaws.com" 323 | SourceArn: !Ref PipelineApprovalSNSNotification 324 | 325 | PipelineApprovalSNSNotification: 326 | Type: AWS::SNS::Topic 327 | Properties: 328 | Subscription: 329 | - Protocol: "lambda" 330 | Endpoint: { "Fn::GetAtt" : ["RegisterTimeWindowLambdaFunction", "Arn"]} 331 | 332 | ProcessTimeWindowsFunctionExecutionRole: 333 | Type: AWS::IAM::Role 334 | Properties: 335 | AssumeRolePolicyDocument: 336 | Statement: 337 | - Action: ['sts:AssumeRole'] 338 | Effect: Allow 339 | Principal: 340 | Service: [lambda.amazonaws.com] 341 | Version: '2012-10-17' 342 | Path: / 343 | Policies: 344 | - PolicyName: CloudWatchAccess 345 | PolicyDocument: 346 | Version: '2012-10-17' 347 | Statement: 348 | - Effect: Allow 349 | Action: 350 | - "logs:CreateLogGroup" 351 | - "logs:CreateLogStream" 352 | - "logs:PutLogEvents" 353 | Resource: "arn:aws:logs:*:*:*" 354 | - Effect: Allow 355 | Action: 356 | - "codepipeline:PutApprovalResult" 357 | Resource: 358 | - "Fn::Join": ["", ["arn:aws:codepipeline:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":", {"Ref": "Pipeline"}, "/*"]] 359 | - Effect: Allow 360 | Action: 361 | - "dynamodb:Scan" 362 | - "dynamodb:DeleteItem" 363 | Resource: 364 | - "Fn::Join": ["", ["arn:aws:dynamodb:", { "Ref" : "AWS::Region" }, ":", { "Ref" : "AWS::AccountId" }, ":table/", {"Ref": "PipelineAppovalTable"}]] 365 | 366 | ProcessTimeWindowsLambdaFunction: 367 | Type: AWS::Lambda::Function 368 | Properties: 369 | Handler: "process-time-windows.handler" 370 | Role: !GetAtt [ProcessTimeWindowsFunctionExecutionRole, Arn] 371 | Code: 372 | S3Bucket: !Ref DemoResourcesS3BucketName 373 | S3Key: !Ref DemoResourcesS3ObjectKey 374 | Runtime: "nodejs4.3" 375 | Timeout: "25" 376 | 377 | ProcessTimeWindowsInvokePermission: 378 | Type: AWS::Lambda::Permission 379 | Properties: 380 | FunctionName: !GetAtt [ProcessTimeWindowsLambdaFunction, Arn] 381 | Action: "lambda:InvokeFunction" 382 | Principal: "events.amazonaws.com" 383 | SourceArn: !GetAtt [ProcessTimeWindowsScheduleTrigger, Arn] 384 | 385 | ProcessTimeWindowsScheduleTrigger: 386 | Type: AWS::Events::Rule 387 | Properties: 388 | ScheduleExpression: rate(1 minute) 389 | State: ENABLED 390 | Targets: 391 | - Arn: !GetAtt [ProcessTimeWindowsLambdaFunction, Arn] 392 | Id: "InvokeProcessTimeWindows" 393 | --------------------------------------------------------------------------------