├── .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 | 
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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------