├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── browser ├── StepFunctions.json ├── favicon.ico ├── gov-cloud-import-s3sync-com-bucket-policy.json ├── gov-cloud-import-s3sync-gov-bucket-policy.json ├── images │ ├── ImageImportFinal.gif │ ├── ImportS3Final.gif │ ├── StepFunctionOverview.png │ ├── aws.png │ ├── cloudwatch.png │ ├── cloudwatchSchedule.png │ ├── commercial-resources.png │ ├── diagramImage.png │ ├── diagramOverview.png │ ├── diagramS3.png │ ├── gov-cloud-import-install.gif │ ├── govcloud-resources.png │ ├── govcloud.png │ ├── overviewDiagram.png │ ├── ssm.png │ ├── trouble1.png │ └── trouble2.png ├── importImage.js ├── importS3.js ├── scripts │ ├── aws-sdk-2.192.0.min.js │ ├── gov-cloud-import-browser.js │ ├── jquery-2.2.4.min.js │ └── jquery.mobile-1.4.5.min.js └── themes │ ├── amzn.min.css │ ├── images │ ├── ajax-loader.gif │ ├── aws.png │ ├── diagram.png │ ├── govcloud.png │ └── icons-png │ │ ├── action-black.png │ │ ├── action-white.png │ │ ├── alert-black.png │ │ ├── alert-white.png │ │ ├── arrow-d-black.png │ │ ├── arrow-d-l-black.png │ │ ├── arrow-d-l-white.png │ │ ├── arrow-d-r-black.png │ │ ├── arrow-d-r-white.png │ │ ├── arrow-d-white.png │ │ ├── arrow-l-black.png │ │ ├── arrow-l-white.png │ │ ├── arrow-r-black.png │ │ ├── arrow-r-white.png │ │ ├── arrow-u-black.png │ │ ├── arrow-u-l-black.png │ │ ├── arrow-u-l-white.png │ │ ├── arrow-u-r-black.png │ │ ├── arrow-u-r-white.png │ │ ├── arrow-u-white.png │ │ ├── audio-black.png │ │ ├── audio-white.png │ │ ├── back-black.png │ │ ├── back-white.png │ │ ├── bars-black.png │ │ ├── bars-white.png │ │ ├── bullets-black.png │ │ ├── bullets-white.png │ │ ├── calendar-black.png │ │ ├── calendar-white.png │ │ ├── camera-black.png │ │ ├── camera-white.png │ │ ├── carat-d-black.png │ │ ├── carat-d-white.png │ │ ├── carat-l-black.png │ │ ├── carat-l-white.png │ │ ├── carat-r-black.png │ │ ├── carat-r-white.png │ │ ├── carat-u-black.png │ │ ├── carat-u-white.png │ │ ├── check-black.png │ │ ├── check-white.png │ │ ├── clock-black.png │ │ ├── clock-white.png │ │ ├── cloud-black.png │ │ ├── cloud-white.png │ │ ├── comment-black.png │ │ ├── comment-white.png │ │ ├── delete-black.png │ │ ├── delete-white.png │ │ ├── edit-black.png │ │ ├── edit-white.png │ │ ├── eye-black.png │ │ ├── eye-white.png │ │ ├── forbidden-black.png │ │ ├── forbidden-white.png │ │ ├── forward-black.png │ │ ├── forward-white.png │ │ ├── gear-black.png │ │ ├── gear-white.png │ │ ├── grid-black.png │ │ ├── grid-white.png │ │ ├── heart-black.png │ │ ├── heart-white.png │ │ ├── home-black.png │ │ ├── home-white.png │ │ ├── info-black.png │ │ ├── info-white.png │ │ ├── location-black.png │ │ ├── location-white.png │ │ ├── lock-black.png │ │ ├── lock-white.png │ │ ├── mail-black.png │ │ ├── mail-white.png │ │ ├── minus-black.png │ │ ├── minus-white.png │ │ ├── navigation-black.png │ │ ├── navigation-white.png │ │ ├── phone-black.png │ │ ├── phone-white.png │ │ ├── plus-black.png │ │ ├── plus-white.png │ │ ├── power-black.png │ │ ├── power-white.png │ │ ├── recycle-black.png │ │ ├── recycle-white.png │ │ ├── refresh-black.png │ │ ├── refresh-white.png │ │ ├── search-black.png │ │ ├── search-white.png │ │ ├── shop-black.png │ │ ├── shop-white.png │ │ ├── star-black.png │ │ ├── star-white.png │ │ ├── tag-black.png │ │ ├── tag-white.png │ │ ├── user-black.png │ │ ├── user-white.png │ │ ├── video-black.png │ │ └── video-white.png │ ├── jquery.mobile.icons.min.css │ └── jquery.mobile.structure-1.4.5.min.css ├── cloudformation ├── gov-cloud-import-commercial.json ├── gov-cloud-import-govcloud-east.json ├── gov-cloud-import-govcloud-vmimport.json ├── gov-cloud-import-govcloud-west.json └── gov-cloud-import-govcloud.json ├── ec2 ├── gov-cloud-import-ec2prep.sh ├── gov-cloud-import.js ├── mountBucket.sh └── umountBucket.sh ├── gov-cloud-import-install.sh ├── index.html └── lambda ├── appStatus └── index.js ├── cleanUp └── index.js ├── ec2Run └── index.js ├── importImage └── index.js ├── importImageStatus └── index.js ├── initS3Sync └── index.js ├── initStepFunction └── index.js ├── listComBuckets └── index.js ├── listGovBuckets └── index.js ├── makeVolume └── index.js ├── makeVolumeStatus └── index.js ├── moveImage └── index.js ├── moveStatus └── index.js ├── removeS3Image └── index.js ├── snsPublish └── index.js └── snsSubscribe └── index.js /.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 you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 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 | -------------------------------------------------------------------------------- /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/awslabs/aws-gov-cloud-import/issues), or [recently closed](https://github.com/awslabs/aws-gov-cloud-import/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/awslabs/aws-gov-cloud-import/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/awslabs/aws-gov-cloud-import/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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AWS Gov Cloud Import 2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GovCloud Import Tool 2 | AWS has multiple Identity partitions; AWS, AWS GovCloud (US), and AWS China. You can see these represented in their ARNs. Respectively; arn:aws, arn:aws-us-gov, arn:aws-cn. For security, services like Amazon Simple Storage Service (Amazon S3) do not have access to credentials beyond their boundary, which can make it difficult to transfer information from inside one Identity boundary to another. 3 |

4 | With the tool GovCloud Import Tool, you have a web-based UI that allows you to import an AMI or Snapshot ID from Commercial to GovCloud (up to 100GiB). Within the same tool you can input a Commercial S3 bucket and a GovCloud Destination Bucket and perform one way synchronizations(up to 1TiB). 5 | ![Overview Diagram](https://github.com/awslabs/aws-gov-cloud-import/raw/master/browser/images/diagramOverview.png) 6 | 7 |
8 | 9 | ## Installation 10 | The installation script deploys two CloudFormation Templates, one in Commercial and one in GovCloud. In GovCloud, an S3 Bucket(for Importing Images only) and IAM resources to make the necessary API Calls for importing. In Commercial, we deploy Step Functions, EC2, Lambda, SSM, S3, and SNS. Step Functions will control the workflow as well as show progress. EC2 Worker is used for the actual transfer of images or synchronization of buckets. Lambda is used for calling all other functions in the workflow. SSM parameter store securely keeps sensitive keys and other parameters necessary the overall application. S3 houses the user interface to gov-cloud-import. SNS is used for notification at the end of an import. 11 | 12 | Prior to install, please remove vmimport role from GovCloud if currently created. Script should be able to install on any BASH shell(tested on MacOS and Amzn Linux). You'll need API Keys with admin privileges for Commercial and GovCloud. Installation takes a few minutes and will give progress during that time. When finished, it will give you a URL to access the Web UI. Please see sample install below. 13 | Please note the errors. These are from checking if cloudformation stack exists. If they don't exist, the AWS CLI doesn't allow to suppress the errors. 14 | ``` 15 | git clone https://github.com/awslabs/aws-gov-cloud-import.git 16 | cd aws-gov-cloud-import 17 | chmod +x gov-cloud-import-install.sh 18 | ./gov-cloud-import-install.sh 19 | ``` 20 | ![Install](https://github.com/awslabs/aws-gov-cloud-import/raw/master/browser/images/gov-cloud-import-install.gif) 21 |
22 | 23 | ## Usage gov-cloud-import-image 24 | Browse to the URL output by the installation script. You need keys that allow for lambda-invoke, lambda-list, and e2-describeRegions. Browse to the API Key page, enter keys, and click validate. Then click notifications if you like to add your email or phone(sms) alerts. 25 | 26 | For importing images, input an AMI or Snapshot ID. The AMI or Snapshot must be owned by the account. While the input field checks the format of the string, it does not check permissions. Select the OS and Source/Destination Regions. Verify your input with a dialog box and import. You'll receive a State Machine Execution ARN to the import job. Click to open in a new tab and watch process. 27 | 28 | ![Import Image](https://github.com/awslabs/aws-gov-cloud-import/raw/master/browser/images/ImageImportFinal.gif) 29 |
30 | 31 | ## Usage gov-cloud-import-s3 32 | For importing s3 buckets, you must give gov-cloud-import permissions to the source and destination bucket. Please download the sample policy and modify to your needs. Then click the link for Permissions and install your policy inline to the gov-cloud-import-ec2role. Then click on the 2nd link for permissions and install your 2nd inline policy for user gov-cloud-import-user. 33 | 34 | Once permissions are set, proceed to the Import S3 page. From first the drop down menu, select your source. From the second drop down menu, select your destination. Verify your input and submit. 35 | 36 | ![Import S3](https://github.com/awslabs/aws-gov-cloud-import/raw/master/browser/images/ImportS3Final.gif) 37 |
38 | 39 | ## Usage gov-cloud-import and AWS SDK 40 | If you like to build gov-cloud-import into your application, here are sample calls in Javascript that can be used with AWS SDK to start an import. Be sure to find the correct FunctionName as cloudformation suffixes randoms characters. These can be sent to us-west-2 or us-east-2 depending on which GovCloud Region (and adjacent Commercial Region) you have installed and will import images. 41 | 42 | For importing Images: 43 | ``` 44 | function initImportImage(lambda){ 45 | return new Promise((resolve, reject) => { 46 | let image = 'ami-1234abcd' /*AMI or Snapshot ID*/ 47 | let os = 'Windows'/*Windows or Linux*/ 48 | let region = 'us-east-1'/*Source Commercial Region */ 49 | //Params for Lambda invoke 50 | let params = { 51 | FunctionName : initStepFunction, 52 | InvocationType : 'RequestResponse', 53 | LogType : 'Tail', 54 | Payload : JSON.stringify({"image": image, "region": region, "os": os}) 55 | }; 56 | // Call the Lambda function 57 | lambda.invoke(params, function(err, data) { 58 | if (err) { 59 | reject(err); 60 | } else { 61 | resolve(data); 62 | } 63 | }); 64 | }); 65 | } 66 | ``` 67 | 68 | For Importing from S3: 69 | ``` 70 | function initS3Import(lambda){ 71 | return new Promise((resolve, reject) => { 72 | let comBucket = 'my-commercial-bucket' /*Source Commercial Bucket*/ 73 | let govBucket = 'my-govcloud-bucket' /*Destination GovCloud Bucket*/ 74 | //Params for Lambda invoke 75 | let params = { 76 | FunctionName : initS3Sync, 77 | InvocationType : 'RequestResponse', 78 | LogType : 'Tail', 79 | Payload : JSON.stringify({"source": comBucket, "dest": govBucket}) 80 | }; 81 | // Call the Lambda function 82 | lambda.invoke(params, function(err, data) { 83 | if (err) { 84 | reject(err); 85 | } else { 86 | resolve(data); 87 | } 88 | }); 89 | }); 90 | } 91 | ``` 92 | 93 | For HTTPS Success/Failed Callbacks: 94 | ``` 95 | function initSNSSubscribe(lambda){ 96 | return new Promise((resolve, reject) => { 97 | let snsProtocol = 'https', 98 | let snsTopic = ''; 99 | let snsRegion = '';; 100 | let snsEndpoint = 'https://my.application.com/some/ping/back'; 101 | //Params for Lambda invoke 102 | let params = { 103 | FunctionName : snsSubscribe, 104 | InvocationType : 'RequestResponse', 105 | LogType : 'Tail', 106 | Payload : JSON.stringify({ 107 | "protocol": snsProtocol, 108 | "topic": snsTopic, 109 | "endpoint": snsEndpoint, 110 | "region": snsRegion 111 | }) 112 | }; 113 | // Call the Lambda function 114 | lambda.invoke(params, function(err, data) { 115 | if (err) { 116 | reject(err) 117 | } else { 118 | resolve(data); 119 | } 120 | }); 121 | }); 122 | } 123 | ``` 124 | 125 | Success or Failure notifications for Images: 126 | ``` 127 | { 128 | "sourceRegion": "us-west-2", 129 | "source": "ami-0123abdc", 130 | "destRegion": "us-gov-west-1", 131 | "dest": "ami-wxyz9876" 132 | } 133 | 134 | { 135 | "sourceRegion": "us-west-2", 136 | "source": "ami-0123abdc", 137 | "destRegion": "us-gov-west-1", 138 | "dest": "failed" 139 | } 140 | 141 | ``` 142 | 143 | Success or Failure notifications for S3: 144 | ``` 145 | { 146 | "sourceRegion": "us-west-2", 147 | "source": "my-aws-bucket", 148 | "destRegion": "us-gov-west-1", 149 | "dest": "my-govcloud-bucket" 150 | } 151 | 152 | { 153 | "sourceRegion": "us-west-2", 154 | "source": "my-aws-bucket", 155 | "destRegion": "us-gov-west-1", 156 | "dest": "failed" 157 | } 158 | 159 | ``` 160 | ## Scheduling S3 Synchronization 161 | With the use of scheduled CloudWatch events rules, you can trigger the initStepFuntion Lambda based on whatever schedule you need. Take note, you want to make sure your schedule interval is greater than the time it takes to synchronize the bucket. While it can run in parallel, it will decrease performance. Below shows an example input. 162 | 163 | ``` 164 | {"sourceBucket":"source-bucket", "destBucket":"destination bucket"} 165 | ``` 166 | 167 | ![CloudWatch](https://github.com/awslabs/aws-gov-cloud-import/raw/master/browser/images/cloudwatchSchedule.png) 168 | -------------------------------------------------------------------------------- /browser/StepFunctions.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "A state machine that launches an AMI, stops it, detaches volume, and terminates.", 3 | "StartAt": "EC2 Prep", 4 | "States": { 5 | "EC2 Prep": { 6 | "Type": "Task", 7 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-ec2Run-1GJXENX8OBBGS", 8 | "Next": "Move Image", 9 | "InputPath": "$", 10 | "ResultPath": "$.ec2" 11 | }, 12 | "Move Image": { 13 | "Type": "Task", 14 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-moveImage-12XRM1VKYY6ZO", 15 | "Next": "Wait Move", 16 | "InputPath": "$", 17 | "ResultPath": "$.imageNew" 18 | }, 19 | "Wait Move": { 20 | "Type": "Wait", 21 | "SecondsPath": "$.waitMove", 22 | "Next": "Move Image Status" 23 | }, 24 | "Move Image Status": { 25 | "Type": "Task", 26 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-moveStatus-F32L74F9S4DQ", 27 | "Next": "Move Complete?", 28 | "ResultPath": "$.status" 29 | }, 30 | "Move Complete?": { 31 | "Type": "Choice", 32 | "Choices": [ 33 | { 34 | "Variable": "$.status", 35 | "StringEquals": "failed", 36 | "Next": "Move Failed" 37 | }, 38 | { 39 | "Variable": "$.status", 40 | "StringEquals": "pending", 41 | "Next": "Wait Move" 42 | }, 43 | { 44 | "Variable": "$.status", 45 | "StringEquals": "available", 46 | "Next": "Make Volume" 47 | } 48 | ], 49 | "Default": "Move Failed" 50 | }, 51 | "Make Volume": { 52 | "Type": "Task", 53 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-makeVolume-IC091Q6M1TWK", 54 | "InputPath": "$", 55 | "ResultPath": "$.volumeId", 56 | "Next": "Make Volume Status" 57 | }, 58 | "Wait Volume": { 59 | "Type": "Wait", 60 | "SecondsPath": "$.waitVol", 61 | "Next": "Make Volume Status" 62 | }, 63 | "Make Volume Status": { 64 | "Type": "Task", 65 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-makeVolumeStatus-1DD9RYHK6ZZHN", 66 | "InputPath": "$", 67 | "ResultPath": "$.volume", 68 | "Next": "Volume Ready?" 69 | }, 70 | "Volume Ready?": { 71 | "Type": "Choice", 72 | "Choices": [ 73 | { 74 | "Variable": "$.volume.status", 75 | "StringEquals": "failed", 76 | "Next": "Volume Failed" 77 | }, 78 | { 79 | "Variable": "$.volume.status", 80 | "StringEquals": "creating", 81 | "Next": "Wait Volume" 82 | }, 83 | { 84 | "Variable": "$.volume.status", 85 | "StringEquals": "available", 86 | "Next": "Instance Status" 87 | } 88 | ], 89 | "Default": "Volume Failed" 90 | }, 91 | "Wait Instance": { 92 | "Type": "Wait", 93 | "SecondsPath": "$.waitEc2", 94 | "Next": "Instance Status" 95 | }, 96 | "Instance Status": { 97 | "Type": "Task", 98 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-ec2Run-1GJXENX8OBBGS", 99 | "InputPath": "$", 100 | "ResultPath": "$.status", 101 | "Next": "EC2 Ready?" 102 | }, 103 | "EC2 Ready?": { 104 | "Type": "Choice", 105 | "Choices": [ 106 | { 107 | "Variable": "$.status", 108 | "StringEquals": "stopped", 109 | "Next": "EC2 Failed" 110 | }, 111 | { 112 | "Variable": "$.status", 113 | "StringEquals": "pending", 114 | "Next": "Wait Instance" 115 | }, 116 | { 117 | "Variable": "$.status", 118 | "StringEquals": "running", 119 | "Next": "Clean Up" 120 | } 121 | ], 122 | "Default": "EC2 Failed" 123 | }, 124 | "Clean Up": { 125 | "Type": "Task", 126 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-cleanUp-12BKNLGY1AI74", 127 | "InputPath": "$", 128 | "ResultPath": "$.cleanUp", 129 | "Next": "S3 Copy Status" 130 | }, 131 | "Wait S3 Copy": { 132 | "Type": "Wait", 133 | "SecondsPath": "$.waitMove", 134 | "Next": "S3 Copy Status" 135 | }, 136 | "S3 Copy Status": { 137 | "Type": "Task", 138 | "Resource": "arn:aws:states:us-west-2:968854541546:activity:ec2s3Copy", 139 | "TimeoutSeconds": 7200, 140 | "HeartbeatSeconds": 300, 141 | "InputPath": "$", 142 | "ResultPath": "$.s3Status", 143 | "Next": "S3 Copy Finish?" 144 | }, 145 | "S3 Copy Finish?": { 146 | "Type": "Choice", 147 | "Choices": [ 148 | { 149 | "Variable": "$.s3Status", 150 | "StringEquals": "failed", 151 | "Next": "S3 Failed" 152 | }, 153 | { 154 | "Variable": "$.s3Status", 155 | "StringEquals": "transfering", 156 | "Next": "Wait S3 Copy" 157 | }, 158 | { 159 | "Variable": "$.s3Status", 160 | "BooleanEquals": true, 161 | "Next": "Import Image" 162 | } 163 | ], 164 | "Default": "S3 Failed" 165 | }, 166 | "Import Image": { 167 | "Type": "Task", 168 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-importImage-1C7URIDTNSECA", 169 | "InputPath": "$", 170 | "ResultPath": "$.importTaskId", 171 | "Next": "Import Task ID?" 172 | }, 173 | "Import Task ID?": { 174 | "Type": "Choice", 175 | "Choices": [ 176 | { 177 | "Variable": "$.importTaskId", 178 | "StringEquals": "failed", 179 | "Next": "Import Image Failed" 180 | }, 181 | { 182 | "Variable": "$.importTaskId", 183 | "StringGreaterThan": "import-ami-", 184 | "Next": "Wait Import Image" 185 | } 186 | ], 187 | "Default": "Import Image Failed" 188 | }, 189 | "Wait Import Image": { 190 | "Type": "Wait", 191 | "SecondsPath": "$.waitMove", 192 | "Next": "Import Image Status" 193 | }, 194 | "Import Image Status": { 195 | "Type": "Task", 196 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-importImageStatus-ASK67LVTBB0X", 197 | "InputPath": "$", 198 | "ResultPath": "$.importImageStatus", 199 | "Next": "Import Image Finish?" 200 | }, 201 | "Import Image Finish?": { 202 | "Type": "Choice", 203 | "Choices": [ 204 | { 205 | "Variable": "$.importImageStatus", 206 | "StringEquals": "failed", 207 | "Next": "Import Image Failed" 208 | }, 209 | { 210 | "Variable": "$.importImageStatus", 211 | "StringEquals": "active", 212 | "Next": "Wait Import Image" 213 | }, 214 | { 215 | "Variable": "$.importImageStatus", 216 | "StringEquals": "completed", 217 | "Next": "Remove S3 Image" 218 | } 219 | ], 220 | "Default": "Import Image Failed" 221 | }, 222 | "Remove S3 Image": { 223 | "Type": "Task", 224 | "Resource": "arn:aws:lambda:us-west-2:968854541546:function:gov-cloud-import-removeS3Image-1WA7OWNY924S2", 225 | "InputPath": "$", 226 | "Next": "SuccessState" 227 | }, 228 | "SuccessState": { 229 | "Type": "Succeed" 230 | }, 231 | "Move Failed": { 232 | "Type": "Fail", 233 | "Cause": "AWS Batch Job Failed", 234 | "Error": "Move AMI returned FAILED" 235 | }, 236 | "Volume Failed": { 237 | "Type": "Fail", 238 | "Cause": "AWS Batch Job Failed", 239 | "Error": "Make Volume returned FAILED" 240 | }, 241 | "EC2 Failed": { 242 | "Type": "Fail", 243 | "Cause": "AWS Batch Job Failed", 244 | "Error": "Launch EC2 worker for copying volume to GovCloud returned FAILED" 245 | }, 246 | "S3 Failed": { 247 | "Type": "Fail", 248 | "Cause": "AWS Batch Job Failed", 249 | "Error": "Volume copy to S3 GovCloud failed" 250 | }, 251 | "Import Image Failed": { 252 | "Type": "Fail", 253 | "Cause": "AWS Batch Job Failed", 254 | "Error": "Import Import from GovCloud S3 to GovCloud EC2 failed" 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /browser/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/favicon.ico -------------------------------------------------------------------------------- /browser/gov-cloud-import-s3sync-com-bucket-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "s3:*", 7 | "Resource": [ 8 | "arn:aws:s3:::<#BucketName#>/*", 9 | "arn:aws:s3:::<#BucketName#>" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /browser/gov-cloud-import-s3sync-gov-bucket-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": "s3:*", 7 | "Resource": [ 8 | "arn:aws-us-gov:s3:::<#BucketName#>/*", 9 | "arn:aws-us-gov:s3:::<#BucketName#>" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /browser/images/ImageImportFinal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/ImageImportFinal.gif -------------------------------------------------------------------------------- /browser/images/ImportS3Final.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/ImportS3Final.gif -------------------------------------------------------------------------------- /browser/images/StepFunctionOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/StepFunctionOverview.png -------------------------------------------------------------------------------- /browser/images/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/aws.png -------------------------------------------------------------------------------- /browser/images/cloudwatch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/cloudwatch.png -------------------------------------------------------------------------------- /browser/images/cloudwatchSchedule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/cloudwatchSchedule.png -------------------------------------------------------------------------------- /browser/images/commercial-resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/commercial-resources.png -------------------------------------------------------------------------------- /browser/images/diagramImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/diagramImage.png -------------------------------------------------------------------------------- /browser/images/diagramOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/diagramOverview.png -------------------------------------------------------------------------------- /browser/images/diagramS3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/diagramS3.png -------------------------------------------------------------------------------- /browser/images/gov-cloud-import-install.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/gov-cloud-import-install.gif -------------------------------------------------------------------------------- /browser/images/govcloud-resources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/govcloud-resources.png -------------------------------------------------------------------------------- /browser/images/govcloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/govcloud.png -------------------------------------------------------------------------------- /browser/images/overviewDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/overviewDiagram.png -------------------------------------------------------------------------------- /browser/images/ssm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/ssm.png -------------------------------------------------------------------------------- /browser/images/trouble1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/trouble1.png -------------------------------------------------------------------------------- /browser/images/trouble2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/images/trouble2.png -------------------------------------------------------------------------------- /browser/importImage.js: -------------------------------------------------------------------------------- 1 | function initImportImage(lambda){ 2 | return new Promise((resolve, reject) => { 3 | let image = 'ami-1234abcd' /*AMI or Snapshot ID*/ 4 | let os = 'Windows'/*Windows or Linux*/ 5 | let region = 'us-east-1'/*Source Commercial Region */ 6 | //Params for Lambda invoke 7 | let params = { 8 | FunctionName : initStepFunction, 9 | InvocationType : 'RequestResponse', 10 | LogType : 'Tail', 11 | Payload : JSON.stringify({"image": image, "region": region, "os": os}) 12 | }; 13 | // Call the Lambda function 14 | lambda.invoke(params, function(err, data) { 15 | if (err) { 16 | reject(err); 17 | } else { 18 | resolve(data); 19 | } 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /browser/importS3.js: -------------------------------------------------------------------------------- 1 | function initS3StepFunction(lambda){ 2 | return new Promise((resolve, reject) => { 3 | let comBucket = 'my-commercial-bucket' /*Source Commercial Bucket*/ 4 | let govBucket = 'my-govcloud-bucket' /*Destination GovCloud Bucket*/ 5 | //Params for Lambda invoke 6 | let params = { 7 | FunctionName : initS3Sync, 8 | InvocationType : 'RequestResponse', 9 | LogType : 'Tail', 10 | Payload : JSON.stringify({"source": comBucket, "dest": govBucket}) 11 | }; 12 | // Call the Lambda function 13 | lambda.invoke(params, function(err, data) { 14 | if (err) { 15 | reject(err); 16 | } else { 17 | resolve(data); 18 | } 19 | }); 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /browser/themes/amzn.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Mobile 1.4.5 3 | * Git HEAD hash: 68e55e78b292634d3991c795f06f5e37a512decc <> Date: Fri Oct 31 2014 17:33:30 UTC 4 | * http://jquerymobile.com 5 | * 6 | * Copyright 2010, 2014 jQuery Foundation, Inc. and othercontributors 7 | * Released under the MIT license. 8 | * http://jquery.org/license 9 | * 10 | */ 11 | 12 | 13 | /* Globals */ 14 | /* Font 15 | -----------------------------------------------------------------------------------------------------------*/ 16 | html { 17 | font-size: 100%; 18 | } 19 | body, 20 | input, 21 | select, 22 | textarea, 23 | button, 24 | .ui-btn { 25 | font-size: 1em; 26 | line-height: 1.3; 27 | font-family: sans-serif /*{global-font-family}*/; 28 | } 29 | legend, 30 | .ui-input-text input, 31 | .ui-input-search input { 32 | color: inherit; 33 | text-shadow: inherit; 34 | } 35 | /* Form labels (overrides font-weight bold in bars, and mini font-size) */ 36 | .ui-mobile label, 37 | div.ui-controlgroup-label { 38 | font-weight: normal; 39 | font-size: 16px; 40 | } 41 | /* Separators 42 | -----------------------------------------------------------------------------------------------------------*/ 43 | /* Field contain separator (< 28em) */ 44 | .ui-field-contain { 45 | border-bottom-color: #828282; 46 | border-bottom-color: rgba(0,0,0,.15); 47 | border-bottom-width: 1px; 48 | border-bottom-style: solid; 49 | } 50 | /* Table opt-in classes: strokes between each row, and alternating row stripes */ 51 | /* Classes table-stroke and table-stripe are deprecated in 1.4. */ 52 | .table-stroke thead th, 53 | .table-stripe thead th, 54 | .table-stripe tbody tr:last-child { 55 | border-bottom: 1px solid #d6d6d6; /* non-RGBA fallback */ 56 | border-bottom: 1px solid rgba(0,0,0,.1); 57 | } 58 | .table-stroke tbody th, 59 | .table-stroke tbody td { 60 | border-bottom: 1px solid #e6e6e6; /* non-RGBA fallback */ 61 | border-bottom: 1px solid rgba(0,0,0,.05); 62 | } 63 | .table-stripe.table-stroke tbody tr:last-child th, 64 | .table-stripe.table-stroke tbody tr:last-child td { 65 | border-bottom: 0; 66 | } 67 | .table-stripe tbody tr:nth-child(odd) td, 68 | .table-stripe tbody tr:nth-child(odd) th { 69 | background-color: #eeeeee; /* non-RGBA fallback */ 70 | background-color: rgba(0,0,0,.04); 71 | } 72 | /* Buttons 73 | -----------------------------------------------------------------------------------------------------------*/ 74 | .ui-btn, 75 | label.ui-btn { 76 | font-weight: bold; 77 | border-width: 1px; 78 | border-style: solid; 79 | } 80 | .ui-btn { 81 | text-decoration: none !important; 82 | } 83 | .ui-btn-active { 84 | cursor: pointer; 85 | } 86 | /* Corner rounding 87 | -----------------------------------------------------------------------------------------------------------*/ 88 | /* Class ui-btn-corner-all deprecated in 1.4 */ 89 | .ui-corner-all { 90 | -webkit-border-radius: .6em /*{global-radii-blocks}*/; 91 | border-radius: .6em /*{global-radii-blocks}*/; 92 | } 93 | /* Buttons */ 94 | .ui-btn-corner-all, 95 | .ui-btn.ui-corner-all, 96 | /* Slider track */ 97 | .ui-slider-track.ui-corner-all, 98 | /* Flipswitch */ 99 | .ui-flipswitch.ui-corner-all, 100 | /* Count bubble */ 101 | .ui-li-count { 102 | -webkit-border-radius: .3125em /*{global-radii-buttons}*/; 103 | border-radius: .3125em /*{global-radii-buttons}*/; 104 | } 105 | /* Icon-only buttons */ 106 | .ui-btn-icon-notext.ui-btn-corner-all, 107 | .ui-btn-icon-notext.ui-corner-all { 108 | -webkit-border-radius: 1em; 109 | border-radius: 1em; 110 | } 111 | /* Radius clip workaround for cleaning up corner trapping */ 112 | .ui-btn-corner-all, 113 | .ui-corner-all { 114 | -webkit-background-clip: padding; 115 | background-clip: padding-box; 116 | } 117 | /* Popup arrow */ 118 | .ui-popup.ui-corner-all > .ui-popup-arrow-guide { 119 | left: .6em /*{global-radii-blocks}*/; 120 | right: .6em /*{global-radii-blocks}*/; 121 | top: .6em /*{global-radii-blocks}*/; 122 | bottom: .6em /*{global-radii-blocks}*/; 123 | } 124 | /* Shadow 125 | -----------------------------------------------------------------------------------------------------------*/ 126 | .ui-shadow { 127 | -webkit-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 128 | -moz-box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 129 | box-shadow: 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 130 | } 131 | .ui-shadow-inset { 132 | -webkit-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 133 | -moz-box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 134 | box-shadow: inset 0 1px 3px /*{global-box-shadow-size}*/ rgba(0,0,0,.2) /*{global-box-shadow-color}*/; 135 | } 136 | .ui-overlay-shadow { 137 | -webkit-box-shadow: 0 0 12px rgba(0,0,0,.6); 138 | -moz-box-shadow: 0 0 12px rgba(0,0,0,.6); 139 | box-shadow: 0 0 12px rgba(0,0,0,.6); 140 | } 141 | /* Icons 142 | -----------------------------------------------------------------------------------------------------------*/ 143 | .ui-btn-icon-left:after, 144 | .ui-btn-icon-right:after, 145 | .ui-btn-icon-top:after, 146 | .ui-btn-icon-bottom:after, 147 | .ui-btn-icon-notext:after { 148 | background-color: #666666 /*{global-icon-color}*/; 149 | background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; 150 | background-position: center center; 151 | background-repeat: no-repeat; 152 | -webkit-border-radius: 1em; 153 | border-radius: 1em; 154 | } 155 | /* Alt icons */ 156 | .ui-alt-icon.ui-btn:after, 157 | .ui-alt-icon .ui-btn:after, 158 | html .ui-alt-icon.ui-checkbox-off:after, 159 | html .ui-alt-icon.ui-radio-off:after, 160 | html .ui-alt-icon .ui-checkbox-off:after, 161 | html .ui-alt-icon .ui-radio-off:after { 162 | background-color: #666666 /*{global-icon-color}*/; 163 | background-color: rgba(0,0,0,.15) /*{global-icon-disc}*/; 164 | } 165 | /* No disc */ 166 | .ui-nodisc-icon.ui-btn:after, 167 | .ui-nodisc-icon .ui-btn:after { 168 | background-color: transparent; 169 | } 170 | /* Icon shadow */ 171 | .ui-shadow-icon.ui-btn:after, 172 | .ui-shadow-icon .ui-btn:after { 173 | -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; 174 | -moz-box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; 175 | box-shadow: 0 1px 0 rgba(255,255,255,.3) /*{global-icon-shadow}*/; 176 | } 177 | /* Checkbox and radio */ 178 | .ui-btn.ui-checkbox-off:after, 179 | .ui-btn.ui-checkbox-on:after, 180 | .ui-btn.ui-radio-off:after, 181 | .ui-btn.ui-radio-on:after { 182 | display: block; 183 | width: 18px; 184 | height: 18px; 185 | margin: -9px 2px 0 2px; 186 | } 187 | .ui-checkbox-off:after, 188 | .ui-btn.ui-radio-off:after { 189 | filter: Alpha(Opacity=30); 190 | opacity: .3; 191 | } 192 | .ui-btn.ui-checkbox-off:after, 193 | .ui-btn.ui-checkbox-on:after { 194 | -webkit-border-radius: .1875em; 195 | border-radius: .1875em; 196 | } 197 | .ui-btn.ui-checkbox-off:after { 198 | background-color: #666; 199 | background-color: rgba(0,0,0,.3); 200 | } 201 | .ui-radio .ui-btn.ui-radio-on:after { 202 | background-image: none; 203 | background-color: #fff; 204 | width: 8px; 205 | height: 8px; 206 | border-width: 5px; 207 | border-style: solid; 208 | } 209 | .ui-alt-icon.ui-btn.ui-radio-on:after, 210 | .ui-alt-icon .ui-btn.ui-radio-on:after { 211 | background-color: #000; 212 | } 213 | /* Loader */ 214 | .ui-icon-loading { 215 | background: url("images/ajax-loader.gif"); 216 | background-size: 2.875em 2.875em; 217 | }.ui-bar-a,.ui-page-theme-a .ui-bar-inherit,html .ui-bar-a .ui-bar-inherit,html .ui-body-a .ui-bar-inherit,html body .ui-group-theme-a .ui-bar-inherit{background-color:#e9e9e9 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #eeeeee ;font-weight:bold;}.ui-bar-a{border-width:1px;border-style:solid;}.ui-overlay-a,.ui-page-theme-a,.ui-page-theme-a .ui-panel-wrapper{background-color:#f9f9f9 ;border-color:#bbbbbb ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-a,.ui-page-theme-a .ui-body-inherit,html .ui-bar-a .ui-body-inherit,html .ui-body-a .ui-body-inherit,html body .ui-group-theme-a .ui-body-inherit,html .ui-panel-page-container-a{background-color:#ffffff ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-a{border-width:1px;border-style:solid;}.ui-page-theme-a a,html .ui-bar-a a,html .ui-body-a a,html body .ui-group-theme-a a{color:#f7931e ;font-weight:bold;}.ui-page-theme-a a:visited,html .ui-bar-a a:visited,html .ui-body-a a:visited,html body .ui-group-theme-a a:visited{ color:#a05f13 ;}.ui-page-theme-a a:hover,html .ui-bar-a a:hover,html .ui-body-a a:hover,html body .ui-group-theme-a a:hover{color:#FFa922 ;}.ui-page-theme-a a:active,html .ui-bar-a a:active,html .ui-body-a a:active,html body .ui-group-theme-a a:active{color:#f7931e ;}.ui-page-theme-a .ui-btn,html .ui-bar-a .ui-btn,html .ui-body-a .ui-btn,html body .ui-group-theme-a .ui-btn,html head + body .ui-btn.ui-btn-a,.ui-page-theme-a .ui-btn:visited,html .ui-bar-a .ui-btn:visited,html .ui-body-a .ui-btn:visited,html body .ui-group-theme-a .ui-btn:visited,html head + body .ui-btn.ui-btn-a:visited{background-color:#f6f6f6 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-a .ui-btn:hover,html .ui-bar-a .ui-btn:hover,html .ui-body-a .ui-btn:hover,html body .ui-group-theme-a .ui-btn:hover,html head + body .ui-btn.ui-btn-a:hover{background-color:#ededed ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-a .ui-btn:active,html .ui-bar-a .ui-btn:active,html .ui-body-a .ui-btn:active,html body .ui-group-theme-a .ui-btn:active,html head + body .ui-btn.ui-btn-a:active{background-color:#e8e8e8 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-a .ui-btn.ui-btn-active,html .ui-bar-a .ui-btn.ui-btn-active,html .ui-body-a .ui-btn.ui-btn-active,html body .ui-group-theme-a .ui-btn.ui-btn-active,html head + body .ui-btn.ui-btn-a.ui-btn-active,.ui-page-theme-a .ui-checkbox-on:after,html .ui-bar-a .ui-checkbox-on:after,html .ui-body-a .ui-checkbox-on:after,html body .ui-group-theme-a .ui-checkbox-on:after,.ui-btn.ui-checkbox-on.ui-btn-a:after,.ui-page-theme-a .ui-flipswitch-active,html .ui-bar-a .ui-flipswitch-active,html .ui-body-a .ui-flipswitch-active,html body .ui-group-theme-a .ui-flipswitch-active,html body .ui-flipswitch.ui-bar-a.ui-flipswitch-active,.ui-page-theme-a .ui-slider-track .ui-btn-active,html .ui-bar-a .ui-slider-track .ui-btn-active,html .ui-body-a .ui-slider-track .ui-btn-active,html body .ui-group-theme-a .ui-slider-track .ui-btn-active,html body div.ui-slider-track.ui-body-a .ui-btn-active{background-color:#f7931e ;border-color:#875010 ;color:#ffffff ;text-shadow:0 1px 0 #444444 ;}.ui-page-theme-a .ui-radio-on:after,html .ui-bar-a .ui-radio-on:after,html .ui-body-a .ui-radio-on:after,html body .ui-group-theme-a .ui-radio-on:after,.ui-btn.ui-radio-on.ui-btn-a:after{border-color:#f7931e ;}.ui-page-theme-a .ui-btn:focus,html .ui-bar-a .ui-btn:focus,html .ui-body-a .ui-btn:focus,html body .ui-group-theme-a .ui-btn:focus,html head + body .ui-btn.ui-btn-a:focus,.ui-page-theme-a .ui-focus,html .ui-bar-a .ui-focus,html .ui-body-a .ui-focus,html body .ui-group-theme-a .ui-focus,html head + body .ui-btn-a.ui-focus,html head + body .ui-body-a.ui-focus{-webkit-box-shadow:0 0 12px #f7931e ;-moz-box-shadow:0 0 12px #f7931e ;box-shadow:0 0 12px #f7931e ;}.ui-bar-b,.ui-page-theme-b .ui-bar-inherit,html .ui-bar-b .ui-bar-inherit,html .ui-body-b .ui-bar-inherit,html body .ui-group-theme-b .ui-bar-inherit{background-color:#e9e9e9 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #eeeeee ;font-weight:bold;}.ui-bar-b{border-width:1px;border-style:solid;}.ui-overlay-b,.ui-page-theme-b,.ui-page-theme-b .ui-panel-wrapper{background-color:#f9f9f9 ;border-color:#bbbbbb ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-b,.ui-page-theme-b .ui-body-inherit,html .ui-bar-b .ui-body-inherit,html .ui-body-b .ui-body-inherit,html body .ui-group-theme-b .ui-body-inherit,html .ui-panel-page-container-b{background-color:#ffffff ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-b{border-width:1px;border-style:solid;}.ui-page-theme-b a,html .ui-bar-b a,html .ui-body-b a,html body .ui-group-theme-b a{color:#3388cc ;font-weight:bold;}.ui-page-theme-b a:visited,html .ui-bar-b a:visited,html .ui-body-b a:visited,html body .ui-group-theme-b a:visited{ color:#3388cc ;}.ui-page-theme-b a:hover,html .ui-bar-b a:hover,html .ui-body-b a:hover,html body .ui-group-theme-b a:hover{color:#005599 ;}.ui-page-theme-b a:active,html .ui-bar-b a:active,html .ui-body-b a:active,html body .ui-group-theme-b a:active{color:#005599 ;}.ui-page-theme-b .ui-btn,html .ui-bar-b .ui-btn,html .ui-body-b .ui-btn,html body .ui-group-theme-b .ui-btn,html head + body .ui-btn.ui-btn-b,.ui-page-theme-b .ui-btn:visited,html .ui-bar-b .ui-btn:visited,html .ui-body-b .ui-btn:visited,html body .ui-group-theme-b .ui-btn:visited,html head + body .ui-btn.ui-btn-b:visited{background-color:#f6f6f6 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-b .ui-btn:hover,html .ui-bar-b .ui-btn:hover,html .ui-body-b .ui-btn:hover,html body .ui-group-theme-b .ui-btn:hover,html head + body .ui-btn.ui-btn-b:hover{background-color:#ededed ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-b .ui-btn:active,html .ui-bar-b .ui-btn:active,html .ui-body-b .ui-btn:active,html body .ui-group-theme-b .ui-btn:active,html head + body .ui-btn.ui-btn-b:active{background-color:#e8e8e8 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-b .ui-btn.ui-btn-active,html .ui-bar-b .ui-btn.ui-btn-active,html .ui-body-b .ui-btn.ui-btn-active,html body .ui-group-theme-b .ui-btn.ui-btn-active,html head + body .ui-btn.ui-btn-b.ui-btn-active,.ui-page-theme-b .ui-checkbox-on:after,html .ui-bar-b .ui-checkbox-on:after,html .ui-body-b .ui-checkbox-on:after,html body .ui-group-theme-b .ui-checkbox-on:after,.ui-btn.ui-checkbox-on.ui-btn-b:after,.ui-page-theme-b .ui-flipswitch-active,html .ui-bar-b .ui-flipswitch-active,html .ui-body-b .ui-flipswitch-active,html body .ui-group-theme-b .ui-flipswitch-active,html body .ui-flipswitch.ui-bar-b.ui-flipswitch-active,.ui-page-theme-b .ui-slider-track .ui-btn-active,html .ui-bar-b .ui-slider-track .ui-btn-active,html .ui-body-b .ui-slider-track .ui-btn-active,html body .ui-group-theme-b .ui-slider-track .ui-btn-active,html body div.ui-slider-track.ui-body-b .ui-btn-active{background-color:#3388cc ;border-color:#3388cc ;color:#ffffff ;text-shadow:0 1px 0 #005599 ;}.ui-page-theme-b .ui-radio-on:after,html .ui-bar-b .ui-radio-on:after,html .ui-body-b .ui-radio-on:after,html body .ui-group-theme-b .ui-radio-on:after,.ui-btn.ui-radio-on.ui-btn-b:after{border-color:#3388cc ;}.ui-page-theme-b .ui-btn:focus,html .ui-bar-b .ui-btn:focus,html .ui-body-b .ui-btn:focus,html body .ui-group-theme-b .ui-btn:focus,html head + body .ui-btn.ui-btn-b:focus,.ui-page-theme-b .ui-focus,html .ui-bar-b .ui-focus,html .ui-body-b .ui-focus,html body .ui-group-theme-b .ui-focus,html head + body .ui-btn-b.ui-focus,html head + body .ui-body-b.ui-focus{-webkit-box-shadow:0 0 12px #3388cc ;-moz-box-shadow:0 0 12px #3388cc ;box-shadow:0 0 12px #3388cc ;}.ui-bar-c,.ui-page-theme-c .ui-bar-inherit,html .ui-bar-c .ui-bar-inherit,html .ui-body-c .ui-bar-inherit,html body .ui-group-theme-c .ui-bar-inherit{background-color:#e9e9e9 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #eeeeee ;font-weight:bold;}.ui-bar-c{border-width:1px;border-style:solid;}.ui-overlay-c,.ui-page-theme-c,.ui-page-theme-c .ui-panel-wrapper{background-color:#f9f9f9 ;border-color:#bbbbbb ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-c,.ui-page-theme-c .ui-body-inherit,html .ui-bar-c .ui-body-inherit,html .ui-body-c .ui-body-inherit,html body .ui-group-theme-c .ui-body-inherit,html .ui-panel-page-container-c{background-color:#ffffff ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-body-c{border-width:1px;border-style:solid;}.ui-page-theme-c a,html .ui-bar-c a,html .ui-body-c a,html body .ui-group-theme-c a{color:#3388cc ;font-weight:bold;}.ui-page-theme-c a:visited,html .ui-bar-c a:visited,html .ui-body-c a:visited,html body .ui-group-theme-c a:visited{ color:#3388cc ;}.ui-page-theme-c a:hover,html .ui-bar-c a:hover,html .ui-body-c a:hover,html body .ui-group-theme-c a:hover{color:#005599 ;}.ui-page-theme-c a:active,html .ui-bar-c a:active,html .ui-body-c a:active,html body .ui-group-theme-c a:active{color:#005599 ;}.ui-page-theme-c .ui-btn,html .ui-bar-c .ui-btn,html .ui-body-c .ui-btn,html body .ui-group-theme-c .ui-btn,html head + body .ui-btn.ui-btn-c,.ui-page-theme-c .ui-btn:visited,html .ui-bar-c .ui-btn:visited,html .ui-body-c .ui-btn:visited,html body .ui-group-theme-c .ui-btn:visited,html head + body .ui-btn.ui-btn-c:visited{background-color:#f6f6f6 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-c .ui-btn:hover,html .ui-bar-c .ui-btn:hover,html .ui-body-c .ui-btn:hover,html body .ui-group-theme-c .ui-btn:hover,html head + body .ui-btn.ui-btn-c:hover{background-color:#ededed ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-c .ui-btn:active,html .ui-bar-c .ui-btn:active,html .ui-body-c .ui-btn:active,html body .ui-group-theme-c .ui-btn:active,html head + body .ui-btn.ui-btn-c:active{background-color:#e8e8e8 ;border-color:#dddddd ;color:#333333 ;text-shadow:0 1px 0 #f3f3f3 ;}.ui-page-theme-c .ui-btn.ui-btn-active,html .ui-bar-c .ui-btn.ui-btn-active,html .ui-body-c .ui-btn.ui-btn-active,html body .ui-group-theme-c .ui-btn.ui-btn-active,html head + body .ui-btn.ui-btn-c.ui-btn-active,.ui-page-theme-c .ui-checkbox-on:after,html .ui-bar-c .ui-checkbox-on:after,html .ui-body-c .ui-checkbox-on:after,html body .ui-group-theme-c .ui-checkbox-on:after,.ui-btn.ui-checkbox-on.ui-btn-c:after,.ui-page-theme-c .ui-flipswitch-active,html .ui-bar-c .ui-flipswitch-active,html .ui-body-c .ui-flipswitch-active,html body .ui-group-theme-c .ui-flipswitch-active,html body .ui-flipswitch.ui-bar-c.ui-flipswitch-active,.ui-page-theme-c .ui-slider-track .ui-btn-active,html .ui-bar-c .ui-slider-track .ui-btn-active,html .ui-body-c .ui-slider-track .ui-btn-active,html body .ui-group-theme-c .ui-slider-track .ui-btn-active,html body div.ui-slider-track.ui-body-c .ui-btn-active{background-color:#3388cc ;border-color:#3388cc ;color:#ffffff ;text-shadow:0 1px 0 #005599 ;}.ui-page-theme-c .ui-radio-on:after,html .ui-bar-c .ui-radio-on:after,html .ui-body-c .ui-radio-on:after,html body .ui-group-theme-c .ui-radio-on:after,.ui-btn.ui-radio-on.ui-btn-c:after{border-color:#3388cc ;}.ui-page-theme-c .ui-btn:focus,html .ui-bar-c .ui-btn:focus,html .ui-body-c .ui-btn:focus,html body .ui-group-theme-c .ui-btn:focus,html head + body .ui-btn.ui-btn-c:focus,.ui-page-theme-c .ui-focus,html .ui-bar-c .ui-focus,html .ui-body-c .ui-focus,html body .ui-group-theme-c .ui-focus,html head + body .ui-btn-c.ui-focus,html head + body .ui-body-c.ui-focus{-webkit-box-shadow:0 0 12px #3388cc ;-moz-box-shadow:0 0 12px #3388cc ;box-shadow:0 0 12px #3388cc ;}.ui-disabled,.ui-state-disabled,button[disabled],.ui-select .ui-btn.ui-state-disabled{filter:Alpha(Opacity=30);opacity:.3;cursor:default !important;pointer-events:none;}.ui-btn:focus,.ui-btn.ui-focus{outline:0;}.ui-noboxshadow .ui-shadow,.ui-noboxshadow .ui-shadow-inset,.ui-noboxshadow .ui-overlay-shadow,.ui-noboxshadow .ui-shadow-icon.ui-btn:after,.ui-noboxshadow .ui-shadow-icon .ui-btn:after,.ui-noboxshadow .ui-focus,.ui-noboxshadow .ui-btn:focus,.ui-noboxshadow input:focus,.ui-noboxshadow .ui-panel{-webkit-box-shadow:none !important;-moz-box-shadow:none !important;box-shadow:none !important;}.ui-noboxshadow .ui-btn:focus,.ui-noboxshadow .ui-focus{outline-width:1px;outline-style:auto;} -------------------------------------------------------------------------------- /browser/themes/images/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/ajax-loader.gif -------------------------------------------------------------------------------- /browser/themes/images/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/aws.png -------------------------------------------------------------------------------- /browser/themes/images/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/diagram.png -------------------------------------------------------------------------------- /browser/themes/images/govcloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/govcloud.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/action-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/action-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/action-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/action-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/alert-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/alert-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/alert-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/alert-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-l-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-l-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-l-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-l-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-r-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-r-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-r-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-r-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-d-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-d-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-l-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-l-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-l-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-l-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-r-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-r-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-r-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-r-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-l-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-l-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-l-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-l-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-r-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-r-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-r-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-r-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/arrow-u-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/arrow-u-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/audio-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/audio-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/audio-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/audio-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/back-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/back-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/back-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/back-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/bars-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/bars-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/bars-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/bars-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/bullets-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/bullets-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/bullets-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/bullets-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/calendar-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/calendar-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/calendar-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/calendar-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/camera-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/camera-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/camera-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/camera-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-d-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-d-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-d-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-d-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-l-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-l-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-l-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-l-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-r-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-r-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-r-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-r-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-u-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-u-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/carat-u-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/carat-u-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/check-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/check-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/check-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/check-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/clock-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/clock-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/clock-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/clock-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/cloud-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/cloud-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/cloud-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/cloud-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/comment-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/comment-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/comment-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/comment-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/delete-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/delete-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/delete-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/delete-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/edit-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/edit-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/edit-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/edit-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/eye-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/eye-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/eye-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/eye-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/forbidden-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/forbidden-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/forbidden-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/forbidden-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/forward-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/forward-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/forward-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/forward-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/gear-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/gear-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/gear-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/gear-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/grid-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/grid-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/grid-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/grid-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/heart-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/heart-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/heart-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/heart-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/home-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/home-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/home-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/home-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/info-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/info-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/info-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/info-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/location-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/location-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/location-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/location-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/lock-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/lock-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/lock-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/lock-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/mail-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/mail-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/mail-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/mail-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/minus-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/minus-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/minus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/minus-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/navigation-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/navigation-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/navigation-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/navigation-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/phone-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/phone-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/phone-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/phone-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/plus-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/plus-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/plus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/plus-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/power-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/power-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/power-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/power-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/recycle-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/recycle-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/recycle-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/recycle-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/refresh-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/refresh-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/refresh-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/refresh-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/search-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/search-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/search-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/search-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/shop-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/shop-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/shop-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/shop-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/star-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/star-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/star-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/star-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/tag-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/tag-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/tag-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/tag-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/user-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/user-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/user-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/user-white.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/video-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/video-black.png -------------------------------------------------------------------------------- /browser/themes/images/icons-png/video-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/aws-gov-cloud-import/62be734b90ca0d40a68f75fc8c0e8a90c0904ea6/browser/themes/images/icons-png/video-white.png -------------------------------------------------------------------------------- /cloudformation/gov-cloud-import-govcloud-east.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Deploys Resources for gov-cloud-import in GovCloud", 5 | 6 | "Resources" : { 7 | "User" : { 8 | "Type" : "AWS::IAM::User", 9 | "Properties" : { 10 | "UserName" : "gov-cloud-import-user-east" 11 | } 12 | }, 13 | "UserPolicies" : { 14 | "Type" : "AWS::IAM::Policy", 15 | "Properties" : { 16 | "PolicyName" : "gov-cloud-import-user-east-policy", 17 | "PolicyDocument" : { 18 | "Statement": [ 19 | { 20 | "Effect": "Allow", 21 | "Action": [ 22 | "s3:*" 23 | ], 24 | "Resource": [ 25 | { 26 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"}]] 27 | } 28 | ] 29 | }, 30 | { 31 | "Effect": "Allow", 32 | "Action": [ 33 | "s3:*" 34 | ], 35 | "Resource": [ 36 | { 37 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"},"/*"]] 38 | } 39 | ] 40 | }, 41 | { 42 | "Effect": "Allow", 43 | "Action": [ 44 | "cloudformation:DescribeStacks", 45 | "ec2:DescribeImportImageTasks", 46 | "ec2:ImportImage", 47 | "ec2:RegisterImage", 48 | "ec2:DescribeImportSnapshotTasks", 49 | "ec2:ImportSnapshot", 50 | "s3:ListAllMyBuckets", 51 | "s3:HeadBucket", 52 | "s3:ListObjects" 53 | ], 54 | "Resource": "*" 55 | } 56 | ] 57 | }, 58 | "Users" : [ { "Ref" : "User" } ] 59 | } 60 | }, 61 | "Bucket": { 62 | "Type": "AWS::S3::Bucket", 63 | "Properties": { 64 | "AccessControl": "BucketOwnerFullControl" 65 | } 66 | } 67 | }, 68 | "Outputs" : { 69 | "NewUser" : { 70 | "Value" : { "Ref" : "User" }, 71 | "Description" : "Id of new user" 72 | }, 73 | "Bucket": { 74 | "Value": { "Ref" : "Bucket" }, 75 | "Description": "S3 Bucket for Importing Images" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cloudformation/gov-cloud-import-govcloud-vmimport.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Deploys VMImport Role for gov-cloud-import in GovCloud", 5 | 6 | "Resources" : { 7 | "Role" : { 8 | "Type" : "AWS::IAM::Role", 9 | "Properties" : { 10 | "RoleName" : "vmimport", 11 | "AssumeRolePolicyDocument" : { 12 | "Version" : "2012-10-17", 13 | "Statement" : [ 14 | { 15 | "Effect" : "Allow", 16 | "Principal" : { 17 | "Service" : "vmie.amazonaws.com" 18 | }, 19 | "Action" : "sts:AssumeRole", 20 | "Condition" : { 21 | "StringEquals" : { 22 | "sts:Externalid" : "vmimport" 23 | } 24 | } 25 | } 26 | ] 27 | } 28 | } 29 | }, 30 | "RolePolicies" : { 31 | "Type" : "AWS::IAM::Policy", 32 | "Properties" : { 33 | "PolicyName" : "gov-cloud-vmimport-policy", 34 | "PolicyDocument" : { 35 | "Statement": [ 36 | { 37 | "Action": [ 38 | "s3:*" 39 | ], 40 | "Resource": [ 41 | "arn:aws-us-gov:s3:::gov-cloud-import-bucket-*" 42 | ], 43 | "Effect": "Allow" 44 | }, 45 | { 46 | "Action": [ 47 | "s3:*" 48 | ], 49 | "Resource": [ 50 | "arn:aws-us-gov:s3:::gov-cloud-import-bucket-*/*" 51 | ], 52 | "Effect": "Allow" 53 | }, 54 | { 55 | "Action": [ 56 | "ec2:ModifySnapshotAttribute", 57 | "ec2:CopySnapshot", 58 | "ec2:RegisterImage", 59 | "ec2:Describe*", 60 | "ec2:FullAccess" 61 | ], 62 | "Resource": "*", 63 | "Effect": "Allow" 64 | } 65 | ] 66 | }, 67 | "Roles" : [ { "Ref" : "Role" } ] 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /cloudformation/gov-cloud-import-govcloud-west.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Deploys Resources for gov-cloud-import in GovCloud", 5 | 6 | "Resources" : { 7 | "User" : { 8 | "Type" : "AWS::IAM::User", 9 | "Properties" : { 10 | "UserName" : "gov-cloud-import-user-west" 11 | } 12 | }, 13 | "UserPolicies" : { 14 | "Type" : "AWS::IAM::Policy", 15 | "Properties" : { 16 | "PolicyName" : "gov-cloud-import-user-west-policy", 17 | "PolicyDocument" : { 18 | "Statement": [ 19 | { 20 | "Effect": "Allow", 21 | "Action": [ 22 | "s3:*" 23 | ], 24 | "Resource": [ 25 | { 26 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"}]] 27 | } 28 | ] 29 | }, 30 | { 31 | "Effect": "Allow", 32 | "Action": [ 33 | "s3:*" 34 | ], 35 | "Resource": [ 36 | { 37 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"},"/*"]] 38 | } 39 | ] 40 | }, 41 | { 42 | "Effect": "Allow", 43 | "Action": [ 44 | "cloudformation:DescribeStacks", 45 | "ec2:DescribeImportImageTasks", 46 | "ec2:ImportImage", 47 | "ec2:RegisterImage", 48 | "ec2:DescribeImportSnapshotTasks", 49 | "ec2:ImportSnapshot", 50 | "s3:ListAllMyBuckets", 51 | "s3:HeadBucket", 52 | "s3:ListObjects" 53 | ], 54 | "Resource": "*" 55 | } 56 | ] 57 | }, 58 | "Users" : [ { "Ref" : "User" } ] 59 | } 60 | }, 61 | "Bucket": { 62 | "Type": "AWS::S3::Bucket", 63 | "Properties": { 64 | "AccessControl": "BucketOwnerFullControl" 65 | } 66 | } 67 | }, 68 | "Outputs" : { 69 | "NewUser" : { 70 | "Value" : { "Ref" : "User" }, 71 | "Description" : "Id of new user" 72 | }, 73 | "Bucket": { 74 | "Value": { "Ref" : "Bucket" }, 75 | "Description": "S3 Bucket for Importing Images" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cloudformation/gov-cloud-import-govcloud.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion" : "2010-09-09", 3 | 4 | "Description" : "Deploys Resources for gov-cloud-import in GovCloud", 5 | 6 | "Resources" : { 7 | "User" : { 8 | "Type" : "AWS::IAM::User", 9 | "Properties" : { 10 | "UserName" : "gov-cloud-import-user" 11 | } 12 | }, 13 | "UserPolicies" : { 14 | "Type" : "AWS::IAM::Policy", 15 | "Properties" : { 16 | "PolicyName" : "gov-cloud-import-user-policy", 17 | "PolicyDocument" : { 18 | "Statement": [ 19 | { 20 | "Effect": "Allow", 21 | "Action": [ 22 | "s3:*" 23 | ], 24 | "Resource": [ 25 | { 26 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"}]] 27 | } 28 | ] 29 | }, 30 | { 31 | "Effect": "Allow", 32 | "Action": [ 33 | "s3:*" 34 | ], 35 | "Resource": [ 36 | { 37 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"},"/*"]] 38 | } 39 | ] 40 | }, 41 | { 42 | "Effect": "Allow", 43 | "Action": [ 44 | "cloudformation:DescribeStacks", 45 | "ec2:DescribeImportImageTasks", 46 | "ec2:ImportImage", 47 | "ec2:RegisterImage", 48 | "ec2:DescribeImportSnapshotTasks", 49 | "ec2:ImportSnapshot", 50 | "s3:ListAllMyBuckets", 51 | "s3:HeadBucket", 52 | "s3:ListObjects" 53 | ], 54 | "Resource": "*" 55 | } 56 | ] 57 | }, 58 | "Users" : [ { "Ref" : "User" } ] 59 | } 60 | }, 61 | "Role" : { 62 | "Type" : "AWS::IAM::Role", 63 | "Properties" : { 64 | "RoleName" : "vmimport", 65 | "AssumeRolePolicyDocument" : { 66 | "Version" : "2012-10-17", 67 | "Statement" : [ 68 | { 69 | "Effect" : "Allow", 70 | "Principal" : { 71 | "Service" : "vmie.amazonaws.com" 72 | }, 73 | "Action" : "sts:AssumeRole", 74 | "Condition" : { 75 | "StringEquals" : { 76 | "sts:Externalid" : "vmimport" 77 | } 78 | } 79 | } 80 | ] 81 | } 82 | } 83 | }, 84 | "RolePolicies" : { 85 | "Type" : "AWS::IAM::Policy", 86 | "Properties" : { 87 | "PolicyName" : "gov-cloud-vmimport-policy", 88 | "PolicyDocument" : { 89 | "Statement": [ 90 | { 91 | "Effect": "Allow", 92 | "Action": [ 93 | "s3:*" 94 | ], 95 | "Resource": [ 96 | { 97 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"}]] 98 | } 99 | ] 100 | }, 101 | { 102 | "Effect": "Allow", 103 | "Action": [ 104 | "s3:*" 105 | ], 106 | "Resource": [ 107 | { 108 | "Fn::Join": ["", ["arn:aws-us-gov:s3:::",{"Ref": "Bucket"},"/*"]] 109 | } 110 | ] 111 | }, 112 | { 113 | "Effect": "Allow", 114 | "Action": [ 115 | "ec2:ModifySnapshotAttribute", 116 | "ec2:CopySnapshot", 117 | "ec2:RegisterImage", 118 | "ec2:Describe*", 119 | "ec2:FullAccess" 120 | ], 121 | "Resource": "*" 122 | } 123 | ] 124 | }, 125 | "Roles" : [ { "Ref" : "Role" } ] 126 | } 127 | }, 128 | 129 | "Bucket": { 130 | "Type": "AWS::S3::Bucket", 131 | "Properties": { 132 | "AccessControl": "BucketOwnerFullControl" 133 | } 134 | } 135 | }, 136 | 137 | "Outputs" : { 138 | "NewUser" : { 139 | "Value" : { "Ref" : "User" }, 140 | "Description" : "Id of new user" 141 | }, 142 | "Bucket": { 143 | "Value": { "Ref" : "Bucket" }, 144 | "Description": "S3 Bucket for Importing Images" 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ec2/gov-cloud-import-ec2prep.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ###################################################################################################################### 4 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 5 | # # 6 | # SPDX-License-Identifier: Apache-2.0 # 7 | ###################################################################################################################### 8 | 9 | #Update Instance 10 | sudo yum update -y 11 | 12 | #Install scripts 13 | cd /home/ec2-user 14 | aws s3 cp s3:///ec2/gov-cloud-import.js ./ 15 | curl --silent --location https://rpm.nodesource.com/setup_9.x | sudo bash - 16 | 17 | #Shell Scripts 18 | mkdir /home/ec2-user/shell 19 | cd /home/ec2-user/shell 20 | aws s3 cp s3:///ec2/umountBucket.sh ./ 21 | aws s3 cp s3:///ec2/mountBucket.sh ./ 22 | chmod +x *.sh 23 | cd /home/ec2-user/ 24 | 25 | #Install binaries 26 | sudo yum install -y gcc libstdc++-devel gcc-c++ fuse fuse-devel curl-devel libxml2-devel mailcap automake openssl-devel git mlocate figlet awslogs nodejs 27 | 28 | #Install Nodejs modules 29 | sudo -H -u ec2-user bash -c 'npm install xmlhttprequest && npm install aws-sdk && npm install executive && npm install s3-node-client && npm install copy-dynamodb-table' 30 | 31 | #Install S3fs 32 | sudo git clone https://github.com/s3fs-fuse/s3fs-fuse 33 | cd s3fs-fuse/ 34 | sudo ./autogen.sh 35 | sudo ./configure --prefix=/usr --with-openssl 36 | sudo make 37 | sudo make install 38 | cd ../ 39 | 40 | #Mount point for commercial buckets 41 | mkdir /home/ec2-user/s3fs 42 | #Clean Up 43 | sudo rm -rf /home/ec2-user/s3fs-fuse 44 | 45 | #Update MOTD to look kewl 46 | sudo touch /etc/update-motd.d/90-footer 47 | sudo chmod 777 /etc/update-motd.d/90-footer 48 | echo "figlet -t 'gov-cloud-import'" >> /etc/update-motd.d/90-footer 49 | echo "echo" >> /etc/update-motd.d/90-footer 50 | echo "echo 'Written by awsjason'" >> /etc/update-motd.d/90-footer 51 | echo "echo" >> /etc/update-motd.d/90-footer 52 | sudo chmod 755 /etc/update-motd.d/90-footer 53 | sudo update-motd 54 | 55 | #Set Up Variables 56 | INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) 57 | EC2_REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed 's/\(.*\)[a-z]/\1/') 58 | #ROOT_VOL=$(aws ec2 describe-instance-attribute --instance-id $INSTANCE_ID \ 59 | #--attribute blockDeviceMapping --region $EC2_REGION | awk '/vol-*/ {print $2}' \ 60 | #| tr -d '",') 61 | #echo $ROOT_VOL > /home/ec2-user/rootVol 62 | 63 | #Setup awslogs 64 | #Add S3 Sync Logs 65 | sudo echo ' 66 | [syncLog] 67 | datetime_format = %b %d %H:%M:%S 68 | file = /home/ec2-user/s3SyncLog 69 | buffer_duration = 5000 70 | log_stream_name = {instance_id} 71 | initial_position = start_of_file 72 | log_group_name = /gov-cloud-import/s3Synclog 73 | ' >> /etc/awslogs/awslogs.conf 74 | #Replace the default with our app 75 | sed -i 's|file = /var/log/messages|file = /home/ec2-user/log|g' /etc/awslogs/awslogs.conf 76 | sed -i 's|/var/log/messages|/gov-cloud-import/EC2Worker|g' /etc/awslogs/awslogs.conf 77 | sed -i 's|log_group_name = /var/log/messages|log_group_name = /gov-cloud-import/ec2Worker|g' /etc/awslogs/awslogs.conf 78 | sed -i "s/us-east-1/$EC2_REGION/g" /etc/awslogs/awscli.conf 79 | #Start Service 80 | sudo service awslogs start 81 | sudo chkconfig awslogs on 82 | 83 | #run gov-cloud-script.js and check every 5 minutes if its running 84 | echo '*/5 * * * * root /bin/bash /home/ec2-user/shell/gov-cloud-import-cron.sh' > /etc/cron.d/gov-cloud-import-run-node.sh 85 | chmod 600 /etc/cron.d/gov-cloud-import-run-node.sh 86 | 87 | #Script for starting and verifing Node is running 88 | echo ' 89 | #!/bin/bash 90 | NOW=$(date +%a" "%b" "%d" "%Y" "%R:%S" GMT"%z" ("%Z")") 91 | NODE_PID=$(cat /home/ec2-user/gov-cloud-import-node-pid) 92 | NODE_VERIFY=$(ps aux | grep gov-cloud-import.js | grep sudo | awk *{print $2}*) 93 | 94 | if [[ -z $NODE_PID ]]; then 95 | sudo node /home/ec2-user/gov-cloud-import.js & >> /home/ec2-user/log 96 | echo "$NOW gov-cloud-import.js is starting Node PID: $!" >> /home/ec2-user/log 97 | echo $! > /home/ec2-user/gov-cloud-import-node-pid 98 | elif [[ $NODE_PID == $NODE_VERIFY ]]; then 99 | echo "$NOW gov-cloud-import.js is running. Node PID: $NODE_PID" >> /home/ec2-user/log 100 | else 101 | sudo node /home/ec2-user/gov-cloud-import.js & >> /home/ec2-user/log 102 | echo "$NOW gov-cloud-import.js is starting Node PID: $!" >> /home/ec2-user/log 103 | echo $! > /home/ec2-user/gov-cloud-import-node-pid 104 | fi' > /home/ec2-user/shell/gov-cloud-import-cron.sh 105 | chmod +rx /home/ec2-user/shell/gov-cloud-import-cron.sh 106 | sed -i 's/*/\x27/g' /home/ec2-user/shell/gov-cloud-import-cron.sh 107 | touch /home/ec2-user/gov-cloud-import-node-pid 108 | 109 | #Start Node 110 | /home/ec2-user/shell/gov-cloud-import-cron.sh 111 | -------------------------------------------------------------------------------- /ec2/gov-cloud-import.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | //For S3FS mounting via shell 10 | let exec = require('executive'); 11 | //S3 Client that allow for synchronizng 12 | let s3 = require('s3-node-client'); 13 | //For HTTP Requests 14 | const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; 15 | // Load the AWS SDK for Node.js 16 | const AWS = require('aws-sdk'); 17 | //Get HTTP Function 18 | function httpGet(theUrl) { 19 | let xmlHttp = new XMLHttpRequest(); 20 | xmlHttp.open( "GET", theUrl, false ); // false for synchronous request 21 | xmlHttp.send( null ); 22 | return xmlHttp.responseText; 23 | } 24 | 25 | //Find az and make region 26 | let az = httpGet("http://169.254.169.254/latest/meta-data/placement/availability-zone"); 27 | let region = az.slice(0, -1); 28 | AWS.config.update({region: region}); 29 | let instanceId = httpGet("http://169.254.169.254/latest/meta-data/instance-id"); 30 | 31 | //StepFunctions Heartbeat 32 | let heartbeat = []; 33 | 34 | //Find the GovCloud region based on Instance Region 35 | function findGovRegion(){ 36 | if (region == 'us-west-2') { 37 | return 'us-gov-west-1'; 38 | } else if (region == 'us-east-2'){ 39 | return 'us-gov-east-1'; 40 | } 41 | } 42 | 43 | //For Reading Mount Point and Writing Logs 44 | const fs = require('fs'); 45 | 46 | //Create AWS Objects 47 | const sts = new AWS.STS(); 48 | const ec2 = new AWS.EC2(); 49 | const ssm = new AWS.SSM(); 50 | const stepfunctions = new AWS.StepFunctions(); 51 | 52 | //Setting Scripts Inital Timers 53 | let now = Date.now(); 54 | let stopScriptTime = now + 1800000; 55 | 56 | //Reset timers for when to quit; 5 mins from last copy 57 | function setTime(){ 58 | now = Date.now(); 59 | stopScriptTime = now + 300000; 60 | } 61 | 62 | //Test if object is empty 63 | function isEmpty(obj){ 64 | for(let key in obj) { 65 | if(obj.hasOwnProperty(key)) 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | //Format HttpUploadProgress Size 72 | function formatBytes(a, b) { 73 | if (0 == a) return "0 Bytes"; 74 | let c = 1024, 75 | d = b || 2, 76 | e = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], 77 | f = Math.floor(Math.log(a) / Math.log(c)); 78 | return parseFloat((a / Math.pow(c, f)).toFixed(d)) + " " + e[f]; 79 | } 80 | 81 | //An app has got to log! 82 | function writeToLog(logInfo, logName){ 83 | let d = new Date(); 84 | fs.appendFile('/home/ec2-user/'+logName, d.toString()+" "+logInfo+"\n", function (err) { 85 | if (err) throw err; 86 | }); 87 | } 88 | 89 | //Change Instance Tag 90 | function changeInstanceTag(instanceId){ 91 | return new Promise((resolve, reject) => { 92 | var params = { 93 | Resources: [ 94 | instanceId 95 | ], 96 | Tags: [ 97 | { 98 | Key: "gov-cloud-import", 99 | Value: "false" 100 | } 101 | ] 102 | }; 103 | ec2.createTags(params, function(err, data){ 104 | if (err) { 105 | writeToLog(JSON.stringify(err), 'log'); 106 | reject(err); 107 | } else { 108 | writeToLog(JSON.stringify(data) + 'Tag changed to false.', 'log'); 109 | resolve(data); 110 | } 111 | }); 112 | }); 113 | } 114 | 115 | //Timeout to allow for volume attach time 116 | const timeout = ms => new Promise(res => setTimeout(res, ms)) 117 | 118 | //Find available drive letter 119 | function findLetter(volumeId){ 120 | return new Promise((resolve, reject) => { 121 | let paramsInst = { 122 | Filters: [ 123 | { Name: 'tag:gov-cloud-import', 124 | Values: ['true'] 125 | }, 126 | { Name: 'instance-state-name', 127 | Values: ['running'] 128 | } 129 | ] 130 | }; 131 | ec2.describeInstances(paramsInst, function(err, data) { 132 | if (err) { 133 | writeToLog(err, err.stack); // an error occurred 134 | } else { 135 | //Construct an array of mounted drives so when know which letter to use for mounting 136 | const instanceId = data.Reservations[0].Instances[0].InstanceId; 137 | let length = data.Reservations[0].Instances[0].BlockDeviceMappings.length; 138 | let mountedDrives = []; 139 | for (let index = 0; index < length; ++index) { 140 | let drive = data.Reservations[0].Instances[0].BlockDeviceMappings[index].DeviceName; 141 | let lastChar = drive.substr(drive.length - 1); 142 | mountedDrives[index] = lastChar; 143 | } 144 | //Possible letters for mounting volume 145 | let driveLetters = ['f','g','h','i','j','k','l','m',]; 146 | //If we max the connected volumes, send back a note 147 | if (mountedDrives.length >= 9){ 148 | callback(null, "Max number of connected volumes. Restart Import.") 149 | } 150 | 151 | //Removing the mounted drive letters from possibilities 152 | for (let i=0; i< mountedDrives.length; i++) { 153 | let index = driveLetters.indexOf(mountedDrives[i]); 154 | if (index > -1) { 155 | driveLetters.splice(index, 1); 156 | } 157 | } 158 | //Pick a letter 159 | const device = '/dev/sd'+driveLetters[0]; 160 | resolve(device); 161 | } 162 | }); 163 | }); 164 | } 165 | 166 | //Attach Volume 167 | function attachVolume(taskData, instanceId){ 168 | return new Promise((resolve, reject) => { 169 | let paramsAttach = { 170 | Device: taskData.device, 171 | InstanceId: instanceId, 172 | VolumeId: taskData.volumeId 173 | }; 174 | ec2.attachVolume(paramsAttach, function(err, data) { 175 | if (err) { 176 | writeToLog(JSON.stringify(err), 'log'); 177 | reject(err); 178 | } else { 179 | writeToLog(JSON.stringify(data), 'log'); 180 | resolve("success"); 181 | } 182 | }); 183 | }); 184 | } 185 | 186 | //Detach Volume 187 | function detachVolume(volumeId){ 188 | return new Promise((resolve, reject) => { 189 | let params = { 190 | VolumeId: volumeId 191 | }; 192 | ec2.detachVolume(params, function(err, data) { 193 | if (err) { 194 | writeToLog(JSON.stringify(err), 'log'); 195 | reject(err); 196 | } else { 197 | writeToLog(JSON.stringify(data)), 'log'; 198 | return volumeId 199 | } 200 | }); 201 | }); 202 | } 203 | 204 | //Delete Volume 205 | function deleteVolume(volumeId){ 206 | let params = { 207 | VolumeId: volumeId 208 | }; 209 | ec2.deleteVolume(params, function(err, data) { 210 | if (err) { 211 | writeToLog(JSON.stringify(err), 'log'); 212 | } else { 213 | writeToLog(JSON.stringify(volumeId+" has been detached and deleted."), 'log'); 214 | } 215 | }); 216 | 217 | } 218 | 219 | //Tie Detach and Delete together so we can time it 220 | function removeVolume(volumeId){ 221 | detachVolume(volumeId); 222 | //Give detached time to fully detach 223 | setTimeout(deleteVolume, 10000, volumeId); 224 | } 225 | 226 | //Remove from Heartbeat array 227 | function removeHeartbeat(array, element) { 228 | const index = array.indexOf(element); 229 | if (index !== -1) { 230 | array.splice(index, 1); 231 | } 232 | writeToLog("Heartbeat removed", 'log'); 233 | } 234 | 235 | //Construct each Heartbeat 236 | function heartbeatFunc(taskToken){ 237 | //change tag so instance will not recieve new tasks. 238 | changeInstanceTag(instanceId); 239 | let paramsHeartbeat = { 240 | taskToken: taskToken 241 | }; 242 | stepfunctions.sendTaskHeartbeat(paramsHeartbeat, function(err, data) { 243 | if (err) { 244 | writeToLog(JSON.stringify(err), 'log'); 245 | if (err.code == "TaskTimedOut"){ 246 | removeHeartbeat(heartbeat, taskToken); 247 | } 248 | } else { 249 | //writeToLog(JSON.stringify(data)); 250 | writeToLog('Heartbeat Sent: '+taskToken, 'log'); 251 | } 252 | }); 253 | } 254 | 255 | //Send each Heartbeat in array 256 | function sendHeartbeat(){ 257 | for (let i=0; i< heartbeat.length; i++) { 258 | //writeToLog(JSON.stringify(heartbeat[i])) 259 | heartbeatFunc(heartbeat[i]); 260 | } 261 | } 262 | 263 | //Loop for Heartbeat(s), send every 3 mins. Activity heartbeat timeout = 5 min. 264 | setInterval(function(){ 265 | if(heartbeat.length > 0){ 266 | sendHeartbeat(); 267 | //Reset shutdown timer 268 | setTime(); 269 | } 270 | }, 180000); 271 | 272 | //Get GovCloud Creds 273 | function getParameter(value){ 274 | return new Promise(resolve => { 275 | let params = { 276 | Name: value, 277 | WithDecryption: true 278 | }; 279 | ssm.getParameter(params, function(err, data) { 280 | if (err) { 281 | writeToLog(JSON.stringify(err), 'log'); 282 | reject(err); 283 | } else { 284 | //Resolve with Parameter Value 285 | resolve(data.Parameter.Value); 286 | } 287 | }); 288 | }); 289 | } 290 | 291 | function terminateInstance(){ 292 | let instanceId = httpGet("http://169.254.169.254/latest/meta-data/instance-id"); 293 | let ec2Params = { 294 | InstanceIds: [instanceId] 295 | }; 296 | ec2.terminateInstances(ec2Params, function(err, data) { 297 | if (err) { 298 | writeToLog(JSON.stringify(err), 'log'); 299 | } else { 300 | writeToLog(JSON.stringify(data), 'log'); 301 | } 302 | }); 303 | } 304 | 305 | //Terminate Instance after 5 min from copy finish. 306 | setInterval(function(){ 307 | now = Date.now(); 308 | writeToLog(stopScriptTime+' Time at which to terminate instance.', 'log'); 309 | if (now > stopScriptTime){ 310 | //Remove Connected, Orphaned Volumes before stopping instance 311 | //orphanedVolumes(); 312 | writeToLog("Terminating this instance...", 'log') 313 | terminateInstance(); 314 | } 315 | }, 65000); 316 | 317 | //Construct Activty ARN so we can find tasks 318 | function createActivityArn(){ 319 | return new Promise((resolve, reject) => { 320 | let callerParams = { 321 | }; 322 | //Use STS to find the account number 323 | sts.getCallerIdentity(callerParams, function(err, data) { 324 | if (err) { 325 | writeToLog(JSON.stringify(err), 'log'); 326 | reject(err); 327 | } else { 328 | //Create Step Functions State Machine ARN 329 | let arn = 'arn:aws:states:'+region+':'+data.Account+':activity:ec2s3Copy'; 330 | resolve(arn); 331 | } 332 | }); 333 | }); 334 | } 335 | 336 | //Find Tasks to copy 337 | function findTask(activityArn){ 338 | return new Promise((resolve, reject) => { 339 | let params = { 340 | activityArn: activityArn, 341 | }; 342 | stepfunctions.getActivityTask(params, function(err, data) { 343 | if (err) { 344 | writeToLog(JSON.stringify(err)); 345 | } else { 346 | if (isEmpty(data)){ 347 | reject(err); 348 | } else { 349 | //Parse the results 350 | let obj = JSON.parse(data.input); 351 | obj.taskToken = data.taskToken; 352 | writeToLog('***New Task***', 'log'); 353 | writeToLog(JSON.stringify(obj), 'log'); 354 | resolve(obj); 355 | } 356 | } 357 | }); 358 | }); 359 | } 360 | 361 | function sendTaskFailure(taskToken){ 362 | return new Promise((resolve, reject) => { 363 | let params = { 364 | taskToken: taskToken, 365 | cause: 'S3 Copy Failed' 366 | }; 367 | stepfunctions.sendTaskFailure(params, function(err, data) { 368 | if (err) { 369 | writeToLog(JSON.stringify(err), 'log'); 370 | } else { 371 | writeToLog(JSON.stringify('Sent Task Failure to StepFunctions'), 'log'); 372 | return(data); 373 | } 374 | }); 375 | }); 376 | } 377 | 378 | function sendTaskSuccess(taskToken){ 379 | return new Promise((resolve, reject) => { 380 | let params = { 381 | output: "true", 382 | taskToken: taskToken 383 | }; 384 | stepfunctions.sendTaskSuccess(params, function(err, data) { 385 | if (err) { 386 | writeToLog(JSON.stringify(err), 'log'); 387 | } else { 388 | writeToLog(JSON.stringify('Sent Task Success to StepFunctions'), 'log'); 389 | return(data); 390 | } 391 | }); 392 | }); 393 | } 394 | 395 | //Mount Buckets via s3fs shell script 396 | function mountBucket(bucket){ 397 | return new Promise((resolve, reject) => { 398 | exec('./shell/mountBucket.sh -b '+ bucket, (err, stdout, stderr) => { 399 | if (stdout.startsWith("success")) { 400 | writeToLog(stdout+' '+bucket, 'log') 401 | resolve("success"); 402 | } else { 403 | resolve('fail'); 404 | } 405 | }); 406 | }); 407 | } 408 | 409 | //Unmount Buckets via s3fs shell script 410 | function umountBucket(bucket){ 411 | return new Promise((resolve, reject) => { 412 | exec('./shell/umountBucket.sh -b '+ bucket, (err, stdout, stderr) => { 413 | if (stdout.startsWith("success")) { 414 | resolve("success"); 415 | } else { 416 | resolve('fail'); 417 | } 418 | }); 419 | }); 420 | } 421 | 422 | //Construct S3 Sync Activty ARN 423 | function createS3ActivityArn(){ 424 | return new Promise((resolve, reject) => { 425 | let callerParams = { 426 | }; 427 | //Use STS to find the account number 428 | sts.getCallerIdentity(callerParams, function(err, data) { 429 | if (err) { 430 | writeToLog(JSON.stringify(err), 'log'); 431 | reject(err); 432 | } else { 433 | //Create Step Functions State Machine ARN 434 | let arn = 'arn:aws:states:'+region+':'+data.Account+':activity:s3s3Sync'; 435 | resolve(arn); 436 | } 437 | }); 438 | }); 439 | } 440 | 441 | //Find S3 Sync Tasks 442 | function findS3Task(s3activityArn){ 443 | return new Promise((resolve, reject) => { 444 | let params = { 445 | activityArn: s3activityArn, 446 | }; 447 | stepfunctions.getActivityTask(params, function(err, data) { 448 | if (err) { 449 | writeToLog(JSON.stringify(err), 'log'); 450 | } else { 451 | if (isEmpty(data)){ 452 | reject(err); 453 | } else { 454 | //Parse the results 455 | let obj = JSON.parse(data.input); 456 | obj.taskToken = data.taskToken; 457 | writeToLog('***New S3 Sync Task***', 'log'); 458 | writeToLog(JSON.stringify(obj), 'log'); 459 | resolve(obj); 460 | } 461 | } 462 | }); 463 | }); 464 | } 465 | 466 | //Copy connected volumne to GovCloud S3 467 | function copyVolume(taskData){ 468 | return new Promise((resolve, reject) => { 469 | let govRegion = findGovRegion(); 470 | //Set Object & Params for GovCloud S3 Upload 471 | let s3 = new AWS.S3({ 472 | region: govRegion, 473 | accessKeyId: taskData.accessKeyId, 474 | secretAccessKey: taskData.secretAccessKey 475 | }); 476 | // call S3 to retrieve upload file to specified bucket 477 | let uploadParams = {Bucket: taskData.s3Bucket, Key: taskData.volumeId, Body: ''}; 478 | 479 | //Read Volume Mount point and set it as S3 boday 480 | let fileStream = fs.createReadStream(taskData.device); 481 | fileStream.on('error', function(err) { 482 | writeToLog(JSON.stringify(err), 'log'); 483 | }); 484 | uploadParams.Body = fileStream; 485 | //Set S3 Upload Options 486 | let options = {partSize: 10 * 1024 * 1024, queueSize: 4}; 487 | //call S3 to upload 488 | s3.upload(uploadParams, options, function (err, data){ 489 | if (err) { 490 | removeHeartbeat(heartbeat, taskData.taskToken); 491 | //Send a failure to Step Functions 492 | sendTaskFailure(taskData.taskToken); 493 | writeToLog(JSON.stringify(err)), 'log'; 494 | removeVolume(taskData.volumeId); 495 | reject(err); 496 | } if (data) { 497 | removeHeartbeat(heartbeat, taskData.taskToken); 498 | writeToLog(JSON.stringify(data), 'log'); 499 | resolve(data); 500 | } 501 | }).on('httpUploadProgress', function(evt) { 502 | msg = taskData.volumeId+' Progress: '+formatBytes(evt.loaded) 503 | writeToLog(msg, "imageProgressLog"); 504 | }); 505 | }); 506 | } 507 | 508 | //Flow Control in order 509 | async function copyImage(){ 510 | try { 511 | //changeInstanceTag(instanceId); 512 | let activityArn = await createActivityArn(); 513 | //findTask promise rejects when no tasks are present 514 | let taskData = await findTask(activityArn); 515 | taskData.device = await findLetter(taskData.volumeId); 516 | await attachVolume(taskData, instanceId); 517 | //Give the Volume a chance to mount 518 | await timeout(10000) 519 | //Get GovCloud Creds from Commercial SSM 520 | taskData.s3Bucket = await getParameter('gov-cloud-import-s3Bucket'); 521 | taskData.accessKeyId = await getParameter('gov-cloud-import-accessKey'); 522 | taskData.secretAccessKey = await getParameter('gov-cloud-import-secretKey'); 523 | //Add to heartbeat array 524 | heartbeat.push(taskData.taskToken); 525 | //Copy the Volume to GovCloud S3 526 | changeInstanceTag(instanceId); 527 | await copyVolume(taskData); 528 | sendTaskSuccess(taskData.taskToken); 529 | removeVolume(taskData.volumeId); 530 | } catch(err){ 531 | if (err){ 532 | writeToLog(err, 'log'); 533 | } else { 534 | writeToLog('No New Image Import Tasks', 'log'); 535 | } 536 | } 537 | }; 538 | 539 | async function syncS3(){ 540 | try { 541 | //changeInstanceTag(instanceId); 542 | let activityArn = await createS3ActivityArn(); 543 | let taskData = await findS3Task(activityArn); 544 | let mounted = await mountBucket(taskData.sourceBucket); 545 | if (mounted == "success"){ 546 | changeInstanceTag(instanceId); 547 | taskData.accessKeyId = await getParameter('gov-cloud-import-accessKey'); 548 | taskData.secretAccessKey = await getParameter('gov-cloud-import-secretKey'); 549 | let govRegion = findGovRegion() 550 | //Create S3 client object 551 | let client = s3.createClient({ 552 | maxAsyncS3: 20, // this is the default 553 | s3RetryCount: 3, // this is the default 554 | s3RetryDelay: 1000, // this is the default 555 | multipartUploadThreshold: 20971520, // this is the default (20 MB) 556 | multipartUploadSize: 15728640, // this is the default (15 MB) 557 | s3Options: { 558 | accessKeyId: taskData.accessKeyId, 559 | secretAccessKey: taskData.secretAccessKey, 560 | region: govRegion 561 | }, 562 | }); 563 | let params = { 564 | localDir: "/home/ec2-user/s3fs/" + taskData.sourceBucket, 565 | deleteRemoved: true, 566 | s3Params: { 567 | Bucket: taskData.destBucket, 568 | }, 569 | }; 570 | //Start Sync 571 | let uploader = client.uploadDir(params); 572 | //Create Heartbeat 573 | heartbeat.push(taskData.taskToken); 574 | //EventEmitters 575 | uploader.on('error', function(err) { 576 | writeToLog("unable to sync: "+err, 'log'); 577 | removeHeartbeat(heartbeat, taskData.taskToken); 578 | umountBucket(taskData.sourceBucket); 579 | sendTaskFailure(taskData.taskToken); 580 | }); 581 | uploader.on('progress', function() { 582 | let msg = "S3 Sync Progress: "+formatBytes(uploader.progressAmount)+" of "+formatBytes(uploader.progressTotal) 583 | writeToLog(msg, "syncProgressLog"); 584 | }); 585 | uploader.on('fileUploadStart', function(localFilePath, s3Key) { 586 | let filePath = localFilePath.replace("/home/ec2-user/s3fs/", ""); 587 | let msg = "Started copying From: s3://"+filePath+" To: s3://"+taskData.destBucket+'/'+s3Key 588 | writeToLog(msg, "s3SyncLog"); 589 | }); 590 | uploader.on('fileUploadEnd', function(localFilePath, s3Key) { 591 | let filePath = localFilePath.replace("/home/ec2-user/s3fs/", ""); 592 | let msg = "Finished copying From: s3://"+filePath+" To: s3://"+taskData.destBucket+'/'+s3Key 593 | writeToLog(msg, "s3SyncLog"); 594 | }); 595 | uploader.on('end', function() { 596 | let msg = "Destination: "+ taskData.destBucket +" has been synchronized with Source: "+ taskData.sourceBucket 597 | writeToLog(msg, 'log'); 598 | sendTaskSuccess(taskData.taskToken); 599 | removeHeartbeat(heartbeat, taskData.taskToken); 600 | umountBucket(taskData.sourceBucket); 601 | }); 602 | } else { 603 | writeToLog("S3FS Commercial S3 Bucket failed to mount", 'log') 604 | } 605 | } catch (err){ 606 | if (err){ 607 | writeToLog(err, 'log'); 608 | } else { 609 | writeToLog('No New S3 Sync Tasks', 'log'); 610 | } 611 | } 612 | } 613 | 614 | //Start S3 Sync Script 615 | syncS3(); 616 | 617 | //Start new poll every 65 seconds, AWS Task long poll is 60 second timeout. 618 | let intervalIdS3 = setInterval(function(){ 619 | if (isEmpty(heartbeat)){ 620 | syncS3(); 621 | } else { 622 | clearInterval(intervalIdS3); 623 | } 624 | }, 65000); 625 | 626 | //Start Image Import Script 627 | copyImage(); 628 | 629 | //Start new poll every 65 seconds, AWS Task long poll is 60 second timeout. 630 | let intervalIdImage = setInterval(function(){ 631 | if (isEmpty(heartbeat)){ 632 | copyImage(); 633 | } else { 634 | clearInterval(intervalIdImage); 635 | } 636 | }, 65000); 637 | -------------------------------------------------------------------------------- /ec2/mountBucket.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ###################################################################################################################### 4 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 5 | # # 6 | # SPDX-License-Identifier: Apache-2.0 # 7 | ###################################################################################################################### 8 | 9 | #Test for IAM Role 10 | iamrole_verify(){ 11 | IAMROLE=$(aws configure list | awk '/iam/ {print $3}' | head -n 1) 12 | if [[ $IAMROLE != 'iam-role' ]]; then 13 | echo "fail:iam" 14 | exit 15 | fi 16 | } 17 | 18 | #Passed Variables 19 | while getopts b: OPTION 20 | do 21 | case "${OPTION}" 22 | in 23 | b) BUCKET=$OPTARG;; 24 | esac 25 | done 26 | 27 | s3_mount(){ 28 | MOUNTED_BUCKET=$(ps aux | awk '/home\/ec2-user\/s3fs\/'$BUCKET'/ {print $12}') 29 | if [[ $MOUNTED_BUCKET != $BUCKET ]]; then 30 | #Make mount point for s3 bucket 31 | if [[ -d "/home/ec2-user/s3fs/$BUCKET" ]]; then 32 | sudo rm -f /home/ec2-user/s3fs/$BUCKET/* 33 | else 34 | sudo mkdir /home/ec2-user/s3fs/$BUCKET 35 | fi 36 | #Mount 37 | sudo s3fs $BUCKET /home/ec2-user/s3fs/$BUCKET/ -o iam_role=auto -o ensure_diskfree=2048 \ 38 | -o parallel_count=15 -o use_cache=/tmp -o allow_other -o readwrite_timeout=180 39 | PS_AUX=("sudo s3fs $BUCKET /home/ec2-user/s3fs/$BUCKET/ -o iam_role=auto -o ensure_diskfree=2048 -o parallel_count=5 -o use_cache=/tmp -o allow_other -o readwrite_timeout=180") 40 | fi 41 | } 42 | 43 | s3_verify(){ 44 | #We're trusting that if its a running process, its up... 45 | MOUNTED_BUCKET=$(ps aux | awk '/home\/ec2-user\/s3fs\/'$BUCKET'/ {print $12}') 46 | if [[ $MOUNTED_BUCKET == $BUCKET ]]; then 47 | STATE="mounted" 48 | #Response to Node 49 | echo "success" 50 | else 51 | echo "fail" 52 | fi 53 | } 54 | 55 | s3_mount 56 | s3_verify 57 | -------------------------------------------------------------------------------- /ec2/umountBucket.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ###################################################################################################################### 4 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 5 | # # 6 | # SPDX-License-Identifier: Apache-2.0 # 7 | ###################################################################################################################### 8 | 9 | #Passed Variables 10 | while getopts b: OPTION 11 | do 12 | case "${OPTION}" 13 | in 14 | b) BUCKET=$OPTARG;; 15 | esac 16 | done 17 | 18 | 19 | s3_verify(){ 20 | NOW=$(date +%a" "%b" "%d" "%Y" "%R:%S" GMT"%z" ("%Z")") 21 | #We're trusting that if its a running process, its up... 22 | MOUNTED_BUCKET=$(ps aux | awk '/home\/ec2-user\/s3fs\/'$BUCKET'/ {print $12}') 23 | MOUNTED_BUCKET_PID=$(ps aux | awk '/home\/ec2-user\/s3fs\/'$BUCKET'/ {print $2}') 24 | if [[ $MOUNTED_BUCKET == $BUCKET ]]; then 25 | echo "$NOW $BUCKET still mounted attempting to kill the process" >> /home/ec2-user/log 26 | kill -15 $MOUNTED_BUCKET_PID 27 | MOUNTED_BUCKET=$(ps aux | awk '/home\/ec2-user\/s3fs\/'$BUCKET'/ {print $12}') 28 | if [[ $MOUNTED_BUCKET == $BUCKET ]]; then 29 | echo "$NOW $BUCKET still mounted and couldn't kill the process" >> /home/ec2-user/log 30 | else 31 | DIR="/home/ec2-user/s3fs/$BUCKET" 32 | # look for empty dir 33 | if [ "$(ls -A $DIR)" ]; then 34 | echo "$NOW $DIR has files, can't remove folder" >> /home/ec2-user/log 35 | else 36 | echo "$NOW $DIR has been removed" >> /home/ec2-user/log 37 | fi 38 | rm -fr /home/ec2-user/s3fs/$BUCKET 39 | fi 40 | else 41 | DIR="/home/ec2-user/s3fs/$BUCKET" 42 | # look for empty dir 43 | if [ "$(ls -A $DIR)" ]; then 44 | echo "$NOW $DIR has files, can't remove folder" >> /home/ec2-user/log 45 | else 46 | echo "$NOW $DIR has been removed" >> /home/ec2-user/log 47 | fi 48 | rm -fr /home/ec2-user/s3fs/$BUCKET 49 | fi 50 | } 51 | 52 | clear_cache(){ 53 | rm -rf /tmp/$bucket 54 | if [[ -d /tmp/$BUCKET ]]; then 55 | rm -rf /tmp/$BUCKET 56 | fi 57 | if [[ -d /tmp/.$BUCKET ]]; then 58 | rm -rf /tmp/.$BUCKET 59 | fi 60 | } 61 | 62 | s3_log(){ 63 | if [ ! -d "/home/ec2-user/s3fs/$BUCKET" ]; then 64 | NOW=$(date +%a" "%b" "%d" "%Y" "%R:%S" GMT"%z" ("%Z")") 65 | echo "$NOW Successfully umount S3 Bucket $BUCKET" >> /home/ec2-user/log 66 | else 67 | echo "$NOW Error on unmount of Bucket $BUCKET or directory removal" >> /home/ec2-user/log 68 | fi 69 | } 70 | 71 | #Unmount Bucket 72 | sleep 3 73 | umount /home/ec2-user/s3fs/$BUCKET 74 | sleep 3 75 | s3_verify 76 | clear_cache 77 | s3_log 78 | -------------------------------------------------------------------------------- /gov-cloud-import-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ###################################################################################################################### 4 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 5 | # # 6 | # SPDX-License-Identifier: Apache-2.0 # 7 | ###################################################################################################################### 8 | clear 9 | cat << "EOF" 10 | _ _ _ _ 11 | __ _ _____ __ ___| | ___ _ _ __| | (_)_ __ ___ _ __ ___ _ __| |_ 12 | / _` |/ _ \ \ / /____ / __| |/ _ \| | | |/ _` |_____| | '_ ` _ \| '_ \ / _ \| '__| __| 13 | | (_| | (_) \ V /_____| (__| | (_) | |_| | (_| |_____| | | | | | | |_) | (_) | | | |_ 14 | \__, |\___/ \_/ \___|_|\___/ \__,_|\__,_| |_|_| |_| |_| .__/ \___/|_| \__| 15 | |___/ |_| 16 | 17 | Written by awsjason 18 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 19 | Licensed under the Apache-2.0 License. 20 | EOF 21 | AWS=$(command -v aws) 22 | if [ -e $AWS ]; then 23 | echo "" 24 | echo "aws cli installed at $AWS" 25 | else 26 | echo "" 27 | echo "aws cli doesn't seem to be installed. Please verify before proceeding." 28 | echo "" 29 | exit 30 | fi 31 | 32 | echo "" 33 | echo "Control-C to exit" 34 | echo "" 35 | echo "Please Enter **Admin** API Keys for Install:" 36 | echo "" 37 | read -p "Admin AWS GovCloud (US) Access Key: " GOV_ACCESS_KEY 38 | read -p "Admin AWS GovCloud (US) Secret Key: " GOV_SECRET_KEY 39 | read -p "Admin AWS Access Key: " COM_ACCESS_KEY 40 | read -p "Admin AWS Secret Key: " COM_SECRET_KEY 41 | clear 42 | cat << "EOF" 43 | _ _ _ _ 44 | __ _ _____ __ ___| | ___ _ _ __| | (_)_ __ ___ _ __ ___ _ __| |_ 45 | / _` |/ _ \ \ / /____ / __| |/ _ \| | | |/ _` |_____| | '_ ` _ \| '_ \ / _ \| '__| __| 46 | | (_| | (_) \ V /_____| (__| | (_) | |_| | (_| |_____| | | | | | | |_) | (_) | | | |_ 47 | \__, |\___/ \_/ \___|_|\___/ \__,_|\__,_| |_|_| |_| |_| .__/ \___/|_| \__| 48 | |___/ |_| 49 | 50 | Written by awsjason 51 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 52 | Licensed under the Apache-2.0 License. 53 | EOF 54 | echo "" 55 | while : 56 | do 57 | echo "Which Region?" 58 | echo " 1. West" 59 | echo " 2. East" 60 | echo -n "Please enter a selection: " 61 | read OPT 62 | case $OPT in 63 | 1) COM_REGION="us-west-2" 64 | GOV_REGION="us-gov-west-1" 65 | CARD_DIR="west" 66 | break;; 67 | 68 | 2) COM_REGION="us-east-2"; 69 | GOV_REGION="us-gov-east-1" 70 | CARD_DIR="east" 71 | break;; 72 | 73 | *) echo "$OPT is an invaild option."; 74 | echo "Press [enter] to continue..." 75 | read enterKey;; 76 | esac 77 | done 78 | echo "" 79 | while : 80 | do 81 | echo "Does $COM_REGION have the limits for another VPC? Please Verify." 82 | echo " 1. Yes" 83 | echo " 2. No" 84 | echo -n "Please enter a selection: " 85 | read OPT 86 | case $OPT in 87 | 1) echo "" 88 | echo "Proceeding..." 89 | break;; 90 | 91 | 2) echo "" 92 | echo "Please fix before proceeding" 93 | exit;; 94 | 95 | *) echo "$OPT is an invaild option."; 96 | echo "Press [enter] to continue..." 97 | read enterKey;; 98 | esac 99 | done 100 | 101 | #Set the creds 102 | aws configure set aws_access_key_id $GOV_ACCESS_KEY --profile gov12345 103 | aws configure set aws_secret_access_key $GOV_SECRET_KEY --profile gov12345 104 | aws configure set region $GOV_REGION --profile gov12345 105 | aws configure set output text --profile gov12345 106 | 107 | aws configure set aws_access_key_id $COM_ACCESS_KEY --profile com12345 108 | aws configure set aws_secret_access_key $COM_SECRET_KEY --profile com12345 109 | aws configure set region $COM_REGION --profile com12345 110 | aws configure set output text --profile com12345 111 | clear 112 | cat << "EOF" 113 | _ _ _ _ 114 | __ _ _____ __ ___| | ___ _ _ __| | (_)_ __ ___ _ __ ___ _ __| |_ 115 | / _` |/ _ \ \ / /____ / __| |/ _ \| | | |/ _` |_____| | '_ ` _ \| '_ \ / _ \| '__| __| 116 | | (_| | (_) \ V /_____| (__| | (_) | |_| | (_| |_____| | | | | | | |_) | (_) | | | |_ 117 | \__, |\___/ \_/ \___|_|\___/ \__,_|\__,_| |_|_| |_| |_| .__/ \___/|_| \__| 118 | |___/ |_| 119 | 120 | Written by awsjason 121 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 122 | Licensed under the Apache-2.0 License. 123 | EOF 124 | echo "" 125 | echo "Thank you. Checking for Previous Installations elements." 126 | echo "" 127 | echo "" 128 | COM_STATUS=$(aws cloudformation describe-stacks --stack-name gov-cloud-import --profile com12345 2> null | awk 'NF>1{print $NF}' | head -n 1) 129 | GOV_STATUS=$(aws cloudformation describe-stacks --stack-name gov-cloud-import --profile gov12345 2> null | awk 'NF>1{print $NF}' | head -n 1) 130 | ROLE_STATUS=$(aws iam get-role --role-name vmimport --profile gov12345 2> null | awk '/vmie.amazonaws.com/ {print $2}') 131 | 132 | if [ "$GOV_STATUS" != "" ] || [ "$COM_STATUS" != "" ]; then 133 | if [[ -z ${GOV_STATUS} ]]; then 134 | GOV_STATUS="Does not exist" 135 | fi 136 | if [[ -z ${COM_STATUS} ]]; then 137 | COM_STATUS="Does not exist" 138 | fi 139 | if [[ -z ${ROLE_STATUS} ]]; then 140 | ROLE_STATUS="Does not exist" 141 | fi 142 | echo "" 143 | echo "Previous Cloudformation detected:" 144 | echo "" 145 | echo "$GOV_REGION status: $GOV_STATUS" 146 | echo "$COM_REGION status: $COM_STATUS" 147 | echo "VMImport Role status: $ROLE_STATUS" 148 | exit 149 | else 150 | echo "" 151 | echo "No Cloudformation detected in $GOV_REGION or $COM_REGION." 152 | echo "VMImport Role status: $ROLE_STATUS" 153 | sleep 5 154 | fi 155 | 156 | clear 157 | cat << "EOF" 158 | _ _ _ _ 159 | __ _ _____ __ ___| | ___ _ _ __| | (_)_ __ ___ _ __ ___ _ __| |_ 160 | / _` |/ _ \ \ / /____ / __| |/ _ \| | | |/ _` |_____| | '_ ` _ \| '_ \ / _ \| '__| __| 161 | | (_| | (_) \ V /_____| (__| | (_) | |_| | (_| |_____| | | | | | | |_) | (_) | | | |_ 162 | \__, |\___/ \_/ \___|_|\___/ \__,_|\__,_| |_|_| |_| |_| .__/ \___/|_| \__| 163 | |___/ |_| 164 | 165 | Written by awsjason 166 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 167 | Licensed under the Apache-2.0 License. 168 | EOF 169 | echo "" 170 | echo "Supplied API Keys will be removed upon completion." 171 | echo "" 172 | echo "Starting gov-cloud-import deployment to $GOV_REGION and $COM_REGION." 173 | echo "" 174 | echo "" 175 | echo "Making S3 Bucket in $COM_REGION" 176 | echo "This is PUBLICALLY EXPOSED to allow access to Web Interface" 177 | echo "" 178 | #Make Bucket 179 | RAND=$(openssl rand -base64 100 | tr -dc 'a-z0-9' | fold -w 16 | head -n 1) 180 | 181 | aws s3 mb s3://gov-cloud-import-$RAND --profile com12345 > /dev/null 182 | 183 | echo '{ 184 | "Version": "2012-10-17", 185 | "Statement": [ 186 | { 187 | "Sid": "PublicListGet", 188 | "Effect": "Allow", 189 | "Principal": "*", 190 | "Action": [ 191 | "s3:List*", 192 | "s3:Get*" 193 | ], 194 | "Resource": [ 195 | "arn:aws:s3:::gov-cloud-import-'$RAND'", 196 | "arn:aws:s3:::gov-cloud-import-'$RAND'/*" 197 | ] 198 | } 199 | ] 200 | } ' > policy.json 201 | 202 | aws s3api put-bucket-policy --bucket gov-cloud-import-$RAND --policy file://policy.json --profile com12345 203 | rm policy.json 204 | #Save Bucket As Parameter 205 | aws ssm put-parameter --name "gov-cloud-import-app" --type "String" --value "gov-cloud-import-$RAND" --overwrite --profile com12345 > /dev/null 206 | 207 | echo "Copying App to S3 Bucket in $COM_REGION" 208 | echo "" 209 | #Sync the local repo clone to new s3 buckets 210 | aws s3 sync ./ s3://gov-cloud-import-$RAND --quiet --profile com12345 --exclude gov-cloud-import-install.sh 211 | 212 | #Update Scripts with New Bucket Location 213 | #BUCKET_BASE_URL="https://s3.$COM_REGION.amazonaws.com/gov-cloud-import-$RAND" 214 | BUCKET_BASE="gov-cloud-import-$RAND" 215 | BUCKET_BASE_URL="https://s3.$COM_REGION.amazonaws.com/gov-cloud-import-$RAND" 216 | 217 | sed -ie "s||$BUCKET_BASE|g" ec2/gov-cloud-import-ec2prep.sh 218 | sed -ie "s||$BUCKET_BASE|g" cloudformation/gov-cloud-import-commercial.json 219 | 220 | #Zip up lambda files 221 | echo "Compressing Lambda Functions" 222 | echo "" 223 | chmod -R 755 lambda/ 224 | cd lambda 225 | for i in `find . -type d | tail -n +2 | tr -d './'` 226 | do 227 | if [[ $i == "." ]]; then 228 | echo "Root Directory" 229 | else 230 | cd $i 231 | zip -X ../$i.zip index.js 232 | cd ../ 233 | fi 234 | done 235 | cd .. 236 | echo "" 237 | echo "Synchronizing files to S3 bucket" 238 | #Sync Files to Bucket 239 | aws s3 sync ./ s3://gov-cloud-import-$RAND --profile com12345 --quiet --exclude gov-cloud-import-install.sh 240 | 241 | echo "Deploying CloudFormations" 242 | #Start Cloudformation 243 | aws cloudformation create-stack --stack-name gov-cloud-import --template-body file://cloudformation/gov-cloud-import-commercial.json --capabilities CAPABILITY_IAM --profile com12345 244 | aws cloudformation create-stack --stack-name gov-cloud-import --template-body file://cloudformation/gov-cloud-import-govcloud-$CARD_DIR.json --capabilities CAPABILITY_NAMED_IAM --profile gov12345 245 | if [ "$ROLE_STATUS" != "vmie.amazonaws.com" ]; then 246 | aws cloudformation create-stack --stack-name gov-cloud-import-vmimport --template-body file://cloudformation/gov-cloud-import-govcloud-vmimport.json --capabilities CAPABILITY_NAMED_IAM --profile gov12345 247 | fi 248 | #Watch Formations 249 | echo "" 250 | echo "Script will check Cloudformation status every 30 seconds until complete." 251 | echo "This will take 5+ mins to complete." 252 | echo "" 253 | 254 | while : 255 | do 256 | COM_STATUS=$(aws cloudformation describe-stacks --stack-name gov-cloud-import --profile com12345 | awk 'NF>1{print $NF}' | head -n 1) 257 | GOV_STATUS=$(aws cloudformation describe-stacks --stack-name gov-cloud-import --profile gov12345 | awk 'NF>1{print $NF}' | head -n 1) 258 | NOW=$(date +%Y-%m-%d_%H:%M:%S) 259 | if [ $GOV_STATUS == "CREATE_IN_PROGRESS" ] || [ $COM_STATUS == "CREATE_IN_PROGRESS" ]; then 260 | echo "$NOW AWS GovCloud (US) Status: $GOV_STATUS" 261 | echo "$NOW AWS Status: $COM_STATUS" 262 | elif [ $GOV_STATUS == "ROLLBACK_IN_PROGRESS" ] && [ $COM_STATUS == "ROLLBACK_IN_PROGRESS" ]; then 263 | echo "" 264 | echo "Something went wrong with Cloudformation" 265 | echo "$NOW AWS GovCloud (US) Status: $GOV_STATUS" 266 | echo "$NOW AWS Status: $COM_STATUS" 267 | exit 268 | elif [ $GOV_STATUS == "CREATE_COMPLETE" ] && [ $COM_STATUS == "CREATE_COMPLETE" ]; then 269 | echo "$NOW AWS GovCloud (US) Status: $GOV_STATUS" 270 | echo "$NOW AWS Status: $COM_STATUS" 271 | break 272 | fi 273 | sleep 30 274 | done 275 | 276 | 277 | #Make AWS GovCloud (US) API Keys 278 | echo "" 279 | echo "Creating AWS GovCloud (US) Access Keys and Storing them in AWS SSM Parameters." 280 | CREATE_KEYS=$(aws iam create-access-key --user-name gov-cloud-import-user-$CARD_DIR --profile gov12345) 281 | ACCESS_KEY=$(echo $CREATE_KEYS | awk '{print $2}') 282 | SECRET_KEY=$(echo $CREATE_KEYS | awk '{print $4}') 283 | GOV_BUCKET=$(aws cloudformation describe-stacks --stack-name gov-cloud-import --profile gov12345 | awk '/gov-cloud-import-bucket*/ {print $8}') 284 | echo $GOV_BUCKET 285 | #Submit Parameters to AWS 286 | aws ssm put-parameter --name "gov-cloud-import-accessKey" --overwrite --type "SecureString" --value $ACCESS_KEY --profile com12345 > /dev/null 287 | aws ssm put-parameter --name "gov-cloud-import-secretKey" --overwrite --type "SecureString" --value $SECRET_KEY --profile com12345 > /dev/null 288 | aws ssm put-parameter --name "gov-cloud-import-s3Bucket" --overwrite --type "String" --value $GOV_BUCKET --profile com12345 > /dev/null 289 | 290 | #Remove Creds/Keys and Config 291 | clear 292 | cat << "EOF" 293 | _ _ _ _ 294 | __ _ _____ __ ___| | ___ _ _ __| | (_)_ __ ___ _ __ ___ _ __| |_ 295 | / _` |/ _ \ \ / /____ / __| |/ _ \| | | |/ _` |_____| | '_ ` _ \| '_ \ / _ \| '__| __| 296 | | (_| | (_) \ V /_____| (__| | (_) | |_| | (_| |_____| | | | | | | |_) | (_) | | | |_ 297 | \__, |\___/ \_/ \___|_|\___/ \__,_|\__,_| |_|_| |_| |_| .__/ \___/|_| \__| 298 | |___/ |_| 299 | 300 | Written by awsjason 301 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 302 | Licensed under the Apache-2.0 License. 303 | EOF 304 | echo "" 305 | echo "Removing Install API Keys and Config." 306 | sed -e :a -e '$d;N;2,6ba' -e 'P;D' ~/.aws/credentials > file.txt 307 | chmod 600 file.txt 308 | mv file.txt ~/.aws/credentials 309 | sed -e :a -e '$d;N;2,6ba' -e 'P;D' ~/.aws/config > file.txt 310 | chmod 600 file.txt 311 | mv file.txt ~/.aws/config 312 | 313 | 314 | #Display S3 Bucket 315 | echo "" 316 | echo "Install of gov-cloud-import Complete!" 317 | echo "" 318 | echo "Your import web interface is ready @ $BUCKET_BASE_URL/index.html" 319 | echo "" 320 | echo "This Read-only bucket contains a copy of the application from Github, but was created" 321 | echo "PUBLICLY EXPOSED. Please stay within your company's security posture." 322 | echo "" 323 | echo "" 324 | echo "" 325 | -------------------------------------------------------------------------------- /lambda/appStatus/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ssm = new AWS.SSM(); 11 | let region = process.env.AWS_REGION; 12 | let govRegion; 13 | let status = []; 14 | let version = {}; 15 | version.version = "1.0"; 16 | status.push(version); 17 | 18 | //Determine which GovCloud Region we should be using 19 | function getGovCloudRegion(){ 20 | return new Promise(function(resolve, reject) { 21 | if (region == 'us-west-2') { 22 | govRegion = 'us-gov-west-1'; 23 | resolve(govRegion); 24 | } else if (region == 'us-east-2'){ 25 | govRegion = 'us-gov-east-1'; 26 | resolve(govRegion); 27 | } 28 | }); 29 | } 30 | 31 | //Get Values from Parameter Store 32 | function getParameter(value){ 33 | return new Promise(function(resolve, reject) { 34 | let params = { 35 | Name: value, 36 | WithDecryption: true 37 | }; 38 | ssm.getParameter(params, function(err, data) { 39 | if (err) { 40 | console.log(err); 41 | reject(err); 42 | } else { 43 | resolve(data.Parameter.Value); 44 | } 45 | }); 46 | }); 47 | } 48 | 49 | //GovCloud CloudFormation Status 50 | function govCloudFormationStatus(paramsSSM){ 51 | return new Promise((resolve, reject) => { 52 | let cloudformation = new AWS.CloudFormation({ 53 | region: paramsSSM.govRegion, 54 | accessKeyId: paramsSSM.accessKey, 55 | secretAccessKey: paramsSSM.secretKey 56 | }); 57 | let params = { 58 | StackName: 'gov-cloud-import' 59 | }; 60 | let temp = {}; 61 | cloudformation.describeStacks(params, function(err, data) { 62 | if (err){ 63 | if (err && err.statusCode == '400') { 64 | temp.region = paramsSSM.govRegion; 65 | temp.status = "Not Installed"; 66 | status.push(temp); 67 | resolve("Not Installed"); 68 | } else { 69 | console.log(err); 70 | reject(err); 71 | } 72 | } else { 73 | temp.region = paramsSSM.govRegion; 74 | temp.status = data.Stacks[0].StackStatus; 75 | temp.create = data.Stacks[0].CreationTime; 76 | status.push(temp); 77 | resolve(); 78 | } 79 | }); 80 | }); 81 | } 82 | 83 | //GovCloud CloudFormation Status 84 | function comCloudFormationStatus(){ 85 | return new Promise((resolve, reject) => { 86 | let cloudformation = new AWS.CloudFormation; 87 | let params = { 88 | StackName: 'gov-cloud-import' 89 | }; 90 | let temp = {}; 91 | cloudformation.describeStacks(params, function(err, data) { 92 | if (err){ 93 | if (err && err.statusCode == '400') { 94 | temp.region = process.env.AWS_REGION; 95 | temp.status = "Not Installed"; 96 | status.push(temp); 97 | resolve("Not Installed"); 98 | } else { 99 | console.log(err); 100 | reject(err); 101 | } 102 | } else { 103 | temp.region = process.env.AWS_REGION; 104 | temp.status = data.Stacks[0].StackStatus; 105 | temp.create = data.Stacks[0].CreationTime; 106 | status.push(temp); 107 | resolve(); 108 | } 109 | }); 110 | }); 111 | } 112 | 113 | exports.handler = (event, context, callback) => { 114 | let paramsSSM = event; 115 | getParameter("gov-cloud-import-accessKey") 116 | .then(function(accessKey){ 117 | paramsSSM.accessKey = accessKey; 118 | return getParameter("gov-cloud-import-secretKey"); 119 | }) 120 | .then(function(secretKey){ 121 | paramsSSM.secretKey = secretKey; 122 | return getParameter("gov-cloud-import-s3Bucket"); 123 | }) 124 | .then(function(s3BucketGov){ 125 | paramsSSM.s3BucketGov = s3BucketGov; 126 | return getGovCloudRegion(); 127 | }) 128 | .then(function(govRegion){ 129 | paramsSSM.govRegion = govRegion; 130 | return govCloudFormationStatus(paramsSSM); 131 | }) 132 | .then(function(){ 133 | return comCloudFormationStatus(); 134 | }) 135 | .then(function(){ 136 | callback(null, status); 137 | }) 138 | .catch(function(err){ 139 | console.log(err); 140 | callback(err); 141 | }); 142 | 143 | }; 144 | -------------------------------------------------------------------------------- /lambda/cleanUp/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | //Set Variables 10 | var AWS = require('aws-sdk'); 11 | var ec2 = new AWS.EC2(); 12 | let snapshot; 13 | 14 | exports.handler = (event, context, callback) => { 15 | //Define Delete Snapshot 16 | function deleteSnapshot(){ 17 | var params = { 18 | SnapshotId: snapshot, 19 | }; 20 | ec2.deleteSnapshot(params, function(err, data) { 21 | if (err) { 22 | console.log(err); 23 | callback(err); 24 | } else { 25 | console.log(data); 26 | callback(null, "Snapshot removed"); 27 | } 28 | }); 29 | } 30 | //Define where we have a Snapshot or AMI 31 | const snap = event.imageNew.startsWith("snap"); 32 | const ami = event.imageNew.startsWith("ami"); 33 | if (ami == true){ 34 | //Find AMI Snapshot ID 35 | let params = { 36 | ImageIds: [ 37 | event.imageNew, 38 | ] 39 | }; 40 | ec2.describeImages(params, function(err, data) { 41 | if (err) { 42 | console.log(err); 43 | callback(err); 44 | } else { 45 | console.log(data); 46 | //Snapshot associated with AMI 47 | snapshot = data.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId; 48 | //Deregister AMI and Delete Snapshot 49 | let params = { 50 | ImageId: event.imageNew 51 | }; 52 | ec2.deregisterImage(params, function(err, data) { 53 | if (err) { 54 | console.log(err); 55 | callback(err); 56 | } else { 57 | console.log(data); 58 | deleteSnapshot(); 59 | } 60 | }); 61 | } 62 | }); 63 | } else if (snap == true) { 64 | snapshot = event.imageNew; 65 | //Delete Snapshot 66 | deleteSnapshot(); 67 | } else { 68 | console.log("Not a valid Snapshot or AMI"); 69 | callback(null,"Not a valid Snapshot or AMI"); 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /lambda/ec2Run/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ec2 = new AWS.EC2(); 11 | const ssm = new AWS.SSM(); 12 | 13 | let keyName = "gov-cloud-import-ec2-"+process.env.AWS_REGION; 14 | let ami; 15 | let userDataEncoded; 16 | let region = process.env.AWS_REGION; 17 | let ec2param = {}; 18 | if (region == 'us-west-2') { 19 | ami = 'ami-32cf7b4a'; 20 | } else if (region == 'us-east-2'){ 21 | ami = 'ami-caaf84af'; 22 | } 23 | 24 | //Test if object is empty 25 | function isEmpty(obj) { 26 | for(let key in obj) { 27 | if(obj.hasOwnProperty(key)) 28 | return false; 29 | } 30 | return true; 31 | } 32 | 33 | //User Data Script for EC2 34 | function findEC2PrepScript() { 35 | return new Promise(function(resolve, reject) { 36 | let params = { 37 | Name: 'gov-cloud-import-app', 38 | WithDecryption: true 39 | }; 40 | ssm.getParameter(params, function(err, data) { 41 | if (err) { 42 | console.log(err); 43 | reject(err); 44 | } else { 45 | //let url = 'https://s3.'+region+'.amazonaws.com/'+data.Parameter.Value+'/ec2/gov-cloud-import-ec2prep.sh' 46 | let s3 = 's3://'+data.Parameter.Value+'/ec2/gov-cloud-import-ec2prep.sh'; 47 | let userData= 48 | `#!/bin/bash 49 | aws s3 cp ${s3} ./ 50 | chmod +x gov-cloud-import-ec2prep.sh 51 | ./gov-cloud-import-ec2prep.sh 52 | `; 53 | //Base64 Encode User Data 54 | userDataEncoded = new Buffer(userData).toString('base64'); 55 | //Resolve with Parameter Value 56 | resolve(userDataEncoded); 57 | } 58 | }); 59 | }); 60 | } 61 | 62 | function findKeyPair(){ 63 | return new Promise(function(resolve, reject) { 64 | var params = { 65 | Filters: [ 66 | { Name: 'key-name', 67 | Values: ['gov-cloud-import-ec2-*',] 68 | } 69 | ] 70 | }; 71 | ec2.describeKeyPairs(params, function(err, data) { 72 | if (err) { 73 | console.log(err); 74 | reject(err); 75 | } else { 76 | resolve(data); 77 | } 78 | }); 79 | }); 80 | } 81 | 82 | function createKeyPair(){ 83 | return new Promise(function(resolve, reject) { 84 | var params = { 85 | KeyName: keyName 86 | }; 87 | ec2.createKeyPair(params, function(err, data) { 88 | if (err) { 89 | reject(err, err.stack); 90 | } else { 91 | var paramsSSM = { 92 | Name: keyName, 93 | Type: 'SecureString', 94 | Value: data.KeyMaterial, 95 | Overwrite: true 96 | }; 97 | ssm.putParameter(paramsSSM, function(err, data) { 98 | if (err) { 99 | console.log(err); 100 | reject(err); 101 | } else { 102 | console.log(data); 103 | resolve(data); 104 | } 105 | }); 106 | } 107 | }); 108 | }); 109 | } 110 | 111 | function findRoleArn() { 112 | return new Promise(function(resolve, reject) { 113 | let params = { 114 | Name: 'gov-cloud-import-ec2RoleArn', 115 | WithDecryption: true 116 | }; 117 | ssm.getParameter(params, function(err, data) { 118 | if (err) { 119 | console.log(err); 120 | reject(err); 121 | } else { 122 | //Resolve with Parameter Value 123 | resolve(data.Parameter.Value); 124 | } 125 | }); 126 | }); 127 | } 128 | 129 | function findSecurityGroup() { 130 | return new Promise(function(resolve, reject) { 131 | let params = { 132 | Name: 'gov-cloud-import-sg', 133 | WithDecryption: true 134 | }; 135 | ssm.getParameter(params, function(err, data) { 136 | if (err) { 137 | console.log(err); 138 | reject(err); 139 | } else { 140 | //Resolve with Parameter Value 141 | resolve(data.Parameter.Value); 142 | } 143 | }); 144 | }); 145 | } 146 | 147 | //Find any running Instance 148 | function describeInst(){ 149 | return new Promise(function(resolve, reject) { 150 | let params = { 151 | Filters: [ 152 | { Name: 'tag:gov-cloud-import', 153 | Values: ['true'] 154 | }, 155 | { Name: 'instance-state-name', 156 | Values: ['running', 'pending', 'stopped'] 157 | } 158 | ] 159 | }; 160 | ec2.describeInstances(params, function(err, data) { 161 | if (err) { 162 | console.log(err); 163 | reject(err); 164 | } else { 165 | resolve(data); 166 | } 167 | }); 168 | }); 169 | } 170 | 171 | //Start a stopped instance 172 | function startInst(instanceId){ 173 | return new Promise(function(resolve, reject) { 174 | let params = {InstanceIds: [instanceId]}; 175 | ec2.startInstances(params, function(err, data) { 176 | if (err) { 177 | console.log(err); 178 | reject(err); 179 | } else { 180 | console.log(data); 181 | //Respond with pending so we check on it again 182 | resolve("pending"); 183 | } 184 | }); 185 | }); 186 | } 187 | 188 | //Run New Instance 189 | function runInst(){ 190 | return new Promise(function(resolve, reject) { 191 | let paramsSub = { 192 | Filters: [ 193 | { Name: 'tag:gov-cloud-import', 194 | Values: ['true'] 195 | } 196 | ] 197 | }; 198 | ec2.describeSubnets(paramsSub, function(err, data) { 199 | if (err) { 200 | reject(err, err.stack); 201 | } else { 202 | const subnetId = data.Subnets[0].SubnetId; 203 | let params = { 204 | MaxCount: 1, 205 | MinCount: 1, 206 | ImageId: ami, 207 | InstanceType: "m5.large", 208 | KeyName: keyName, 209 | IamInstanceProfile: { 210 | Arn: ec2param.arn, 211 | }, 212 | SubnetId: subnetId, 213 | SecurityGroupIds: [ec2param.sg], 214 | TagSpecifications: [ 215 | { ResourceType: "instance", 216 | Tags: [ 217 | { Key: 'gov-cloud-import', 218 | Value: 'true' 219 | }, 220 | { Key: 'Name', 221 | Value: 'gov-cloud-import-ec2' 222 | } 223 | ] 224 | }, 225 | ], 226 | UserData: userDataEncoded 227 | }; 228 | ec2.runInstances(params, function(err, data) { 229 | if (err) { 230 | console.log(err); 231 | reject(err); 232 | } else { 233 | //Respond with pending so we'll check on it again 234 | resolve("pending"); 235 | } 236 | }); 237 | } 238 | }); 239 | }); 240 | } 241 | 242 | exports.handler = (event, context, callback) => { 243 | findEC2PrepScript(); 244 | findKeyPair() 245 | .then(function(findPair){ 246 | if(isEmpty(findPair.KeyPairs)){ 247 | createKeyPair(); 248 | } else { 249 | ec2param.keyName = findPair.KeyPairs[0].KeyName; 250 | } 251 | return findRoleArn(); 252 | }) 253 | .then(function(arn){ 254 | ec2param.arn = arn; 255 | return findSecurityGroup(); 256 | }) 257 | .then(function(sg){ 258 | ec2param.sg = sg; 259 | return describeInst(); 260 | }) 261 | .then(function(data){ 262 | //If none, run a new instance 263 | if (isEmpty(data.Reservations)){ 264 | return runInst(); 265 | } else { 266 | //If exists, set state. 267 | const instanceId = data.Reservations[0].Instances[0].InstanceId; 268 | const instanceState = data.Reservations[0].Instances[0].State.Name; 269 | if (instanceState == 'pending') { 270 | callback(null, "pending"); 271 | } else if (instanceState == 'running'){ 272 | callback(null, "running"); 273 | } else if (instanceState == 'stopped'){ 274 | return startInst(instanceId); 275 | } 276 | } 277 | }) 278 | .then(function(data){ 279 | callback(null, data); 280 | }) 281 | .catch(function(err){ 282 | console.log(err); 283 | callback(err); 284 | }); 285 | }; 286 | -------------------------------------------------------------------------------- /lambda/importImage/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ssm = new AWS.SSM(); 11 | let govRegion; 12 | let region = process.env.AWS_REGION; 13 | 14 | //Determine which GovCloud Region we should be using 15 | function getGovCloudRegion(){ 16 | return new Promise(function(resolve, reject) { 17 | if (region == 'us-west-2') { 18 | govRegion = 'us-gov-west-1'; 19 | resolve(govRegion); 20 | } else if (region == 'us-east-2'){ 21 | govRegion = 'us-gov-east-1'; 22 | resolve(govRegion); 23 | } 24 | }); 25 | } 26 | 27 | //Get Values from Parameter Store 28 | function getParameter(value){ 29 | return new Promise(function(resolve, reject) { 30 | let params = { 31 | Name: value, 32 | WithDecryption: true 33 | }; 34 | ssm.getParameter(params, function(err, data) { 35 | if (err) { 36 | console.log(err); 37 | reject(err); 38 | } else { 39 | resolve(data.Parameter.Value); 40 | } 41 | }); 42 | }); 43 | } 44 | 45 | //Import Image cmd to GovCloud - Windows 46 | function importWindows(paramsSSM){ 47 | return new Promise(function(resolve, reject) { 48 | let ec2 = new AWS.EC2({ 49 | region: paramsSSM.govRegion, 50 | accessKeyId: paramsSSM.accessKey, 51 | secretAccessKey: paramsSSM.secretKey 52 | }); 53 | let params = { 54 | Description: 'Import: '+paramsSSM.image+' from '+paramsSSM.region, 55 | DiskContainers: [ 56 | { 57 | Description: 'Import: '+paramsSSM.image+' from '+paramsSSM.region, 58 | Format: 'RAW', 59 | UserBucket: { 60 | S3Bucket: paramsSSM.s3BucketGov, 61 | S3Key: paramsSSM.volumeId 62 | } 63 | }, 64 | ], 65 | LicenseType: 'BYOL', 66 | Platform: paramsSSM.os, 67 | }; 68 | ec2.importImage(params, function(err, data) { 69 | if (err) { 70 | console.log(err); 71 | reject(err); 72 | } else { 73 | resolve(data); 74 | } 75 | }); 76 | }); 77 | } 78 | 79 | //Import Image cmd to GovCloud - Linux 80 | function importLinux(paramsSSM){ 81 | return new Promise(function(resolve, reject) { 82 | let ec2 = new AWS.EC2({ 83 | region: paramsSSM.govRegion, 84 | accessKeyId: paramsSSM.accessKey, 85 | secretAccessKey: paramsSSM.secretKey 86 | }); 87 | let params = { 88 | Description: 'Import: '+paramsSSM.image+' from '+paramsSSM.region, 89 | DiskContainer: { 90 | Description: 'Import: '+paramsSSM.image+' from '+paramsSSM.region, 91 | Format: 'RAW', 92 | UserBucket: { 93 | S3Bucket: paramsSSM.s3BucketGov, 94 | S3Key: paramsSSM.volumeId 95 | } 96 | } 97 | }; 98 | ec2.importSnapshot(params, function(err, data) { 99 | if (err) { 100 | console.log(err); 101 | reject(err); 102 | } else { 103 | resolve(data); 104 | } 105 | }); 106 | }); 107 | } 108 | 109 | exports.handler = (event, context, callback) => { 110 | let paramsSSM = event; 111 | getParameter("gov-cloud-import-accessKey") 112 | .then(function(accessKey){ 113 | paramsSSM.accessKey = accessKey; 114 | return getParameter("gov-cloud-import-secretKey"); 115 | }) 116 | .then(function(secretKey){ 117 | paramsSSM.secretKey = secretKey; 118 | return getParameter("gov-cloud-import-s3Bucket"); 119 | }) 120 | .then(function(s3BucketGov){ 121 | paramsSSM.s3BucketGov = s3BucketGov; 122 | return getGovCloudRegion(); 123 | }) 124 | .then(function(govRegion){ 125 | paramsSSM.govRegion = govRegion; 126 | if (paramsSSM.os == "Windows"){ 127 | return importWindows(paramsSSM); 128 | } else if (paramsSSM.os == "Linux"){ 129 | return importLinux(paramsSSM); 130 | } 131 | }) 132 | .then(function(importData){ 133 | console.log(JSON.stringify(importData)); 134 | callback(null, importData.ImportTaskId); 135 | }) 136 | .catch(function(err){ 137 | console.log(err); 138 | callback(err); 139 | }); 140 | 141 | }; 142 | -------------------------------------------------------------------------------- /lambda/importImageStatus/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ssm = new AWS.SSM; 11 | const lambda = new AWS.Lambda(); 12 | let now = Date.now(); 13 | let govRegion; 14 | let paramsSSM = {}; 15 | let region = process.env.AWS_REGION; 16 | 17 | //Determine which GovCloud Region we should be using 18 | function getGovCloudRegion(){ 19 | return new Promise(function(resolve, reject) { 20 | if (region == 'us-west-2') { 21 | govRegion = 'us-gov-west-1'; 22 | resolve(govRegion); 23 | } else if (region == 'us-east-2'){ 24 | govRegion = 'us-gov-east-1'; 25 | resolve(govRegion); 26 | } 27 | }); 28 | } 29 | 30 | //Get Values from Parameter Store 31 | function getParameter(value){ 32 | return new Promise(function(resolve, reject) { 33 | let params = { 34 | Name: value, 35 | WithDecryption: true 36 | }; 37 | ssm.getParameter(params, function(err, data) { 38 | if (err) { 39 | console.log(err); 40 | reject(err); 41 | } else { 42 | resolve(data.Parameter.Value); 43 | } 44 | }); 45 | }); 46 | } 47 | 48 | function importStatusWindows(paramsSSM){ 49 | return new Promise(function(resolve, reject) { 50 | let ec2 = new AWS.EC2({ 51 | region: paramsSSM.govRegion, 52 | accessKeyId: paramsSSM.accessKey, 53 | secretAccessKey: paramsSSM.secretKey 54 | }); 55 | var params = { 56 | ImportTaskIds: [ 57 | paramsSSM.importTaskId 58 | ], 59 | }; 60 | ec2.describeImportImageTasks(params, function(err, data) { 61 | if (err) { 62 | console.log(err); 63 | reject(err); 64 | } else { 65 | console.log(JSON.stringify(data)); 66 | let status = data.ImportImageTasks[0].Status; 67 | if (status == "completed"){ 68 | paramsSSM.govImageId = data.ImportImageTasks[0].ImageId; 69 | resolve(status); 70 | } else { 71 | resolve(status); 72 | } 73 | } 74 | }); 75 | }); 76 | } 77 | 78 | function importStatusLinux(paramsSSM){ 79 | return new Promise(function(resolve, reject) { 80 | let ec2 = new AWS.EC2({ 81 | region: paramsSSM.govRegion, 82 | accessKeyId: paramsSSM.accessKey, 83 | secretAccessKey: paramsSSM.secretKey 84 | }); 85 | var params = { 86 | ImportTaskIds: [ 87 | paramsSSM.importTaskId 88 | ], 89 | }; 90 | ec2.describeImportSnapshotTasks(params, function(err, data) { 91 | if (err) { 92 | console.log(err); 93 | reject(err); 94 | } else { 95 | //Grab the Snapshot Id so we can register the AMI Image 96 | let status = data.ImportSnapshotTasks[0].SnapshotTaskDetail.Status; 97 | if (status == "completed"){ 98 | paramsSSM.snapshotId = data.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId; 99 | resolve(status); 100 | } 101 | resolve(status); 102 | } 103 | }); 104 | }); 105 | } 106 | 107 | //Find Lambda function 108 | function snsPublish(event){ 109 | return new Promise((resolve, reject) => { 110 | //Params for Lambda invoke 111 | let params = {}; 112 | // Call the Lambda function 113 | lambda.listFunctions(params, function(err, data) { 114 | if (err) { 115 | console.log(err); 116 | reject(err); 117 | } else { 118 | let length = data.Functions.length; 119 | for (let index = 0; index < length; ++index) { 120 | let str = data.Functions[index].FunctionName; 121 | if(str.startsWith("gov-cloud-import-snsPublish")){ 122 | //Params for Lambda invoke 123 | let params = { 124 | FunctionName : str, 125 | InvocationType : 'RequestResponse', 126 | LogType : 'Tail', 127 | Payload : JSON.stringify(event) 128 | }; 129 | //console.log(params) 130 | // Call the Lambda function 131 | lambda.invoke(params, function(err, data) { 132 | if (err) { 133 | console.log(err); 134 | reject(err); 135 | } else { 136 | resolve(data); 137 | } 138 | }); 139 | } 140 | } 141 | } 142 | }); 143 | }); 144 | } 145 | 146 | //This is command has to be run since linux has to be imported as a snapshot, then registered. 147 | function registerImage(paramsSSM){ 148 | return new Promise((resolve, reject) => { 149 | if (paramsSSM.os == "Linux"){ 150 | let ec2 = new AWS.EC2({ 151 | region: paramsSSM.govRegion, 152 | accessKeyId: paramsSSM.accessKey, 153 | secretAccessKey: paramsSSM.secretKey 154 | }); 155 | let params = { 156 | Architecture: 'x86_64', 157 | Name: paramsSSM.image+' from Commercial '+now, 158 | BlockDeviceMappings: [ 159 | { 160 | DeviceName: '/dev/sda1', 161 | Ebs: { 162 | SnapshotId: paramsSSM.snapshotId 163 | } 164 | }, 165 | ], 166 | RootDeviceName: '/dev/sda1', 167 | Description: paramsSSM.image+' from Commercial', 168 | VirtualizationType: 'hvm' 169 | }; 170 | ec2.registerImage(params, function(err, data) { 171 | if (err) { 172 | console.log(err); 173 | reject(err); 174 | } else { 175 | resolve(data.ImageId); 176 | } 177 | }); 178 | } else { 179 | resolve(); 180 | } 181 | }); 182 | } 183 | 184 | function getStatus(paramsSSM){ 185 | return new Promise((resolve, reject) => { 186 | if (paramsSSM.os == "Windows"){ 187 | resolve(importStatusWindows(paramsSSM)); 188 | } else if (paramsSSM.os == "Linux"){ 189 | resolve(importStatusLinux(paramsSSM)); 190 | } else { 191 | resolve(); 192 | } 193 | }); 194 | } 195 | 196 | exports.handler = (event, context, callback) => { 197 | paramsSSM = event; 198 | getParameter("gov-cloud-import-accessKey") 199 | .then(function(accessKey){ 200 | paramsSSM.accessKey = accessKey; 201 | return getParameter("gov-cloud-import-secretKey"); 202 | }) 203 | .then(function(secretKey){ 204 | paramsSSM.secretKey = secretKey; 205 | return getParameter("gov-cloud-import-s3Bucket"); 206 | }) 207 | .then(function(s3BucketGov){ 208 | paramsSSM.s3BucketGov = s3BucketGov; 209 | return getGovCloudRegion(); 210 | }) 211 | .then(function(govRegion){ 212 | paramsSSM.govRegion = govRegion; 213 | return getStatus(paramsSSM); 214 | }) 215 | .then(function(status){ 216 | paramsSSM.status = status; 217 | event.importImageStatus = status; 218 | //End the functions and send Step Functions back to waiting 219 | if (status == "active"){ 220 | callback(null, status); 221 | } 222 | return registerImage(paramsSSM); 223 | }) 224 | .then(function(govImageId){ 225 | paramsSSM.govImageId = govImageId; 226 | return snsPublish(event); 227 | }) 228 | .then(function(){ 229 | callback(null, paramsSSM.status); 230 | }) 231 | .catch(function(err){ 232 | console.log(err); 233 | callback("failed"); 234 | }); 235 | 236 | }; 237 | -------------------------------------------------------------------------------- /lambda/initS3Sync/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | // Load the AWS SDK 10 | const AWS = require('aws-sdk'); 11 | const ssm = new AWS.SSM(); 12 | const stepfunctions = new AWS.StepFunctions(); 13 | 14 | //Find StateMachine's ARN to start workflow 15 | function findStateMchineArn() { 16 | return new Promise(function(resolve, reject) { 17 | let params = {}; 18 | stepfunctions.listStateMachines(params, function(err, data) { 19 | if (err) { 20 | console.log(err); 21 | reject(err); 22 | } // an error occurred 23 | else { 24 | let length = data.stateMachines.length; 25 | for (let index = 0; index < length; ++index) { 26 | let str = data.stateMachines[index].name; 27 | if(str.startsWith("s3Sync")){ 28 | resolve(data.stateMachines[index].stateMachineArn); 29 | } 30 | } 31 | resolve(data); 32 | } 33 | }); 34 | }); 35 | } 36 | //Execute the workflow with parameters 37 | function execStateMachine(myString, arn){ 38 | return new Promise(function(resolve, reject) { 39 | //Set params for Step Functions Exec 40 | const params = { 41 | stateMachineArn: arn, 42 | input: myString 43 | }; 44 | 45 | //Exec Step Function 46 | stepfunctions.startExecution(params, function(err, data) { 47 | if (err) { 48 | console.log(err); 49 | reject(err); 50 | } // an error occurred 51 | else { 52 | console.log(data); 53 | resolve(data); 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | exports.handler = (event, context, callback) => { 60 | // Input into JSON to Pass to Step Functions 61 | const myObject = { 62 | 'sourceBucket': event.source, 63 | 'destBucket' : event.dest, 64 | 'waitEc2' : '120', 65 | 'topic' : 'gov-cloud-import-s3' 66 | }; 67 | 68 | // Convert to string 69 | const myString = JSON.stringify(myObject); 70 | findStateMchineArn() 71 | .then(function(arn){ 72 | //Get ARN 73 | let arnStateMachine = arn; 74 | return execStateMachine(myString, arnStateMachine); 75 | }) 76 | .then(function(data){ 77 | callback(null, data); 78 | }) 79 | .catch(function(err){ 80 | console.log(err); 81 | callback(err); 82 | }); 83 | 84 | 85 | }; 86 | -------------------------------------------------------------------------------- /lambda/initStepFunction/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | // Load the AWS SDK 10 | const AWS = require('aws-sdk'); 11 | const stepfunctions = new AWS.StepFunctions(); 12 | 13 | //Find StateMachine's ARN to start workflow 14 | function findStateMchineArn() { 15 | return new Promise(function(resolve, reject) { 16 | let params = {}; 17 | stepfunctions.listStateMachines(params, function(err, data) { 18 | if (err) { 19 | console.log(err); 20 | reject(err); 21 | } // an error occurred 22 | else { 23 | let length = data.stateMachines.length; 24 | for (let index = 0; index < length; ++index) { 25 | let str = data.stateMachines[index].name; 26 | if(str.startsWith("ImageStateMachine")){ 27 | console.log(data.stateMachines[index].stateMachineArn); 28 | resolve(data.stateMachines[index].stateMachineArn); 29 | } 30 | } 31 | } 32 | }); 33 | }); 34 | } 35 | //Execute the workflow with parameters 36 | function execStateMachine(myString, arn){ 37 | return new Promise(function(resolve, reject) { 38 | //Set params for Step Functions Exec 39 | const params = { 40 | stateMachineArn: arn, 41 | input: myString 42 | }; 43 | 44 | //Exec Step Function 45 | stepfunctions.startExecution(params, function(err, data) { 46 | if (err) { 47 | console.log(err); 48 | reject(err); 49 | } // an error occurred 50 | else { 51 | console.log(data); 52 | resolve(data); 53 | } 54 | }); 55 | }); 56 | } 57 | 58 | exports.handler = (event, context, callback) => { 59 | // Input into JSON to Pass to Step Functions 60 | const myObject = { 61 | 'removeVol': event.removeVol, 62 | 'image' : event.image, 63 | 'region' : event.region, 64 | 'os' : event.os, 65 | 'waitMove' : '60', 66 | 'waitVol' : '15', 67 | 'waitEc2' : '15', 68 | 'topic' : 'gov-cloud-import-image' 69 | }; 70 | 71 | // Convert to string 72 | const myString = JSON.stringify(myObject); 73 | findStateMchineArn() 74 | .then(function(arn){ 75 | //Get ARN 76 | let arnStateMachine = arn; 77 | return execStateMachine(myString, arnStateMachine); 78 | }) 79 | .then(function(data){ 80 | callback(null, data); 81 | }) 82 | .catch(function(err){ 83 | console.log(err); 84 | callback(err); 85 | }); 86 | 87 | 88 | }; 89 | -------------------------------------------------------------------------------- /lambda/listComBuckets/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | 11 | //Get Commercial Buckets 12 | function getList(paramsSSM){ 13 | return new Promise(function(resolve, reject) { 14 | let s3 = new AWS.S3; 15 | s3.listBuckets(function(err, data) { 16 | if (err) { 17 | console.log(err); 18 | reject(err); 19 | } else { 20 | let length = data.Buckets.length; 21 | let bucketList = []; 22 | for (let index = 0; index < length; ++index) { 23 | bucketList[index] = data.Buckets[index].Name; 24 | } 25 | console.log(bucketList) 26 | resolve(bucketList); 27 | } 28 | }); 29 | }); 30 | } 31 | 32 | exports.handler = (event, context, callback) => { 33 | getList() 34 | .then(function(bucketList){ 35 | callback(null, bucketList); 36 | }) 37 | .catch(function(err){ 38 | console.log(err); 39 | callback(err); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /lambda/listGovBuckets/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ssm = new AWS.SSM(); 11 | let govRegion; 12 | 13 | //Determine which GovCloud Region we should be using 14 | function getGovCloudRegion(){ 15 | return new Promise(function(resolve, reject) { 16 | let region = process.env.AWS_REGION; 17 | if (region == 'us-west-2') { 18 | govRegion = 'us-gov-west-1'; 19 | resolve(govRegion); 20 | } else if (region == 'us-east-2'){ 21 | govRegion = 'us-gov-east-1'; 22 | resolve(govRegion); 23 | } 24 | console.log(govRegion); 25 | }); 26 | } 27 | 28 | //Get Values from Parameter Store 29 | function getParameter(value){ 30 | return new Promise(function(resolve, reject) { 31 | let params = { 32 | Name: value, 33 | WithDecryption: true 34 | }; 35 | ssm.getParameter(params, function(err, data) { 36 | if (err) { 37 | console.log(err); 38 | reject(err); 39 | } else { 40 | resolve(data.Parameter.Value); 41 | } 42 | }); 43 | }); 44 | } 45 | 46 | //Get gov-cloud-import-user GovCloud S3 Sync target buckets 47 | function getList(paramsSSM){ 48 | return new Promise(function(resolve, reject) { 49 | let s3 = new AWS.S3({ 50 | region: paramsSSM.govRegion, 51 | accessKeyId: paramsSSM.accessKey, 52 | secretAccessKey: paramsSSM.secretKey 53 | }); 54 | s3.listBuckets(function(err, data) { 55 | if (err) { 56 | console.log(err); 57 | reject(err); 58 | } else { 59 | let length = data.Buckets.length; 60 | let bucketList = []; 61 | for (let index = 0; index < length; ++index) { 62 | bucketList[index] = data.Buckets[index].Name; 63 | } 64 | console.log(bucketList) 65 | resolve(bucketList); 66 | } 67 | }); 68 | }); 69 | } 70 | 71 | exports.handler = (event, context, callback) => { 72 | let paramsSSM = event; 73 | getParameter("gov-cloud-import-accessKey") 74 | .then(function(accessKey){ 75 | paramsSSM.accessKey = accessKey; 76 | return getParameter("gov-cloud-import-secretKey"); 77 | }) 78 | .then(function(secretKey){ 79 | paramsSSM.secretKey = secretKey; 80 | return getGovCloudRegion(); 81 | }) 82 | .then(function(govRegion){ 83 | paramsSSM.govRegion = govRegion; 84 | return getList(paramsSSM); 85 | }) 86 | .then(function(bucketList){ 87 | callback(null, bucketList); 88 | }) 89 | .catch(function(err){ 90 | console.log(err); 91 | callback(err); 92 | }); 93 | 94 | }; 95 | -------------------------------------------------------------------------------- /lambda/makeVolume/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ec2 = new AWS.EC2(); 11 | let az; 12 | 13 | //Find EC2 Worker Subnet 14 | function findSubnet(){ 15 | return new Promise(function(resolve, reject) { 16 | let params = { 17 | Filters: [ 18 | { 19 | Name: 'tag:gov-cloud-import', 20 | Values: [ 21 | 'true', 22 | ] 23 | }, 24 | ], 25 | }; 26 | ec2.describeSubnets(params, function(err, data) { 27 | if (err) { 28 | console.log(err); 29 | reject(err); 30 | } else { 31 | console.log(data); 32 | const az = data.Subnets[0].AvailabilityZone; 33 | resolve(az); 34 | } 35 | }); 36 | }); 37 | } 38 | 39 | //Describe AMI to find Snapshot or relay given snap id 40 | function getSnapshotId(event){ 41 | return new Promise(function(resolve, reject) { 42 | if (event.imageNew.startsWith("ami") == true){ 43 | let params = { 44 | DryRun: false, 45 | ImageIds: [ 46 | event.imageNew, 47 | ], 48 | }; 49 | ec2.describeImages(params, function(err, data) { 50 | if (err) { 51 | console.log(err); 52 | } else { 53 | const snapshot = data.Images[0].BlockDeviceMappings[0].Ebs.SnapshotId; 54 | console.log(data); 55 | resolve(snapshot); 56 | } 57 | }); 58 | } else if (event.imageNew.startsWith("snap") == true) { 59 | resolve(event.imageNew); 60 | } 61 | }); 62 | } 63 | 64 | //Make Volume to attach to EC2 Worker 65 | function makeVolume(az, snapshotId){ 66 | return new Promise(function(resolve, reject) { 67 | let params = { 68 | AvailabilityZone: az, 69 | SnapshotId: snapshotId, 70 | TagSpecifications: [ 71 | { 72 | ResourceType: 'volume', 73 | Tags: [ 74 | { 75 | Key: 'gov-cloud-import', 76 | Value: 'true' 77 | }, 78 | ] 79 | }, 80 | ], 81 | }; 82 | ec2.createVolume(params, function(err, data) { 83 | if (err) { 84 | console.log(err); 85 | } 86 | else { 87 | console.log(data); 88 | resolve(data.VolumeId); 89 | } 90 | }); 91 | 92 | }); 93 | } 94 | 95 | exports.handler = (event, context, callback) => { 96 | findSubnet() 97 | .then(function(data){ 98 | az = data; 99 | return getSnapshotId(event); 100 | }) 101 | .then(function(snapshotId){ 102 | return makeVolume(az, snapshotId); 103 | }) 104 | .then(function(volumeId){ 105 | callback(null, volumeId); 106 | }) 107 | .catch(function(err){ 108 | console.log(err); 109 | callback(err); 110 | }); 111 | }; 112 | 113 | -------------------------------------------------------------------------------- /lambda/makeVolumeStatus/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ec2 = new AWS.EC2(); 11 | let response = {}; 12 | 13 | exports.handler = (event, context, callback) => { 14 | let volumeId = event.volumeId; 15 | let params = {VolumeIds: [volumeId]}; 16 | ec2.describeVolumes(params, function(err, data) { 17 | if (err) { 18 | console.log(err); 19 | } else { 20 | //console.log(data); 21 | const state = data.Volumes[0].State; 22 | if (state == "available"){ 23 | response.status = state; 24 | callback(null, response); 25 | } else if (state == "creating") { 26 | //StepFunctions will wait and check again 27 | response.status = state; 28 | callback(null, response); 29 | } else { 30 | callback(null, "failed"); 31 | } 32 | 33 | } 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /lambda/moveImage/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ec2 = new AWS.EC2(); 11 | 12 | exports.handler = (event, context, callback) => { 13 | //Are we working with Snap or AMI? 14 | const snap = event.image.startsWith("snap"); 15 | const ami = event.image.startsWith("ami"); 16 | console.log(ami); 17 | console.log(snap); 18 | if (snap == true) { 19 | var params = { 20 | SourceRegion: event.region, 21 | SourceSnapshotId: event.image, 22 | Description: 'Temp GovCloud Import Source Snapshot', 23 | //DestinationRegion: 'us-east-2', 24 | }; 25 | ec2.copySnapshot(params, function(err, data) { 26 | if (err) { 27 | console.log(err); 28 | callback(err); 29 | } else { 30 | console.log(data); 31 | callback(null, data.SnapshotId); 32 | } 33 | }); 34 | } else if (ami == true) { 35 | //Copy AMI and callback New AMI ID 36 | let params = { 37 | Name: 'Temp GovCloud Import Source Image', 38 | SourceImageId: event.image, 39 | SourceRegion: event.region, 40 | }; 41 | ec2.copyImage(params, function(err, data) { 42 | if (err) { 43 | console.log(err); 44 | callback(null, "failed"); 45 | } else { 46 | console.log(data); 47 | callback(null, data.ImageId); 48 | } 49 | }); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /lambda/moveStatus/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | var AWS = require('aws-sdk'); 10 | var ec2 = new AWS.EC2(); 11 | 12 | //Describe Image State 13 | function describeImage(event){ 14 | return new Promise(function(resolve, reject) { 15 | let params = { 16 | ImageIds: [ 17 | event.imageNew, 18 | ], 19 | }; 20 | ec2.describeImages(params, function(err, data) { 21 | if (err) { 22 | console.log(err); 23 | } // an error occurred 24 | else { 25 | console.log(data); 26 | resolve(data.Images[0].State); 27 | } 28 | }); 29 | }); 30 | } 31 | 32 | //Describe Snapshot State 33 | function describeSnapshot(event){ 34 | return new Promise(function(resolve, reject) { 35 | let params = { 36 | SnapshotIds: [ event.imageNew ] 37 | }; 38 | ec2.describeSnapshots(params, function(err, data) { 39 | if (err) { 40 | console.log(err); 41 | } 42 | else { 43 | //Use the same value as AMI for completed 44 | if (data.Snapshots[0].State == 'completed') { 45 | const state = 'available'; 46 | console.log(data); 47 | resolve(state); 48 | } 49 | else { 50 | const state = data.Snapshots[0].State; 51 | console.log(data); 52 | resolve(state); 53 | } 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | exports.handler = (event, context, callback) => { 60 | if (event.imageNew == 'failed'){ 61 | callback(null, "failed") 62 | } 63 | //Describe and Relay back to Stepfunctions 64 | if (event.imageNew.startsWith("ami")){ 65 | describeImage(event) 66 | .then(function(state){ 67 | callback(null, state); 68 | }) 69 | .catch(function(err){ 70 | console.log(err); 71 | callback(err); 72 | }); 73 | } else if (event.imageNew.startsWith("snap")) { 74 | describeSnapshot(event) 75 | .then(function(state){ 76 | callback(null, state); 77 | }) 78 | .catch(function(err){ 79 | console.log(err); 80 | callback(err); 81 | }); 82 | } 83 | 84 | }; 85 | 86 | -------------------------------------------------------------------------------- /lambda/removeS3Image/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | const ssm = new AWS.SSM(); 11 | let govRegion; 12 | let region = process.env.AWS_REGION; 13 | 14 | //Determine which GovCloud Region we should be using 15 | function getGovCloudRegion(){ 16 | return new Promise(function(resolve, reject) { 17 | if (region == 'us-west-2') { 18 | govRegion = 'us-gov-west-1'; 19 | resolve(govRegion); 20 | } else if (region == 'us-east-2'){ 21 | govRegion = 'us-gov-east-1'; 22 | resolve(govRegion); 23 | } 24 | console.log(govRegion); 25 | }); 26 | } 27 | 28 | //Get Values from Parameter Store 29 | function getParameter(value){ 30 | return new Promise(function(resolve, reject) { 31 | let params = { 32 | Name: value, 33 | WithDecryption: true 34 | }; 35 | ssm.getParameter(params, function(err, data) { 36 | if (err) { 37 | console.log(err); 38 | reject(err); 39 | } else { 40 | resolve(data.Parameter.Value); 41 | } 42 | }); 43 | }); 44 | } 45 | 46 | //Send Import Image cmd to GovCloud 47 | function removeImage(paramsSSM){ 48 | return new Promise(function(resolve, reject) { 49 | let s3 = new AWS.S3({ 50 | region: paramsSSM.govRegion, 51 | accessKeyId: paramsSSM.accessKey, 52 | secretAccessKey: paramsSSM.secretKey 53 | }); 54 | let params = { 55 | Bucket: paramsSSM.s3BucketGov, 56 | Key: paramsSSM.volumeId 57 | }; 58 | s3.deleteObject(params, function(err, data) { 59 | if (err) { 60 | console.log(err); 61 | reject(err); 62 | } else { 63 | resolve(data); 64 | } 65 | }); 66 | }); 67 | } 68 | 69 | exports.handler = (event, context, callback) => { 70 | let paramsSSM = event; 71 | getParameter("gov-cloud-import-accessKey") 72 | .then(function(accessKey){ 73 | paramsSSM.accessKey = accessKey; 74 | return getParameter("gov-cloud-import-secretKey"); 75 | }) 76 | .then(function(secretKey){ 77 | paramsSSM.secretKey = secretKey; 78 | return getParameter("gov-cloud-import-s3Bucket"); 79 | }) 80 | .then(function(s3BucketGov){ 81 | paramsSSM.s3BucketGov = s3BucketGov; 82 | return getGovCloudRegion(); 83 | }) 84 | .then(function(govRegion){ 85 | paramsSSM.govRegion = govRegion; 86 | return removeImage(paramsSSM); 87 | }) 88 | .then(function(GovCloudremove){ 89 | let removeData = GovCloudremove; 90 | console.log(JSON.stringify(removeData)); 91 | callback(null, removeData); 92 | }) 93 | .catch(function(err){ 94 | console.log(err); 95 | callback(err); 96 | }); 97 | 98 | }; 99 | -------------------------------------------------------------------------------- /lambda/snsPublish/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | let sns = new AWS.SNS(); 11 | let sts = new AWS.STS(); 12 | let region = process.env.AWS_REGION; 13 | let govRegion; 14 | let msg; 15 | 16 | //Determine which GovCloud Region we should be using 17 | function getGovCloudRegion(){ 18 | return new Promise(function(resolve, reject) { 19 | if (region == 'us-west-2') { 20 | govRegion = 'us-gov-west-1'; 21 | resolve(govRegion); 22 | } else if (region == 'us-east-2'){ 23 | govRegion = 'us-gov-east-1'; 24 | resolve(govRegion); 25 | } 26 | }); 27 | } 28 | 29 | function constructTopicArn(topic) { 30 | return new Promise((resolve, reject) => { 31 | let callerParams = { 32 | }; 33 | //Use STS to find the account number 34 | sts.getCallerIdentity(callerParams, function(err, data) { 35 | if (err) { 36 | console.log(err); 37 | reject(err); 38 | } else { 39 | let arn = 'arn:aws:sns:'+region+':'+data.Account+':'+topic; 40 | resolve(arn); 41 | } 42 | }); 43 | }); 44 | } 45 | 46 | function publish(msg, topic, arn) { 47 | return new Promise((resolve, reject) => { 48 | let params = { 49 | Message: msg, 50 | Subject: topic, 51 | TopicArn: arn 52 | }; 53 | sns.publish(params, function(err, data) { 54 | if (err) { 55 | console.log(err); 56 | reject(err); 57 | } else { 58 | console.log(data); 59 | resolve(data); 60 | } 61 | }); 62 | }); 63 | } 64 | 65 | function constructMsg(event) { 66 | return new Promise((resolve, reject) => { 67 | if (event.topic == "gov-cloud-import-image"){ 68 | if (event.importImageStatus == "completed"){ 69 | let msg = JSON.stringify({"sourceRegion": event.region, "source": event.image, "destRegion": govRegion, "dest": event.govImageId}); 70 | resolve(msg); 71 | } else if (event.importImageStatus == "failed" || event.status == "failed" || event.volume.status == "failed" || event.s3Status == "failed" ){ 72 | let msg = JSON.stringify({"sourceRegion": event.region, "source": event.image, "destRegion": govRegion, "dest": "failed"}); 73 | resolve(msg); 74 | } 75 | } else if (event.topic == "gov-cloud-import-s3"){ 76 | if (event.s3Status == 'failed'){ 77 | let msg = JSON.stringify({"sourceRegion": event.region, "source": event.sourceBucket, "destRegion": govRegion, "dest": "failed"}); 78 | resolve(msg); 79 | } else if (event.s3Status == true ){ 80 | let msg = JSON.stringify({"sourceRegion": event.region, "source": event.sourceBucket, "destRegion": govRegion, "dest": event.destBucket}); 81 | resolve(msg); 82 | } 83 | } 84 | }); 85 | } 86 | exports.handler = (event, context, callback) => { 87 | console.log(event); 88 | getGovCloudRegion() 89 | .then(function(){ 90 | return constructTopicArn(event.topic); 91 | }) 92 | .then(function(arn){ 93 | event.arn = arn; 94 | //Create SNS Msg 95 | return constructMsg(event); 96 | }) 97 | .then(function(msg){ 98 | //Publish to SNS 99 | console.log(msg); 100 | return publish(msg, event.topic, event.arn); 101 | }) 102 | .then(function(){ 103 | callback(null, msg); 104 | }) 105 | .catch(function(err){ 106 | console.log(err); 107 | callback(null, "failed"); 108 | }); 109 | }; 110 | -------------------------------------------------------------------------------- /lambda/snsSubscribe/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | ###################################################################################################################### 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 4 | # # 5 | # SPDX-License-Identifier: Apache-2.0 # 6 | ###################################################################################################################### 7 | */ 8 | 9 | const AWS = require('aws-sdk'); 10 | let sns = new AWS.SNS(); 11 | let sts = new AWS.STS(); 12 | 13 | function constructTopicArn(region, topic) { 14 | return new Promise((resolve, reject) => { 15 | let callerParams = { 16 | }; 17 | //Use STS to find the account number 18 | sts.getCallerIdentity(callerParams, function(err, data) { 19 | if (err) { 20 | console.log(err); 21 | reject(err); 22 | } else { 23 | let arn = 'arn:aws:sns:'+region+':'+data.Account+':'+topic; 24 | console.log(arn); 25 | resolve(arn); 26 | } 27 | }); 28 | }); 29 | } 30 | function subscribe(protocol, topicArn, endpoint) { 31 | return new Promise((resolve, reject) => { 32 | var params = { 33 | Protocol: protocol, 34 | TopicArn: topicArn, 35 | Endpoint: endpoint 36 | }; 37 | sns.subscribe(params, function(err, data) { 38 | if (err) { 39 | console.log(err); 40 | reject(err); 41 | } else { 42 | console.log(data); 43 | resolve("Subscription Successful for "+endpoint); 44 | } 45 | }); 46 | }); 47 | } 48 | 49 | exports.handler = (event, context, callback) => { 50 | //Build SNS Topic Arn 51 | constructTopicArn(event.region, event.topic) 52 | .then(function(topicArn){ 53 | //Subscribe the endpoint 54 | return subscribe(event.protocol, topicArn, event.endpoint); 55 | }) 56 | .then(function(msg){ 57 | console.log(msg); 58 | callback(null, msg); 59 | }) 60 | .catch(function(err){ 61 | console.log(err); 62 | callback(null, "failed"); 63 | }); 64 | }; 65 | --------------------------------------------------------------------------------