├── .gitignore ├── .gitmodules ├── .metadata ├── .nojekyll ├── .taskcat.yml ├── CODEOWNERS ├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── docs ├── generated │ ├── parameters │ │ └── index.adoc │ ├── regions │ │ └── index.adoc │ └── services │ │ ├── index.adoc │ │ └── metadata.adoc ├── images │ ├── Architecture_Diagram_RDGateway.pptx │ ├── architecture_diagram.png │ ├── aws-quickstart-graphic.png │ ├── cfn_outputs.png │ ├── image1.png │ ├── image10.png │ ├── image11.png │ ├── image12.png │ ├── image13.png │ ├── image14.png │ ├── image15.png │ ├── image16.png │ ├── image17.png │ ├── image18.png │ ├── image19.png │ ├── image2.png │ ├── image20.png │ ├── image21.png │ ├── image22.png │ ├── image23.png │ ├── image3.png │ ├── image4.png │ ├── image5.png │ ├── image6.png │ ├── image7.png │ ├── image8.png │ ├── image9.png │ ├── rdgateway-architecture-diagram.png │ └── rdgateway-architecture-diagram.pptx └── partner_editable │ ├── _qs_deploy_links.adoc │ ├── _settings.adoc │ ├── additional_info.adoc │ ├── architecture.adoc │ ├── deploy_steps.adoc │ ├── deployment_options.adoc │ ├── faq_troubleshooting.adoc │ ├── licenses.adoc │ ├── overview_target_and_usage.adoc │ ├── planning_the_deployment.adoc │ ├── pre-reqs.adoc │ ├── product_description.adoc │ ├── regions.adoc │ ├── service_limits.adoc │ ├── specialized_knowledge.adoc │ └── test_deployment.adoc ├── manifests └── app-manager │ └── app-manager-manifest.yaml ├── scripts ├── Create-LocalUser.ps1 ├── Initialize-RDGW.ps1 └── Set-EIP.ps1 └── templates ├── rdgw-domain.template ├── rdgw-master.template └── rdgw-standalone.template /.gitignore: -------------------------------------------------------------------------------- 1 | taskcat_outputs/* 2 | .taskcat_overrides.yml 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/quickstart-aws-vpc"] 2 | path = submodules/quickstart-aws-vpc 3 | url = ../../aws-quickstart/quickstart-aws-vpc.git 4 | branch = main 5 | [submodule "submodules/quickstart-microsoft-utilities"] 6 | path = submodules/quickstart-microsoft-utilities 7 | url = ../../aws-quickstart/quickstart-microsoft-utilities.git 8 | branch = main 9 | [submodule "docs/boilerplate"] 10 | path = docs/boilerplate 11 | url = https://github.com/aws-quickstart/quickstart-documentation-base-common.git 12 | branch = main 13 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | language_type: cloudformation 2 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/.nojekyll -------------------------------------------------------------------------------- /.taskcat.yml: -------------------------------------------------------------------------------- 1 | project: 2 | name: quickstart-microsoft-rdgateway 3 | owner: quickstart-eng@amazon.com 4 | package_lambda: false 5 | regions: 6 | - ap-northeast-1 7 | - ap-northeast-2 8 | - ap-south-1 9 | - ap-southeast-1 10 | - ap-southeast-2 11 | - ca-central-1 12 | - eu-central-1 13 | - eu-west-1 14 | - eu-west-2 15 | - us-east-1 16 | - us-east-2 17 | - us-west-1 18 | - us-west-2 19 | s3_bucket: '' 20 | tests: 21 | rdgwMaster: 22 | parameters: 23 | AvailabilityZones: $[taskcat_getaz_2] 24 | RDGWInstanceType: t2.large 25 | VPCCIDR: 10.0.0.0/16 26 | PrivateSubnet1CIDR: 10.0.0.0/19 27 | PrivateSubnet2CIDR: 10.0.32.0/19 28 | PublicSubnet1CIDR: 10.0.128.0/20 29 | PublicSubnet2CIDR: 10.0.144.0/20 30 | RDGWCIDR: 10.0.0.0/16 31 | KeyPairName: $[taskcat_getkeypair] 32 | AdminPassword: $[taskcat_genpass_8] 33 | QSS3BucketName: $[taskcat_autobucket] 34 | QSS3BucketRegion: $[taskcat_current_region] 35 | regions: 36 | - ap-northeast-1 37 | - ap-northeast-2 38 | - ap-south-1 39 | - ap-southeast-1 40 | - ap-southeast-2 41 | - ca-central-1 42 | - eu-central-1 43 | - eu-west-1 44 | - eu-west-2 45 | - us-east-1 46 | - us-east-2 47 | - us-west-1 48 | - us-west-2 49 | template: templates/rdgw-master.template 50 | 51 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @davmayd @vsnyc @aws-quickstart/aws_quickstart_team 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 4 | 5 | http://aws.amazon.com/apache2.0/ 6 | 7 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickstart-microsoft-rdgateway 2 | ## Deprecation Notice 3 | 4 | :x: This repository is subject to deprecation in Q4 2024. For more details, [please review this announcement](https://github.com/aws-ia/.announcements/issues/1). 5 | 6 | ## This repository has been deprecated in favor of https://github.com/aws-ia/cfn-ps-microsoft-rdgateway. 7 | ***We will archive this repository and keep it publicly available until May 1, 2024.*** 8 | -------------------------------------------------------------------------------- /docs/generated/parameters/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/regions/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/services/index.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/generated/services/metadata.adoc: -------------------------------------------------------------------------------- 1 | // placeholder 2 | -------------------------------------------------------------------------------- /docs/images/Architecture_Diagram_RDGateway.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/Architecture_Diagram_RDGateway.pptx -------------------------------------------------------------------------------- /docs/images/architecture_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/architecture_diagram.png -------------------------------------------------------------------------------- /docs/images/aws-quickstart-graphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/aws-quickstart-graphic.png -------------------------------------------------------------------------------- /docs/images/cfn_outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/cfn_outputs.png -------------------------------------------------------------------------------- /docs/images/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image1.png -------------------------------------------------------------------------------- /docs/images/image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image10.png -------------------------------------------------------------------------------- /docs/images/image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image11.png -------------------------------------------------------------------------------- /docs/images/image12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image12.png -------------------------------------------------------------------------------- /docs/images/image13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image13.png -------------------------------------------------------------------------------- /docs/images/image14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image14.png -------------------------------------------------------------------------------- /docs/images/image15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image15.png -------------------------------------------------------------------------------- /docs/images/image16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image16.png -------------------------------------------------------------------------------- /docs/images/image17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image17.png -------------------------------------------------------------------------------- /docs/images/image18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image18.png -------------------------------------------------------------------------------- /docs/images/image19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image19.png -------------------------------------------------------------------------------- /docs/images/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image2.png -------------------------------------------------------------------------------- /docs/images/image20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image20.png -------------------------------------------------------------------------------- /docs/images/image21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image21.png -------------------------------------------------------------------------------- /docs/images/image22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image22.png -------------------------------------------------------------------------------- /docs/images/image23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image23.png -------------------------------------------------------------------------------- /docs/images/image3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image3.png -------------------------------------------------------------------------------- /docs/images/image4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image4.png -------------------------------------------------------------------------------- /docs/images/image5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image5.png -------------------------------------------------------------------------------- /docs/images/image6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image6.png -------------------------------------------------------------------------------- /docs/images/image7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image7.png -------------------------------------------------------------------------------- /docs/images/image8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image8.png -------------------------------------------------------------------------------- /docs/images/image9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/image9.png -------------------------------------------------------------------------------- /docs/images/rdgateway-architecture-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/rdgateway-architecture-diagram.png -------------------------------------------------------------------------------- /docs/images/rdgateway-architecture-diagram.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/images/rdgateway-architecture-diagram.pptx -------------------------------------------------------------------------------- /docs/partner_editable/_qs_deploy_links.adoc: -------------------------------------------------------------------------------- 1 | [cols=",] 2 | |=== 3 | |http://qs_launch_link[Deploy Microsoft RD Gateway into a new VPC on AWS^] 4 | |http://qs_launch_link[Deploy Microsoft RD Gateway into an existing VPC on AWS^] 5 | |=== -------------------------------------------------------------------------------- /docs/partner_editable/_settings.adoc: -------------------------------------------------------------------------------- 1 | :quickstart-project-name: quickstart-microsoft-rdgateway 2 | :partner-product-name: Remote Desktop Gateway 3 | :partner-product-short-name: RD Gateway 4 | :partner-company-name: Microsoft 5 | :doc-month: January 6 | :doc-year: 2021 7 | //:partner-contributors: 8 | :quickstart-contributors: Dave May and Santiago Cardenas, Solutions Architects, AWS Quick Start team 9 | :deployment_time: 20 minutes 10 | :default_deployment_region: us-east-1 11 | :parameters_as_appendix: 12 | // Uncomment these two attributes if you are leveraging 13 | // - an AWS Marketplace listing. 14 | // Additional content will be auto-generated based on these attributes. 15 | // :marketplace_subscription: 16 | // :marketplace_listing_url: https://example.com/ 17 | -------------------------------------------------------------------------------- /docs/partner_editable/additional_info.adoc: -------------------------------------------------------------------------------- 1 | == Post-deployment steps 2 | 3 | After you launch the AWS CloudFormation template for one of the non-domain scenarios in the previous sections and build the stack, follow these steps to complete the configuration of your AWS environment: 4 | 5 | 1. Create security groups for your Windows-based instances that will be located in private VPC subnets. Create an ingress rule permitting TCP port 3389 from the RD Gateway security group, CIDR range, or IP address. Associate these groups with instances as they are launched into the private subnets. 6 | 2. Make sure that your administrative clients can resolve the name for the RD Gateway endpoint (e.g., win-1a2b3c4d5e6.example.com). You can create an A (Host) record in DNS that maps the FQDN to the RD gateway’s Elastic IP or public IP address. For testing purposes, you can configure this mapping in the local host’s file on the machine. 7 | 3. Configure administrative clients with the proper configuration settings. This includes installing the root certificate from each RD Gateway server on the client machines (see the next section for instructions). When you use the AWS CloudFormation templates, the default location for the root certificate will be c:\.cer on each RD Gateway server. 8 | 4. Modify the RD Gateway security group. Remove the ingress rule permitting TCP port 3389. Create a new ingress rule permitting TCP port 443 from your administrator’s IP address. 9 | 5. Make sure that instances in private subnets are associated with a security group containing ingress rules permitting the RD Gateway server IP address to connect via TCP port 3389. 10 | 6. Configure the Remote Desktop connection for administrative clients, as described later in this section 11 | 12 | === Steps for non-domain-joined deployment 13 | 14 | The Quick Start implements a self-signed certificate on the RD gateway intances. After deployment, you must install the root certificate on your administrative clients before you configure the RDP client to connect to your RD gateway instances. The root certificate will automatically be stored as c:\.cer. 15 | 16 | To distribute this file to administrator workstations and install it, follow these steps: 17 | 18 | 1. Open a Command Prompt window using administrative credentials. 19 | 2. Type *mmc* and press *Enter*. 20 | 3. In the Console Root window, on the *File* menu, choose *Add/Remove Snap In*. 21 | 4. In the *Add Standalone Snap-in* dialog box, choose *Certificates*, and then choose *Add*. 22 | 5. In the *Certificates snap-in* dialog box, choose *Computer account*, and then choose *Next*. 23 | 6. In the *Select Computer* dialog box, choose *Finish*. 24 | 7. In the *Add Standalone Snap-in* dialog box, choose *Close*. 25 | 8. On the *Add/Remove Snap-in* dialog box, choose *OK*. 26 | 9. In the Console Root window, expand *Certificates (Local Computer)*. 27 | 10. Under *Certificates (Local Computer)*, expand *Trusted Root Certification Authorities*. 28 | 11. Open the context (right-click) window for *Certificates*, and choose *All Tasks > Import*. 29 | 12. Navigate to the root certificate (e.g., RDGW1.cer) to complete the installation. 30 | 31 | NOTE: The root certificate will be stored as c:\.cer on each RD gateway when deploying servers using the CloudFormation templates. 32 | 33 | === Steps for domain-joined deployment 34 | 35 | To utilize the Remote Desktop Gateway functionality in an Active Directory environment, a trusted certificate through your private Certificate Authority or a public DNS registrar is required. 36 | 37 | . For secure connectivity through the deployed NLB, acquire a trusted certificate through your private Certificate Authority or a public DNS registrar. 38 | . Create a DNS CNAME record in your DNS zone with your preferred DNS endpoint name for RDGW connections and the NLB DNS endpoint as the value. Alternately, for testing, create an entry in you local HOSTS file pointing the DNS endpoint name to the NLB IP address. 39 | . Connect to each RD Gateway instance, copy the trusted certificate to a local path, and import into the computer's personal certificate folder as follows. 40 | .. Type *mmc* and press *Enter*. 41 | .. In the Console Root window, on the *File* menu, choose *Add/Remove Snap In*. 42 | .. In the *Add Standalone Snap-in* dialog box, choose *Certificates*, and then choose *Add*. 43 | .. In the *Certificates snap-in* dialog box, choose *Computer account*, and then choose *Next*. 44 | .. In the *Select Computer* dialog box, choose *Finish*. 45 | .. In the *Add Standalone Snap-in* dialog box, choose *Close*. 46 | .. On the *Add/Remove Snap-in* dialog box, choose *OK*. 47 | .. In the Console Root window, expand *Certificates (Local Computer)*. 48 | .. Under *Certificates (Local Computer)*, expand *Personal*. 49 | .. Open the context (right-click) window for *Certificates*, and choose *All Tasks > Import*. 50 | .. Navigate to the trusted certificate (e.g., test.example.com.cer) to complete the installation. 51 | . On each RD Gateway instance, open the Remote Desktop Gateway Manager console, and then specify your trusted certificate for RD Gateway use. 52 | 53 | === Configure the Remote Desktop Connection client 54 | 55 | 1. Start the Remote Desktop Connection client. 56 | 2. In the computer name field, type the name or IP address of the Windows instance you want to connect to. Keep in mind that this instance needs to be reachable only from the RD gateway, not from the client machine. 57 | 58 | [#postdeploy1] 59 | .The Remote Desktop Connection client 60 | image::../images/image21.png[Post-deployment 1, 50%] 61 | 62 | [start=3] 63 | 3. Choose *Show Options*. On the *Advanced* tab, choose *Settings*. 64 | 4. Choose *Use these RD Gateway server settings*. For server name, specify the FQDN of the RD gateway. If the RD gateway and the server you want to connect to are in the same domain, choose *Use my RD Gateway credentials for the remote computer*, and then choose *OK*. 65 | 66 | [#postdeploy2] 67 | .Advanced properties for the Remote Desktop Connection client 68 | image::../images/image22.png[Post-deployment 2, 50%] 69 | 70 | NOTE: The FQDN server name of the RD Gateway host must match the certificate and the DNS record (or local HOSTS file entry). Otherwise, the secure connection will generate warnings and might fail. 71 | 72 | [start=5] 73 | 5. Enter your credentials, and then choose *OK* to connect to the server. You can supply the same set of credentials for the RD gateway and the destination server, as shown. If your servers are not domain-joined, you will need to authenticate twice: once for the RD gateway and once for the destination server. If your servers aren’t domain-joined, when prompted for the RD Gateway server credentials, provide the *Admin User Name* and *Admin Password* credentials you set in when you launched the Quick Start. Check the *Remember my credentials* box. (Otherwise, if you’re connecting from a Windows computer, you’ll get prompted for your credentials repeatedly, and will be blocked from entering your remote computer credentials.) 74 | 75 | [#postdeploy3] 76 | .Providing credentials for the RD gateway and destination server 77 | image::../images/image23.png[Post-deployment 3, 50%] 78 | 79 | === Run Windows Updates 80 | 81 | In order to ensure the deployed servers' operating systems and installed applications have the latest Microsoft updates, run Windows Update on each server. 82 | 83 | 1. Create an RDP session to the Remote Desktop Gateway server(s). 84 | 2. Open the *Settings* application. 85 | 3. Open *Update & Security*. 86 | 4. Click *Check for updates*. 87 | 5. Install any updates and reboot if necessary. 88 | 89 | == Best practices for using {partner-product-short-name} on AWS 90 | // Provide post-deployment best practices for using the technology on AWS, including considerations such as migrating data, backups, ensuring high performance, high availability, etc. Link to software documentation for detailed information. 91 | 92 | === The Principle of Least Privilege 93 | 94 | When considering remote administrative access to your environment, it is important to follow the principle of _least privilege_. This principle refers to users having the fewest possible permissions necessary to perform their job functions. This helps reduce the attack surface of your environment, making it much harder for an adversary to exploit. An attack surface can be defined as the set of exploitable vulnerabilities in your environment, including the network, software, and users who are involved in the ongoing operation of the system. 95 | 96 | Following the principle of least privilege, we recommend reducing the attack surface of your environment by exposing the absolute minimal set of ports to the network while also restricting the source network or IP address that will have access to your EC2 instances. 97 | 98 | In addition to the functionality that exists in the Microsoft platform, there are several AWS capabilities to help you implement the principle of least privilege, such as subnets, security groups, and trusted ingress CIDR blocks. 99 | 100 | === VPC configuration 101 | 102 | Amazon VPC lets you provision a private, isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define. With Amazon VPC, you can define a virtual network topology closely resembling a traditional network that you might operate on your own premises. You have complete control over your virtual networking environment, including selection of your own IP address range, creation of subnets, and configuration of route tables and network gateways. 103 | 104 | When deploying a Windows-based architecture on the AWS Cloud, we recommend an VPC configuration that supports the following requirements: 105 | 106 | * Critical workloads should be placed in a minimum of two Availability Zones to provide high availability. 107 | * Instances should be placed into individual tiers. For example, in a Microsoft SharePoint deployment, you should have separate tiers for web servers, application servers, database servers, and domain controllers. Traffic between these groups can be controlled to adhere to the principle of least privilege. 108 | * Internal application servers and other non-internet facing servers should be placed in private subnets to prevent direct access to these instances from the internet. 109 | * RD gateways should be deployed into public subnets in each Availability Zone for remote administration. Other components, such as reverse proxy servers, can also be placed into these public subnets if needed. 110 | 111 | This Quick Start supports these best practices, as illustrated earlier in this guide. For details on the VPC design used in this Quick Start, see the https://fwd.aws/9VdxN[Quick Start for building a modular and scalable virtual network architecture with Amazon VPC]. 112 | 113 | === Network Access Control Lists 114 | 115 | A network access control list (ACL) is a set of permissions that can be attached to any network subnet in a VPC to provide stateless filtering of traffic. Network ACLs can be used for inbound or outbound traffic and provide an effective way to blacklist a CIDR block or individual IP addresses. These ACLs can contain ordered rules to allow or deny traffic based on IP protocol, service port, or source or destination IP address. Figure 3 shows the default ACL configuration for a VPC subnet. This configuration is used for the subnets in the Quick Start architecture. 116 | 117 | [#additional1] 118 | .Default network ACL configuration for a VPC subnet 119 | image::../images/image3.png[image,width=543,height=255] 120 | 121 | You may choose to keep the default network ACL configuration, or you may choose to lock it down with more specific rules to restrict traffic between subnets at the network level. For example, you could set a rule that would allow inbound administrative traffic on TCP port 3389 from a specific set of IP addresses. In either case, you’ll also need to implement security group rules to permit access from users connecting to RD gateways and between tiered groups of EC2 instances. 122 | 123 | === Security groups 124 | 125 | All EC2 instances are required to belong to one or more security groups. Security groups allow you to set policies to control open ports and provide isolation between application tiers. In a VPC, every instance runs behind a stateful firewall with all ports closed by default. The security group contains rules responsible for opening inbound and outbound ports on that firewall. While security groups act as an instance-level firewall, they can also be associated with multiple instances, providing isolation between application tiers in your environment. For example, you can create a security group for all your web servers that will allow traffic on TCP port 3389, but only from members of the security group containing your RD Gateway servers. This is illustrated in Figure 4. 126 | 127 | [#additional2] 128 | .Security groups for RD Gateway administrative access 129 | image::../images/image2.png[image,width=648,height=336] 130 | 131 | Notice that inbound connections from the internet are only permitted over TCP port 443 to the RD gateways. The RD gateways have an Elastic IP address assigned and have direct access to the internet. The remaining Windows instances are deployed into private subnets and are assigned private IP addresses only. Security group rules allow only the RD gateways to initiate inbound connections for remote administration to TCP port 3389 for instances in the private subnets. 132 | 133 | In this architecture, RDP connections are established over HTTPS to the RD gateway and proxied to backend instances on the standard RDP TCP port 3389. This configuration helps you reduce the attack surface on your Windows-based instances while allowing administrators to establish connections to all your instances through a single gateway. 134 | 135 | It’s possible to provide remote administrative access to all your Windows-based instances through one RD gateway, but we recommend placing gateways in each Availability Zone for redundancy. The Quick Start implements this best practice, as illustrated in Figure 5. 136 | 137 | === Initial Remote Administration Architecture 138 | 139 | In an initial RD gateway configuration, the servers in the public subnet will need an inbound security group rule permitting TCP port 3389 from the administrator’s source IP address or subnet. Windows instances sitting behind the RD Gateway in a private subnet will be in their own isolated tier. For example, a group of web server instances in a private subnet may be associated with their own web tier security group. This security group will need an inbound rule allowing connections from the RD Gateway on TCP port 3389. 140 | 141 | Using this architecture, an administrator can use a traditional RDP connection to an RD gateway to configure the local server. The RD gateway can also be used as a jump box; that is, when an RDP connection is established to the desktop of the RD gateway, an administrator can start a new RDP client session to initiate a connection to an instance in a private subnet, as illustrated in Figure 1. 142 | 143 | [#additional4] 144 | .Initial architecture for remote administration 145 | image::../images/image1.png[image,width=739,height=384] 146 | 147 | Although this architecture works well for initial administration, it is not recommended for the long term. To further secure connections and reduce the number of RDP sessions required to administer the servers in the private subnets, the inbound rule should be changed to permit TCP port 443, and the RD gateway service should be installed and configured with an SSL certificate, and connection and authorization policies. 148 | 149 | The Quick Start sets up a standard TCP port 3389 connection from the administrator’s IP address. You’ll need to follow the https://aws-quickstart.github.io/quickstart-microsoft-rdgateway/#_post_deployment_steps[post-deployment steps] to modify the security group for RD Gateway to use a single inbound rule permitting TCP port 443, as illustrated in Figure 2. This modification will allow a Transport Layer Security (TLS) encrypted RDP connection to be proxied through the gateway over TCP port 443 directly to one or more Windows-based instances in private subnets on TCP port 3389. This configuration increases the security of the connection and also prevents the need to initiate an RDP session to the desktop of the RD gateway. 150 | 151 | [#additional5] 152 | .Architecture for RD Gateway administrative access 153 | image::../images/image2.png[image,width=648,height=336] 154 | 155 | [[ssl-certificates]] 156 | === SSL Certificates 157 | 158 | The RD Gateway role uses Transport Layer Security (TLS) to encrypt communications over the internet between administrators and gateway servers. To support TLS, a valid X.509 SSL certificate must be installed on each RD gateway. Certificates can be acquired in a number of ways, including: 159 | 160 | * Your own PKI infrastructure, such as a Microsoft Enterprise Certificate Authority (CA) 161 | * Certificates issued by a public CA, such as Verisign or Digicert 162 | * Self-signed certificates 163 | 164 | For smaller test environments, implementing a self-signed certificate is a straightforward process that helps you get up and running quickly. This Quick Start automatically generates a self-signed certificate for RD Gateway. 165 | 166 | However, if you have a large number of varying administrative devices that need to establish a connection to your gateways, we recommend using a public certificate. 167 | 168 | In order for an RDP client to establish a secure connection with an RD gateway, the following certificate and DNS requirements must be met: 169 | 170 | * The issuing CA of the certificate installed on the gateway must be trusted by the RDP client. For example, the root CA certificate must be installed in the client machine’s _Trusted Root Certification Authorities_ store. 171 | * The subject name used on the certificate installed on the gateway must match the DNS name used by the client to connect to the server; for example, rdgw1.example.com. 172 | * The client must be able to resolve the host name (for example, rdgw1.example.com) to the Elastic IP address of the RD Gateway. This will require a Host (A) record in DNS. 173 | 174 | There are various considerations when choosing the right CA to obtain an SSL certificate. For example, a public certificate may be ideal since the issuing CA will be widely trusted by the majority of client devices that need to connect to your gateways. On the other hand, you may choose to utilize your own PKI infrastructure to ensure that only the machines that are part of your organization will trust the issuing CA. 175 | 176 | [[connection-and-resource-authorization-policies]] 177 | === Connection and Resource Authorization Policies 178 | 179 | Users must meet specific requirements in order to connect to RD Gateway instances: 180 | 181 | * *Connection authorization policies* – Remote Desktop connection authorization policies (RD CAPs) allow you to specify who can connect to an RD Gateway instance. For example, you can select a group of users from your domain, such as _Domain Admins_. 182 | * *Resource authorization policies* – Remote Desktop resource authorization policies (RD RAPs) allow you to specify the internal Windows-based instances that remote users can connect to through an RD Gateway instance. For example, you can choose specific domain-joined computers, which administrators can connect to through the RD Gateway. 183 | 184 | This Quick Start automatically sets up connection and resource authorization policies. 185 | -------------------------------------------------------------------------------- /docs/partner_editable/architecture.adoc: -------------------------------------------------------------------------------- 1 | Deploying this Quick Start for a new virtual private cloud (VPC) with 2 | *default parameters* builds the following _{partner-product-name}_ environment in the 3 | AWS Cloud. 4 | 5 | Deploying this Quick Start for a new VPC with default parameters builds the following {partner-product-short-name} environment in the AWS Cloud. 6 | 7 | [#architecture1] 8 | .Quick Start architecture for RD Gateway on AWS 9 | image::../images/rdgateway-architecture-diagram.png[image,width=948,height=629] 10 | 11 | The Quick Start sets up the following: 12 | 13 | * A highly available architecture that spans two Availability Zones.* 14 | * A VPC configured with public and private subnets according to AWS best practices, to provide you with your own virtual network on AWS.* 15 | * An internet gateway to allow access to the internet. This gateway is used by the {partner-product-short-name} instances to send and receive traffic.* 16 | * Managed network address translation (NAT) gateways to allow outbound internet access for resources in the private subnets.* 17 | * In each public subnet, up to four {partner-product-short-name} instances in an Auto Scaling group to provide secure remote access to instances in the private subnets. Each instance is assigned an Elastic IP address so it’s reachable directly from the internet. 18 | * A Network Load Balancer to provide RDP access to the {partner-product-short-name} instances. 19 | * A security group for Windows-based instances that will host the {partner-product-short-name} role, with an ingress rule permitting TCP port 3389 from your administrator IP address. After deployment, you’ll modify the security group ingress rules to configure administrative access through TCP port 443 instead. 20 | * An empty application tier for instances in private subnets. If more tiers are required, you can create additional private subnets with unique CIDR ranges. 21 | * AWS Secrets Manager to securely store credentials used for accessing the {partner-product-short-name} instances. 22 | * AWS Systems Manager to automate the deployment of the {partner-product-short-name} Auto Scaling group. 23 | 24 | *The template that deploys the Quick Start into an existing VPC skips the tasks marked by asterisks and prompts you for your existing VPC configuration. 25 | 26 | The Quick Start also installs a self-signed SSL certificate and configures RD CAP and RD RAP policies. 27 | 28 | -------------------------------------------------------------------------------- /docs/partner_editable/deploy_steps.adoc: -------------------------------------------------------------------------------- 1 | // We need to work around Step numbers here if we are going to potentially exclude the AMI subscription 2 | === Sign in to your AWS account 3 | 4 | . Sign in to your AWS account at https://aws.amazon.com with an IAM user role that has the necessary permissions. For details, see link:#_planning_the_deployment[Planning the deployment] earlier in this guide. 5 | . Ensure that your AWS account is configured correctly, as discussed in the link:#_technical_requirements[Technical requirements] section. 6 | 7 | // Optional based on Marketplace listing. Not to be edited 8 | ifdef::marketplace_subscription[] 9 | === Subscribe to the {partner-product-short-name} AMI 10 | 11 | This Quick Start requires a subscription to the AMI for {partner-product-short-name} in AWS Marketplace. 12 | 13 | . Sign in to your AWS account. 14 | . {marketplace_listing_url}[Open the page for the {partner-product-short-name} AMI in AWS Marketplace], and then choose *Continue to Subscribe*. 15 | . Review the terms and conditions for software usage, and then choose *Accept Terms*. + 16 | A confirmation page loads, and an email confirmation is sent to the account owner. For detailed subscription instructions, see the https://aws.amazon.com/marketplace/help/200799470[AWS Marketplace documentation^]. 17 | 18 | . When the subscription process is complete, exit out of AWS Marketplace without further action. *Do not* provision the software from AWS Marketplace—the Quick Start deploys the AMI for you. 19 | endif::marketplace_subscription[] 20 | // \Not to be edited 21 | 22 | === Launch the Quick Start 23 | 24 | NOTE: You are responsible for the cost of the AWS services used while running this Quick Start reference deployment. There is no additional cost for using this Quick Start. For full details, see the pricing pages for each AWS service used by this Quick Start. Prices are subject to change. 25 | 26 | . Sign in to your AWS account, and choose one of the following options to launch the AWS CloudFormation template. For help with choosing an option, see link:#_deployment_options[Deployment options] earlier in this guide. 27 | 28 | [cols=2*] 29 | |=== 30 | ^|https://fwd.aws/66VB5[Deploy Remote Desktop Gateway into a new VPC on AWS^] 31 | ^|https://github.com/aws-quickstart/quickstart-microsoft-rdgateway/blob/main/templates/rdgw-master.template[View template^] 32 | 33 | ^|https://fwd.aws/QmmJN[Deploy a standalone Remote Desktop Gateway into an existing VPC on AWS^] 34 | ^|https://github.com/aws-quickstart/quickstart-microsoft-rdgateway/blob/main/templates/rdgw-standalone.template[View template^] 35 | 36 | ^|https://fwd.aws/EwEqm[Deploy a domain-joined Remote Desktop Gateway into an existing VPC on AWS^] 37 | ^|https://github.com/aws-quickstart/quickstart-microsoft-rdgateway/blob/main/templates/rdgw-domain.template[View template^] 38 | |=== 39 | 40 | WARNING: If you deploy {partner-product-short-name} into an existing VPC, ensure that your VPC has two private subnets in different Availability Zones for the workload instances, and that the subnets aren’t shared. This Quick Start doesn’t support https://docs.aws.amazon.com/vpc/latest/userguide/vpc-sharing.html[shared subnets^]. These subnets require https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html[NAT gateways^] in their route tables, to allow the instances to download packages and software without exposing them to the internet. 41 | 42 | Also, ensure that the domain name option in the DHCP options is configured as explained in http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html[DHCP options sets^]. You provide your VPC settings when you launch the Quick Start. 43 | 44 | Each deployment takes about {deployment_time} to complete. 45 | 46 | [start=2] 47 | . Check the AWS Region that’s displayed in the upper-right corner of the navigation bar, and change it if necessary. This Region is where the network infrastructure for {partner-product-short-name} is built. The template is launched in the {default_deployment_region} Region by default. 48 | 49 | // *Note:* This deployment includes Amazon EFS, which isn’t currently supported in all AWS Regions. For a current list of supported Regions, see the https://docs.aws.amazon.com/general/latest/gr/elasticfilesystem.html[endpoints and quotas webpage]. 50 | 51 | [start=3] 52 | . On the *Create stack* page, keep the default setting for the template URL, and then choose *Next*. 53 | . On the *Specify stack details* page, change the stack name if needed. Review the parameters for the template. Provide values for the parameters that require input. For all other parameters, review the default settings and customize them as necessary. 54 | 55 | // In the following tables, parameters are listed by category and described separately for the two deployment options: 56 | 57 | // * Parameters for deploying {partner-product-short-name} into a new VPC 58 | // * Parameters for deploying {partner-product-short-name} into an existing VPC -------------------------------------------------------------------------------- /docs/partner_editable/deployment_options.adoc: -------------------------------------------------------------------------------- 1 | This Quick Start provides three deployment options: 2 | 3 | 4 | * *Deploy {partner-product-short-name} into a new VPC (end-to-end deployment)* This option builds a new AWS environment consisting of the VPC, subnets, NAT gateways, security groups, and other infrastructure components, and then deploys {partner-product-short-name} into this new VPC. 5 | * *Deploy {partner-product-short-name} into an existing VPC*. This option provisions standalone {partner-product-short-name} instances in your existing AWS infrastructure. 6 | * *Deploy domain-joined RD Gateway into an existing VPC.* This is similar to the second option, except that it provides domain-joined {partner-product-short-name} instances in the existing VPC, and provides a few additional parameters for customizing this configuration. 7 | 8 | The Quick Start provides separate templates for these three options. You can also configure CIDR blocks, instance types, and {partner-product-short-name} settings, as discussed later in the deployment steps. -------------------------------------------------------------------------------- /docs/partner_editable/faq_troubleshooting.adoc: -------------------------------------------------------------------------------- 1 | // Add any tips or answers to anticipated questions. This could include the following troubleshooting information. If you don’t have any other Q&A to add, change “FAQ” to “Troubleshooting.” 2 | 3 | == FAQ 4 | 5 | *Q.* I encountered a *CREATE_FAILED* error when I launched the Quick Start. 6 | 7 | *A.* If AWS CloudFormation fails to create the stack, we recommend that you relaunch the template with *Rollback on failure* set to *No*. (This setting is under *Advanced* in the AWS CloudFormation console, *Options* page.) With this setting, the stack’s state is retained and the instance is left running, so you can troubleshoot the issue. (For Windows, look at the log files in %ProgramFiles%\Amazon\EC2ConfigService and C:\cfn\log.) 8 | // If you’re deploying on Linux instances, provide the location for log files on Linux, or omit this sentence. 9 | 10 | *Important:* When you set *Rollback on failure* to *No*, you continue to incur AWS charges for this stack. Be sure to delete the stack when you finish troubleshooting. 11 | 12 | For additional information, see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/troubleshooting.html[Troubleshooting AWS CloudFormation] on the AWS website. 13 | 14 | *Q.* I encountered a size limitation error when I deployed the AWS CloudFormation templates. 15 | 16 | *A.* Launch the Quick Start templates from the links in this guide or from another S3 bucket. If you deploy the templates from a local copy on your computer or from a location other than an S3 bucket, you might encounter template size limitations. For more information about AWS CloudFormation quotas, see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html[AWS CloudFormation quotas] in the AWS documentation. 17 | 18 | 19 | //== Troubleshooting 20 | 21 | // -------------------------------------------------------------------------------- /docs/partner_editable/licenses.adoc: -------------------------------------------------------------------------------- 1 | This product deploys one or more Amazon Elastic Compute Cloud (Amazon EC2) instances running Microsoft Windows Server. The Windows Server licenses are provided by Amazon. -------------------------------------------------------------------------------- /docs/partner_editable/overview_target_and_usage.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // Identify your target audience and explain how/why they would use this Quick Start. 3 | //Avoid borrowing text from third-party websites (copying text from AWS service documentation is fine). Also, avoid marketing-speak, focusing instead on the technical aspect. 4 | 5 | This Quick Start is for users who require one or more Microsoft Remote Desktop servers to provide remote access to their environments. -------------------------------------------------------------------------------- /docs/partner_editable/planning_the_deployment.adoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-quickstart/quickstart-microsoft-rdgateway/f1c0fdfbe8250210d4dd293faeda4423bd32596f/docs/partner_editable/planning_the_deployment.adoc -------------------------------------------------------------------------------- /docs/partner_editable/pre-reqs.adoc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/partner_editable/product_description.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // Briefly describe the software. Use consistent and clear branding. 3 | // Include the benefits of using the software on AWS, and provide details on usage scenarios. 4 | 5 | AWS provides a comprehensive set of services and tools for deploying Microsoft Windows-based workloads on its highly reliable and secure cloud infrastructure. {partner-product-name} uses Remote Desktop Protocol (RDP) over HTTPS to establish a secure, encrypted connection between remote users on the internet and Windows-based EC2 instances, without needing to configure a virtual private network (VPN) connection. This helps reduce the attack surface on your Windows-based instances while providing a remote administration solution for administrators. 6 | 7 | This Quick Start automatically deploys and configures an {partner-product-short-name} infrastructure in the AWS Cloud from scratch, so you can securely administer your Windows-based, Amazon EC2 fleet using RDP over HTTPS. You can use the AWS CloudFormation templates included with the Quick Start to deploy a fully configured {partner-product-short-name} infrastructure in a new or existing VPC in your AWS account. You can also use the AWS CloudFormation templates as a starting point for your own implementation. 8 | 9 | We’ve also published a set of https://aws.amazon.com/quickstart/[Quick Starts] that provide solutions for deploying common Microsoft workloads, such as Microsoft Active Directory, Microsoft SharePoint, Microsoft Exchange, and Microsoft SQL Server, on AWS. Those Quick Starts include the {partner-product-short-name} deployment and architecture described in this guide. You can use them to deploy {partner-product-short-name} along with the additional Microsoft workload. For example, for an automated deployment that includes Active Directory Domain Services and RD gateways, see the https://fwd.aws/N6e7B[AWS Quick Start for Active Directory Domain Services]. 10 | 11 | Implementing the {partner-product-short-name} on the AWS Cloud is an advanced topic. We recommend reviewing the Microsoft documentation for Windows Server 2016 and the AWS documentation https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/connecting_to_windows_instance.html[Connecting to Your Windows Instance]. 12 | 13 | This guide focuses on infrastructure configuration topics that require careful consideration when you are planning and deploying an {partner-product-short-name} infrastructure on the AWS Cloud. It doesn’t cover general Windows Server installation and software configuration tasks. For general software configuration guidance and best practices, consult the Microsoft product documentation. -------------------------------------------------------------------------------- /docs/partner_editable/regions.adoc: -------------------------------------------------------------------------------- 1 | The services deployed by this Quick Start are available in all AWS Regions. -------------------------------------------------------------------------------- /docs/partner_editable/service_limits.adoc: -------------------------------------------------------------------------------- 1 | // Replace the in each row to specify the number of resources used in this deployment. Remove the rows for resources that aren’t used. 2 | |=== 3 | |Resource |This deployment uses 4 | 5 | // Space needed to maintain table headers 6 | |VPCs | 1 7 | |Elastic IP addresses | 1 8 | |AWS Identity and Access Management (IAM) security groups | 1 9 | |IAM roles | 1 10 | |Auto Scaling groups | 1 11 | |EC2 instances | 1-4 12 | |=== 13 | -------------------------------------------------------------------------------- /docs/partner_editable/specialized_knowledge.adoc: -------------------------------------------------------------------------------- 1 | // Replace the content in <> 2 | // Describe or link to specific knowledge requirements; for example: “familiarity with basic concepts in the areas of networking, database operations, and data encryption” or “familiarity with .” 3 | 4 | This Quick Start assumes familiarity with {partner-product-short-name}. 5 | -------------------------------------------------------------------------------- /docs/partner_editable/test_deployment.adoc: -------------------------------------------------------------------------------- 1 | // Add steps as necessary for accessing the software, post-configuration, and testing. Don’t include full usage instructions for your software, but add links to your product documentation for that information. 2 | To test the deployment, use a {partner-product-short-name} client to establish an RDP session to any {partner-product-short-name} servers that were deployed, using the credentials that were specified during the deployment. -------------------------------------------------------------------------------- /manifests/app-manager/app-manager-manifest.yaml: -------------------------------------------------------------------------------- 1 | ssm_app_manager: 2 | should_upload: true 3 | documents_list: 4 | - document_name: AWSQuickStarts-Microsoft-RdGateway-Domain-Joined 5 | template_name: rdgw-domain.template 6 | - document_name: AWSQuickStarts-Microsoft-RdGateway-Existing-VPC 7 | template_name: rdgw-standalone.template 8 | - document_name: AWSQuickStarts-Microsoft-RdGateway 9 | template_name: rdgw-master.template 10 | has_nested_stacks: true 11 | nested_documents: 12 | - resource_name: VPCStack 13 | document_name: AWSQuickStarts-AWS-VPC 14 | - resource_name: RDGWStack 15 | document_name: AWSQuickStarts-Microsoft-RdGateway-Existing-VPC -------------------------------------------------------------------------------- /scripts/Create-LocalUser.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Create-LocalUser.ps1 4 | 5 | .DESCRIPTION 6 | This script creates a new local user, a local group and adds the user to that group as well as the local administrators group 7 | 8 | .EXAMPLE 9 | .\Create-LocalUser -AdminSecParam 'arn:aws:secretsmanager:us-west-2:############:secret:example-VX5fcW' -FullName 'John Doe' -Description 'This is a local user account' 10 | 11 | #> 12 | 13 | [CmdletBinding()] 14 | param( 15 | [Parameter(Mandatory = $true)] 16 | [string]$AdminSecParam 17 | ) 18 | 19 | # Getting Password from Secrets Manager for AD Admin User 20 | Try { 21 | $AdminSecret = Get-SECSecretValue -SecretId $AdminSecParam -ErrorAction Stop | Select-Object -ExpandProperty 'SecretString' 22 | } Catch [System.Exception] { 23 | Write-Output "Failed to get $AdminSecParam Secret $_" 24 | Exit 1 25 | } 26 | 27 | Try { 28 | $AdminPassword = ConvertFrom-Json -InputObject $AdminSecret -ErrorAction Stop 29 | } Catch [System.Exception] { 30 | Write-Output "Failed to convert AdminSecret from JSON $_" 31 | Exit 1 32 | } 33 | 34 | $AdminUserName = $AdminPassword.UserName 35 | $AdminUserPW = ConvertTo-SecureString ($AdminPassword.Password) -AsPlainText -Force 36 | 37 | Write-Output 'Creating local user' 38 | Try { 39 | New-LocalUser -Name $AdminUserName -Password $AdminUserPW -ErrorAction Stop 40 | } Catch [System.Exception]{ 41 | Write-Output "Failed to create local user $_" 42 | Exit 1 43 | } 44 | 45 | Write-Output 'Creating local group' 46 | Try { 47 | New-LocalGroup -Name "RDGWUsers" -Description "Users who will be authorized by default RDGW RAP" -ErrorAction Stop 48 | } Catch [System.Exception]{ 49 | Write-Output "Failed to create local Group $_" 50 | Exit 1 51 | } 52 | 53 | Write-Output 'Adding local user to group' 54 | Try { 55 | Add-LocalGroupMember -Group 'Administrators' -Member $AdminUserName -ErrorAction Stop 56 | Add-LocalGroupMember -Group "RDGWUsers" -Member $AdminUserName -ErrorAction Stop 57 | } Catch [System.Exception]{ 58 | Write-Output "Failed to add local user to group $_" 59 | Exit 1 60 | } -------------------------------------------------------------------------------- /scripts/Initialize-RDGW.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Initialize-RDGW.ps1 4 | 5 | .DESCRIPTION 6 | This creates a selft signed certificate to be used with RDGW. 7 | 8 | .EXAMPLE 9 | .\Initialize-RDGW -DomainDNSName 'example.com' -DomainNetBiosName 'EXAMPLE' -GroupName 'RDS-Group' 10 | 11 | #> 12 | 13 | [CmdletBinding()] 14 | param ( 15 | [Parameter(Mandatory=$true)] 16 | [string]$DomainDNSName, 17 | 18 | [Parameter(Mandatory=$true)] 19 | [string]$DomainNetBiosName, 20 | 21 | [Parameter(Mandatory=$true)] 22 | [string[]]$GroupName 23 | ) 24 | 25 | Import-Module -Name 'RemoteDesktopServices' 26 | 27 | $UserGroups=@() 28 | If ($DomainNetBiosName -eq 'BUILTIN') { 29 | $ServerFQDN = $env:COMPUTERNAME 30 | foreach ($group in $GroupName) { 31 | # Determining whether we should use Group@BUILTIN or GROUP@ComputerName syntax based on SID length of the group 32 | if((Get-LocalGroup $group).sid.BinaryLength -eq 16) { 33 | $UserGroups += "$group@$DomainNetBiosName" 34 | } 35 | else { 36 | $UserGroups += "$group@$env:COMPUTERNAME" 37 | } 38 | } 39 | } Else { 40 | $ServerFQDN = "$env:COMPUTERNAME.$DomainDNSName" 41 | foreach ($group in $GroupName) { 42 | $UserGroups += "$GroupName@$DomainDNSName" 43 | } 44 | } 45 | 46 | Write-Output 'Creating DSC Certificate to Encrypt Credentials in MOF File' 47 | Try { 48 | $Cert = New-SelfSignedCertificate -CertStoreLocation 'Cert:\LocalMachine\My' -DnsName $ServerFQDN -ErrorAction Stop 49 | } Catch [System.Exception] { 50 | Write-Output "Failed to create self signed cert $_" 51 | Exit 1 52 | } 53 | 54 | Write-Output 'Exporting the public key certificate' 55 | Try { 56 | $Cert | Export-Certificate -FilePath "C:\$env:COMPUTERNAME.cer" -Type 'CERT' -Force -ErrorAction Stop 57 | } Catch [System.Exception] { 58 | Write-Output "Failed to copy self signed cert to publickeys directory $_" 59 | Exit 1 60 | } 61 | 62 | # I am not sure if we need the private key or not. 63 | #dir cert:\currentuser\my | Where-Object { $_.Subject -eq "CN=$ServerFQDN" } | Foreach-Object { [system.IO.file]::WriteAllBytes("c:\$env:COMPUTERNAME.cer",($_.Export('CERT', 'secret')) ) } 64 | 65 | Write-Output 'Setting Default CAP' 66 | Try { 67 | New-Item -Path 'RDS:\GatewayServer\CAP' -Name 'Default-CAP' -UserGroups $UserGroups -AuthMethod '1' -ErrorAction Stop 68 | } Catch [System.Exception] { 69 | Write-Output "Failed to set Default CAP $_" 70 | Exit 1 71 | } 72 | 73 | Write-Output 'Setting Default RAP' 74 | Try { 75 | New-Item -Path 'RDS:\GatewayServer\RAP' -Name 'Default-RAP' -UserGroups $UserGroups -ComputerGroupType '2' -ErrorAction Stop 76 | } Catch [System.Exception] { 77 | Write-Output "Failed to set Default RAP $_" 78 | Exit 1 79 | } 80 | 81 | Write-Output 'Getting RDS SSL Thumbprint' 82 | Try { 83 | $CertThumbprint = Get-ChildItem -Path 'cert:\LocalMachine\My' -ErrorAction Stop | Where-Object { $_.Subject -eq "CN=$ServerFQDN" } | Select-Object -ExpandProperty 'Thumbprint' 84 | } Catch [System.Exception] { 85 | Write-Output "Failed getting RDS SSL Thumbprint $_" 86 | Exit 1 87 | } 88 | 89 | Write-Output 'Setting RDS SSL Thumbprint' 90 | Try { 91 | Set-Item -Path 'RDS:\GatewayServer\SSLCertificate\Thumbprint' -Value $CertThumbprint 92 | } Catch [System.Exception] { 93 | Write-Output "Failed setting RDS SSL Thumbprint $_" 94 | Exit 1 95 | } 96 | 97 | Restart-Service -Name 'tsgateway' -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /scripts/Set-EIP.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [Parameter(Mandatory=$true)] 4 | [string[]]$EIPs 5 | ) 6 | 7 | try { 8 | $ErrorActionPreference = "Stop" 9 | 10 | Start-Transcript -Path c:\cfn\log\Set-EIP.ps1.txt -Append 11 | 12 | # Sanitize allowed EIPs to be used from list passed 13 | $allowedEIPs = $EIPs | ? { $PSItem -ne 'Null' } 14 | 15 | # Determine current region 16 | $region = (Invoke-RestMethod http://169.254.169.254/latest/dynamic/instance-identity/document).region 17 | 18 | # Get instance private IP address 19 | $privateIP = (Test-Connection -ComputerName $env:COMPUTERNAME -Count 1).IPV4Address.IPAddressToString 20 | 21 | # Get assigned EIP addresses 22 | $assignedEIP = Get-EC2Address -Region $region | ? { $PSItem.PublicIp -in $allowedEIPs -and $PSItem.PrivateIpAddress -eq $privateIP } 23 | 24 | # If assigned print it, Else wait a random time between 1-30 seconds and try associating... 25 | if ($assignedEIP) { 26 | Write-Host "Elastic IP already assigned:" 27 | $assignedEIP 28 | } else { 29 | $timer = Get-Random -Minimum 1 -Maximum 31 30 | Write-Host "Sleeping for $timer seconds" 31 | Start-Sleep -Seconds $timer 32 | # Get local instance ID 33 | $instanceID = Invoke-RestMethod http://169.254.169.254/latest/meta-data/instance-id 34 | $associated = $false 35 | $tries = 0 36 | do { 37 | # Get avaiable IPs from allowed EIPs that are not associated already 38 | $availableEIPs = Get-EC2Address -Region $region | ? { $PSItem.PublicIp -in $allowedEIPs -and $PSItem.PrivateIpAddress -eq $null } 39 | if($availableEIPs.Count -gt 0) { 40 | # Randomly choose one of the available EIPs for assignment 41 | $randomAvailableEIP = $availableEIPs[(Get-Random -Minimum 0 -Maximum $availableEIPs.Count )] 42 | try { 43 | # Try to associate the EIP 44 | Write-Host "Associating $($randomAvailableEIP.AllocationId): $($randomAvailableEIP.PublicIp)" 45 | $associationID = Register-EC2Address -Region $region -AllocationId $randomAvailableEIP.AllocationId -InstanceId $instanceID 46 | $associated = $true 47 | Write-Host "Successfully associated the Elastic IP" 48 | } catch { 49 | $tries++ 50 | Write-Host "Failed to associate Elastic IP. Try #$tries" 51 | } 52 | } else { 53 | throw "[ERROR] No Elastic IPs available for this region from the allowed list: $($allowedEIPs -join ',')" 54 | } 55 | } while (-not $associated -and $tries -lt 10) 56 | if(-not $associated) { 57 | throw "[ERROR] Unable to associate Elastic IP after multiple tries." 58 | } 59 | 60 | $confirmed = $false 61 | $tries = 0 62 | do { 63 | try { 64 | # Try to get the associated EIP 65 | $associatedEIP = Get-EC2Address -Region $region -AllocationId $randomAvailableEIP.AllocationId 66 | } 67 | catch { 68 | Write-Host "Error fetching associated Elastic IP." 69 | } 70 | # Confirm that it is associated with this instance 71 | if ($associatedEIP.InstanceId -eq $instanceID) { 72 | $confirmed = $true 73 | Write-Host "Confirmed the Elastic IP association:" 74 | $associatedEIP 75 | } else { 76 | $tries++ 77 | Write-Host "Failed to confirm associated Elastic IP. Try#$tries" 78 | Start-Sleep -Seconds 3 79 | } 80 | } while (-not $confirmed -and $tries -lt 40) 81 | if(-not $confirmed) { 82 | throw "[ERROR] Unable to confirm Elastic IP after multiple tries." 83 | } 84 | } 85 | } 86 | catch { 87 | Write-Verbose "$($_.exception.message)@ $(Get-Date)" 88 | $_ | Write-AWSQuickStartException 89 | } 90 | -------------------------------------------------------------------------------- /templates/rdgw-domain.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: >- 3 | This template is intended to be installed into an existing VPC with two public subnets 4 | and an Active Directory domain. It will create an auto-scaling group of RD Gateway 5 | instances in the public VPC subnets. **WARNING** This template creates Amazon EC2 6 | Windows instance and related resources. You will be billed for the AWS resources 7 | used if you create a stack from this template. (qs-1qup6raf2) 8 | Metadata: 9 | QuickStartDocumentation: 10 | EntrypointName: "Parameters for launching into an existing VPC (domain-joined)" 11 | AWS::CloudFormation::Interface: 12 | ParameterGroups: 13 | - Label: 14 | default: Network configuration 15 | Parameters: 16 | - VPCID 17 | - PublicSubnet1ID 18 | - PublicSubnet2ID 19 | - RDGWCIDR 20 | - Label: 21 | default: Amazon EC2 configuration 22 | Parameters: 23 | - KeyPairName 24 | - RDGWInstanceType 25 | - LatestAmiId 26 | - Label: 27 | default: Microsoft Active Directory configuration 28 | Parameters: 29 | - DomainDNSName 30 | - DomainNetBIOSName 31 | - DomainMemberSGID 32 | - DomainAdminUser 33 | - DomainAdminPassword 34 | - Label: 35 | default: Microsoft Remote Desktop Gateway configuration 36 | Parameters: 37 | - NumberOfRDGWHosts 38 | - Label: 39 | default: AWS Quick Start configuration 40 | Parameters: 41 | - QSS3BucketName 42 | - QSS3KeyPrefix 43 | - QSS3BucketRegion 44 | ParameterLabels: 45 | DomainAdminPassword: 46 | default: Domain admin password 47 | DomainAdminUser: 48 | default: Domain admin user name 49 | DomainDNSName: 50 | default: Domain DNS name 51 | DomainMemberSGID: 52 | default: Domain member security group ID 53 | DomainNetBIOSName: 54 | default: Domain NetBIOS name 55 | KeyPairName: 56 | default: Key pair name 57 | LatestAmiId: 58 | default: SSM parameter to grab latest AMI ID 59 | NumberOfRDGWHosts: 60 | default: Number of RDGW hosts 61 | PublicSubnet1ID: 62 | default: Public subnet 1 ID 63 | PublicSubnet2ID: 64 | default: Public subnet 2 ID 65 | QSS3BucketName: 66 | default: Quick Start S3 bucket name 67 | QSS3KeyPrefix: 68 | default: Quick Start S3 key prefix 69 | QSS3BucketRegion: 70 | default: Quick Start S3 bucket Region 71 | RDGWInstanceType: 72 | default: Remote Desktop Gateway instance type 73 | RDGWCIDR: 74 | default: Allowed Remote Desktop Gateway external access CIDR 75 | VPCID: 76 | default: VPC ID 77 | Parameters: 78 | DomainAdminPassword: 79 | Description: Password for the domain admin user. Must be at least 8 characters containing letters, numbers and symbols. 80 | Type: String 81 | MinLength: '8' 82 | MaxLength: '32' 83 | AllowedPattern: (?=^.{6,255}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.* 84 | NoEcho: 'true' 85 | DomainAdminUser: 86 | Description: User name for the domain administrator. This parameter is separate from the 87 | default administrator account. 88 | Type: String 89 | Default: Admin 90 | MinLength: '5' 91 | MaxLength: '25' 92 | AllowedPattern: '[a-zA-Z0-9]*' 93 | DomainNetBIOSName: 94 | AllowedPattern: '[a-zA-Z0-9\-]+' 95 | Description: NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows (for example, EXAMPLE). 96 | MaxLength: '15' 97 | MinLength: '1' 98 | Type: String 99 | DomainDNSName: 100 | Description: Fully qualified domain name (FQDN) such as example.com. 101 | Type: String 102 | MinLength: '2' 103 | MaxLength: '255' 104 | AllowedPattern: '[a-zA-Z0-9\-]+\..+' 105 | DomainMemberSGID: 106 | Description: ID of the domain member security group (for example, sg-7f16e910). 107 | Type: AWS::EC2::SecurityGroup::Id 108 | KeyPairName: 109 | Description: Public/private key pairs allow you to securely connect to your instance after it launches. 110 | Type: AWS::EC2::KeyPair::KeyName 111 | LatestAmiId: 112 | Type: AWS::SSM::Parameter::Value 113 | Default: /aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base 114 | NumberOfRDGWHosts: 115 | AllowedValues: 116 | - '1' 117 | - '2' 118 | - '3' 119 | - '4' 120 | Default: '1' 121 | Description: Enter the number of Remote Desktop Gateway hosts to create. 122 | Type: String 123 | PublicSubnet1ID: 124 | Description: ID of the public subnet 1 that you want to provision the first Remote Desktop Gateway into (for example, subnet-a0246dcd). 125 | Type: AWS::EC2::Subnet::Id 126 | PublicSubnet2ID: 127 | Description: ID of the public subnet 2 you want to provision the second Remote Desktop Gateway into (for example, subnet-e3246d8e). 128 | Type: AWS::EC2::Subnet::Id 129 | QSS3BucketName: 130 | AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ 131 | ConstraintDescription: The Quick Start bucket name can include numbers, lowercase 132 | letters, uppercase letters, and hyphens (-). It cannot start or end with a 133 | hyphen (-). 134 | Default: aws-quickstart 135 | Description: Name of the S3 bucket for your copy of the Quick Start assets. 136 | Keep the default name unless you are customizing the template. 137 | Changing the name updates code references to point to a new Quick 138 | Start location. This name can include numbers, lowercase letters, 139 | uppercase letters, and hyphens, but do not start or end with a hyphen (-). 140 | See https://aws-quickstart.github.io/option1.html. 141 | Type: String 142 | QSS3KeyPrefix: 143 | AllowedPattern: ^[0-9a-zA-Z-/]*$ 144 | ConstraintDescription: The Quick Start S3 key prefix can include numbers, lowercase letters, 145 | uppercase letters, hyphens (-), and forward slash (/). 146 | Default: quickstart-microsoft-rdgateway/ 147 | Description: S3 key prefix that is used to simulate a directory for your copy of the 148 | Quick Start assets. Keep the default prefix unless you are customizing 149 | the template. Changing this prefix updates code references to point to 150 | a new Quick Start location. This prefix can include numbers, lowercase 151 | letters, uppercase letters, hyphens (-), and forward slashes (/). 152 | See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html 153 | and https://aws-quickstart.github.io/option1.html. 154 | Type: String 155 | QSS3BucketRegion: 156 | Default: 'us-east-1' 157 | Description: "'AWS Region where the Quick Start S3 bucket (QSS3BucketName) is 158 | hosted. Keep the default Region unless you are customizing the template. 159 | Changing this Region updates code references to point to a new Quick Start location. 160 | When using your own bucket, specify the Region. 161 | See https://aws-quickstart.github.io/option1.html.'" 162 | Type: String 163 | RDGWInstanceType: 164 | Description: Amazon EC2 instance type for the Remote Desktop Gateway instances. 165 | Type: String 166 | Default: t3.2xlarge 167 | AllowedValues: 168 | - t2.small 169 | - t2.medium 170 | - t2.large 171 | - t3.micro 172 | - t3.small 173 | - t3.medium 174 | - t3.large 175 | - t3.xlarge 176 | - t3.2xlarge 177 | - t3a.micro 178 | - t3a.small 179 | - t3a.medium 180 | - t3a.large 181 | - t3a.xlarge 182 | - t3a.2xlarge 183 | - m5.large 184 | - m5.xlarge 185 | - m5.2xlarge 186 | - m5.4xlarge 187 | - m5a.large 188 | - m5a.xlarge 189 | - m5a.2xlarge 190 | RDGWCIDR: 191 | AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ 192 | Description: Allowed CIDR Block for external access to the Remote Desktop Gateways. 193 | Type: String 194 | VPCID: 195 | Description: ID of the VPC (for example, vpc-0343606e). 196 | Type: AWS::EC2::VPC::Id 197 | Rules: 198 | SubnetsInVPC: 199 | Assertions: 200 | - Assert: !EachMemberIn 201 | - !ValueOfAll 202 | - AWS::EC2::Subnet::Id 203 | - VpcId 204 | - !RefAll 'AWS::EC2::VPC::Id' 205 | AssertDescription: All subnets must be in the VPC. 206 | CheckSupportedInstances: 207 | RuleCondition: !Contains 208 | - - m4.large 209 | - m4.xlarge 210 | - m4.2xlarge 211 | - m4.4xlarge 212 | - !Ref 'RDGWInstanceType' 213 | Assertions: 214 | - Assert: !Not 215 | - !Contains 216 | - - eu-west-3 217 | - !Ref 'AWS::Region' 218 | AssertDescription: M4 instances are not available in the Paris region. 219 | Conditions: 220 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 221 | Resources: 222 | DomainJoinSecrets: 223 | Type: AWS::SecretsManager::Secret 224 | Properties: 225 | Name: !Sub 'DomainJoinSecrets-${AWS::StackName}' 226 | Description: Secrets to join AD domain. 227 | SecretString: !Sub '{"username":"${DomainNetBIOSName}\\${DomainAdminUser}","password":"${DomainAdminPassword}"}' 228 | SetupConfigurationDoc: 229 | Type: AWS::SSM::Document 230 | Properties: 231 | DocumentType: Automation 232 | Content: 233 | schemaVersion: "0.3" 234 | description: "Configure instances on launch." 235 | assumeRole: "{{AutomationAssumeRole}}" 236 | parameters: 237 | InstanceId: 238 | description: "ID of the instance." 239 | type: "String" 240 | ASGName: 241 | description: "Auto Scaling group name." 242 | type: "String" 243 | LCHName: 244 | description: "Life cycle hook name." 245 | type: "String" 246 | DomainDNSName: 247 | default: "example.com" 248 | description: "Fully qualified domain name (FQDN) of the forest root domain (for example, example.com)." 249 | type: "String" 250 | DomainNetBIOSName: 251 | default: "example" 252 | description: "NetBIOS name of the domain (up to 15 characters) for users of earlier versions of Windows (for example, EXAMPLE)." 253 | type: "String" 254 | QSS3BucketName: 255 | default: "aws-quickstart" 256 | description: "Name of the S3 bucket for your copy of the Quick Start assets. 257 | Keep the default name unless you are customizing the template. 258 | Changing the name updates code references to point to a new Quick 259 | Start location. This name can include numbers, lowercase letters, 260 | uppercase letters, and hyphens, but do not start or end with a hyphen (-). 261 | See https://aws-quickstart.github.io/option1.html." 262 | type: "String" 263 | QSS3KeyPrefix: 264 | default: "quickstart-microsoft-rdgateway/" 265 | description: "S3 key prefix that is used to simulate a directory for your copy of the 266 | Quick Start assets. Keep the default prefix unless you are customizing 267 | the template. Changing this prefix updates code references to point to 268 | a new Quick Start location. This prefix can include numbers, lowercase 269 | letters, uppercase letters, hyphens (-), and forward slashes (/). 270 | See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html 271 | and https://aws-quickstart.github.io/option1.html." 272 | type: "String" 273 | URLSuffix: 274 | default: "amazonaws.com" 275 | description: "AWS URL suffix." 276 | type: "String" 277 | AutomationAssumeRole: 278 | default: "" 279 | description: "(Optional) The ARN of the role that allows automation to perform the actions on your behalf." 280 | type: "String" 281 | mainSteps: 282 | - name: waitUntilInstanceStateRunning 283 | action: aws:waitForAwsResourceProperty 284 | timeoutSeconds: 600 285 | inputs: 286 | Service: ec2 287 | Api: DescribeInstanceStatus 288 | InstanceIds: 289 | - "{{InstanceId}}" 290 | PropertySelector: "$.InstanceStatuses[0].InstanceState.Name" 291 | DesiredValues: 292 | - running 293 | - name: assertInstanceStateRunning 294 | action: aws:assertAwsResourceProperty 295 | inputs: 296 | Service: ec2 297 | Api: DescribeInstanceStatus 298 | InstanceIds: 299 | - "{{InstanceId}}" 300 | PropertySelector: "$.InstanceStatuses[0].InstanceState.Name" 301 | DesiredValues: 302 | - running 303 | - name: "InstallDSCmodules" 304 | action: aws:runCommand 305 | onFailure: "step:abandonHookAction" 306 | inputs: 307 | DocumentName: AWS-RunPowerShellScript 308 | InstanceIds: 309 | - "{{InstanceId}}" 310 | CloudWatchOutputConfig: 311 | CloudWatchOutputEnabled: "true" 312 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/SetupConfiguration" 313 | Parameters: 314 | commands: 315 | - | 316 | <# 317 | .SYNOPSIS 318 | install-dsc-modules.ps1 319 | 320 | .DESCRIPTION 321 | This script downloads and installs the required PowerShell modules to create and configure Active Directory Domain Controllers. 322 | It also creates a self signed certificate to be uses with PowerShell DSC. 323 | 324 | .EXAMPLE 325 | .\install-dsc-modules 326 | #> 327 | 328 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 329 | 330 | Write-Output 'Installing NuGet Package Provider' 331 | Try { 332 | Install-PackageProvider -Name 'NuGet' -MinimumVersion '2.8.5' -Force -ErrorAction Stop 333 | } Catch [System.Exception] { 334 | Write-Output "Failed to install NuGet Package Provider $_" 335 | Exit 1 336 | } 337 | 338 | Write-Output 'Setting PSGallery Respository to trusted' 339 | Try { 340 | Set-PSRepository -Name 'PSGallery' -InstallationPolicy 'Trusted' -ErrorAction Stop 341 | } Catch [System.Exception] { 342 | Write-Output "Failed to set PSGallery Respository to trusted $_" 343 | Exit 1 344 | } 345 | 346 | Write-Output 'Installing the needed Powershell DSC modules for this Quick Start' 347 | $Modules = @( 348 | @{ 349 | Name = 'ActiveDirectoryDsc' 350 | Version = '6.0.1' 351 | }, 352 | @{ 353 | Name = 'ComputerManagementDsc' 354 | Version = '8.4.0' 355 | } 356 | ) 357 | 358 | Foreach ($Module in $Modules) { 359 | Try { 360 | Install-Module -Name $Module.Name -RequiredVersion $Module.Version -ErrorAction Stop 361 | } Catch [System.Exception] { 362 | Write-Output "Failed to Import Modules $_" 363 | Exit 1 364 | } 365 | } 366 | - name: "GenerateDomainJoinMof" 367 | action: aws:runCommand 368 | onFailure: "step:abandonHookAction" 369 | inputs: 370 | DocumentName: AWS-RunPowerShellScript 371 | InstanceIds: 372 | - "{{InstanceId}}" 373 | CloudWatchOutputConfig: 374 | CloudWatchOutputEnabled: "true" 375 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/SetupConfiguration" 376 | Parameters: 377 | commands: 378 | - !Sub | 379 | [CmdletBinding()] 380 | # Incoming Parameters for Script, CloudFormation\SSM Parameters being passed in 381 | param() 382 | 383 | # Creating Configuration Data Block that allows plain test password 384 | # for DSC Configuration Processing 385 | $ConfigurationData = @{ 386 | AllNodes = @( 387 | @{ 388 | NodeName="*" 389 | PSDscAllowPlainTextPassword = $true 390 | PSDscAllowDomainUser = $true 391 | }, 392 | @{ 393 | NodeName = 'localhost' 394 | } 395 | ) 396 | } 397 | 398 | Configuration DomainJoin { 399 | $ss = ConvertTo-SecureString -String 'stringdoesntmatter' -AsPlaintext -Force 400 | $Credentials = New-Object PSCredential('${DomainJoinSecrets}', $ss) 401 | 402 | Import-Module -Name PSDesiredStateConfiguration 403 | Import-Module -Name ComputerManagementDsc 404 | 405 | Import-DscResource -Module PSDesiredStateConfiguration 406 | Import-DscResource -Module ComputerManagementDsc 407 | 408 | Node 'localhost' { 409 | 410 | Computer JoinDomain { 411 | Name = $env:COMPUTERNAME 412 | DomainName = '{{DomainDNSName}}' 413 | Credential = $Credentials 414 | } 415 | } 416 | } 417 | 418 | DomainJoin -OutputPath 'C:\AWSQuickstart\DomainJoin' -ConfigurationData $ConfigurationData 419 | - name: "applyDomainJoin" 420 | action: aws:runCommand 421 | onFailure: step:abandonHookAction 422 | inputs: 423 | DocumentName: AWS-ApplyDSCMofs 424 | InstanceIds: 425 | - "{{InstanceId}}" 426 | CloudWatchOutputConfig: 427 | CloudWatchOutputEnabled: "true" 428 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/SetupConfiguration" 429 | Parameters: 430 | MofsToApply: 431 | - "C:\\AWSQuickstart\\DomainJoin\\localhost.mof" 432 | ServicePath: 433 | - default 434 | MofOperationMode: 435 | - Apply 436 | ModuleSourceBucketName: 437 | - "NONE" 438 | AllowPSGalleryModuleSource: 439 | - "True" 440 | RebootBehavior: 441 | - "AfterMof" 442 | UseComputerNameForReporting: 443 | - "False" 444 | EnableVerboseLogging: 445 | - "False" 446 | EnableDebugLogging: 447 | - "False" 448 | - name: "installRDP" 449 | action: aws:runCommand 450 | onFailure: "step:abandonHookAction" 451 | inputs: 452 | DocumentName: AWS-RunPowerShellScript 453 | InstanceIds: 454 | - "{{InstanceId}}" 455 | CloudWatchOutputConfig: 456 | CloudWatchOutputEnabled: "true" 457 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/SetupConfiguration" 458 | Parameters: 459 | commands: 460 | - | 461 | Write-Output 'Installing RD Gateway components' 462 | Try { 463 | Install-WindowsFeature RDS-Gateway,RSAT-RDS-Gateway 464 | } Catch [System.Exception] { 465 | Write-Output "Failed to install RD Gateway components $_" 466 | Exit 1 467 | } 468 | - name: "configurerdgw" 469 | action: "aws:runCommand" 470 | onFailure: "step:abandonHookAction" 471 | inputs: 472 | DocumentName: "AWS-RunRemoteScript" 473 | InstanceIds: 474 | - "{{InstanceId}}" 475 | CloudWatchOutputConfig: 476 | CloudWatchOutputEnabled: "true" 477 | CloudWatchLogGroupName: !Sub '/QuickStart/rdgateway/${AWS::StackName}/SetupConfiguration' 478 | Parameters: 479 | sourceType: "S3" 480 | sourceInfo: 481 | !Sub 482 | - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Initialize-RDGW.ps1"}' 483 | - S3Bucket: !If 484 | - UsingDefaultBucket 485 | - !Sub '${QSS3BucketName}-${AWS::Region}' 486 | - !Ref QSS3BucketName 487 | S3Region: !If 488 | - UsingDefaultBucket 489 | - !Ref AWS::Region 490 | - !Ref QSS3BucketRegion 491 | commandLine: "./Initialize-RDGW.ps1 -DomainDNSName {{DomainDNSName}} -DomainNetBiosName {{DomainNetBIOSName}} -GroupName 'domain admins'" 492 | - name: "completeHookAction" 493 | action: aws:executeAwsApi 494 | isEnd: true 495 | inputs: 496 | Service: autoscaling 497 | Api: CompleteLifecycleAction 498 | AutoScalingGroupName: "{{ASGName}}" 499 | InstanceId: "{{InstanceId}}" 500 | LifecycleActionResult: CONTINUE 501 | LifecycleHookName: "{{LCHName}}" 502 | - name: "abandonHookAction" 503 | action: aws:executeAwsApi 504 | isEnd: true 505 | inputs: 506 | Service: autoscaling 507 | Api: CompleteLifecycleAction 508 | AutoScalingGroupName: "{{ASGName}}" 509 | InstanceId: "{{InstanceId}}" 510 | LifecycleActionResult: ABANDON 511 | LifecycleHookName: "{{LCHName}}" 512 | RemoveConfigurationDoc: 513 | Type: AWS::SSM::Document 514 | Properties: 515 | DocumentType: Automation 516 | Content: 517 | schemaVersion: "0.3" 518 | description: "Remove EC2 Instances from AD domain." 519 | assumeRole: "{{AutomationAssumeRole}}" 520 | parameters: 521 | InstanceId: 522 | description: "ID of the instance." 523 | type: "String" 524 | ASGName: 525 | description: "Auto Scaling group name." 526 | type: "String" 527 | LCHName: 528 | description: "Life cycle hook name." 529 | type: "String" 530 | AutomationAssumeRole: 531 | default: "" 532 | description: "(Optional) The ARN of the role that allows automation to perform the actions on your behalf." 533 | type: "String" 534 | mainSteps: 535 | - name: "GenerateRemoveJoinMof" 536 | action: aws:runCommand 537 | onFailure: "step:abandonHookAction" 538 | inputs: 539 | DocumentName: AWS-RunPowerShellScript 540 | InstanceIds: 541 | - "{{InstanceId}}" 542 | CloudWatchOutputConfig: 543 | CloudWatchOutputEnabled: "true" 544 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/RemoveConfiguration" 545 | Parameters: 546 | commands: 547 | - !Sub | 548 | [CmdletBinding()] 549 | # Incoming Parameters for Script, CloudFormation\SSM Parameters being passed in 550 | param() 551 | 552 | $ConfigurationData = @{ 553 | AllNodes = @( 554 | @{ 555 | NodeName="*" 556 | PSDscAllowPlainTextPassword = $true 557 | PSDscAllowDomainUser = $true 558 | }, 559 | @{ 560 | NodeName = 'localhost' 561 | } 562 | ) 563 | } 564 | 565 | Configuration RemoveDomain { 566 | 567 | $ss = ConvertTo-SecureString -String 'stringdoesntmatter' -AsPlaintext -Force 568 | $Credentials = New-Object PSCredential("${DomainJoinSecrets}", $ss) 569 | 570 | 571 | Import-Module -Name PSDesiredStateConfiguration 572 | Import-Module -Name ComputerManagementDsc 573 | Import-Module -Name ActiveDirectoryDsc 574 | 575 | Import-DscResource -Module PSDesiredStateConfiguration 576 | Import-DscResource -Module ComputerManagementDsc 577 | Import-DscResource -Module ActiveDirectoryDsc 578 | 579 | 580 | Node localhost { 581 | WindowsFeature RSAT-AD-PowerShell { 582 | Name = 'RSAT-AD-PowerShell' 583 | Ensure = 'Present' 584 | } 585 | 586 | ADComputer RemoveDomain { 587 | ComputerName = $env:COMPUTERNAME 588 | Ensure = 'Absent' 589 | Credential = $Credentials 590 | DependsOn = "[WindowsFeature]RSAT-AD-PowerShell" 591 | } 592 | } 593 | } 594 | 595 | RemoveDomain -OutputPath 'C:\AWSQuickstart\RemoveDomain' -ConfigurationData $ConfigurationData 596 | - name: "RemoveFromDomain" 597 | action: aws:runCommand 598 | onFailure: step:abandonHookAction 599 | inputs: 600 | DocumentName: AWS-ApplyDSCMofs 601 | InstanceIds: 602 | - "{{InstanceId}}" 603 | CloudWatchOutputConfig: 604 | CloudWatchOutputEnabled: "true" 605 | CloudWatchLogGroupName: !Sub "/QuickStart/rdgateway/${AWS::StackName}/RemoveConfiguration" 606 | Parameters: 607 | MofsToApply: 608 | - "C:\\AWSQuickstart\\RemoveDomain\\localhost.mof" 609 | ServicePath: 610 | - default 611 | MofOperationMode: 612 | - Apply 613 | ModuleSourceBucketName: 614 | - "NONE" 615 | AllowPSGalleryModuleSource: 616 | - "True" 617 | RebootBehavior: 618 | - "AfterMof" 619 | UseComputerNameForReporting: 620 | - "False" 621 | EnableVerboseLogging: 622 | - "False" 623 | EnableDebugLogging: 624 | - "False" 625 | - name: "completeHookAction" 626 | action: aws:executeAwsApi 627 | isEnd: true 628 | inputs: 629 | Service: autoscaling 630 | Api: CompleteLifecycleAction 631 | AutoScalingGroupName: "{{ASGName}}" 632 | InstanceId: "{{InstanceId}}" 633 | LifecycleActionResult: CONTINUE 634 | LifecycleHookName: "{{LCHName}}" 635 | - name: "abandonHookAction" 636 | action: aws:executeAwsApi 637 | isEnd: true 638 | inputs: 639 | Service: autoscaling 640 | Api: CompleteLifecycleAction 641 | AutoScalingGroupName: "{{ASGName}}" 642 | InstanceId: "{{InstanceId}}" 643 | LifecycleActionResult: ABANDON 644 | LifecycleHookName: "{{LCHName}}" 645 | RDGWHostRole: 646 | Type: AWS::IAM::Role 647 | Properties: 648 | ManagedPolicyArns: 649 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' 650 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' 651 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMDirectoryServiceAccess' 652 | Policies: 653 | - PolicyDocument: 654 | Version: '2012-10-17' 655 | Statement: 656 | - Action: 657 | - s3:GetObject 658 | Resource: !Sub 659 | - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* 660 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 661 | Effect: Allow 662 | - Action: 663 | - s3:ListBucket 664 | Resource: !Sub 665 | - arn:${AWS::Partition}:s3:::${S3Bucket} 666 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 667 | Effect: Allow 668 | PolicyName: aws-quick-start-s3-policy 669 | - PolicyDocument: 670 | Version: '2012-10-17' 671 | Statement: 672 | - Action: 673 | - s3:GetObject 674 | Resource: 675 | - !Sub 'arn:${AWS::Partition}:s3:::aws-ssm-${AWS::Region}/*' 676 | - !Sub 'arn:${AWS::Partition}:s3:::aws-windows-downloads-${AWS::Region}/*' 677 | - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-${AWS::Region}/*' 678 | - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-packages-${AWS::Region}/*' 679 | - !Sub 'arn:${AWS::Partition}:s3:::${AWS::Region}-birdwatcher-prod/*' 680 | - !Sub 'arn:${AWS::Partition}:s3:::patch-baseline-snapshot-${AWS::Region}/*' 681 | Effect: Allow 682 | PolicyName: ssm-custom-s3-policy 683 | - PolicyDocument: 684 | Version: '2012-10-17' 685 | Statement: 686 | - Effect: Allow 687 | Action: 688 | - secretsmanager:GetSecretValue 689 | - secretsmanager:DescribeSecret 690 | Resource: 691 | - !Ref 'DomainJoinSecrets' 692 | PolicyName: ssm-secrets-policy 693 | Path: / 694 | AssumeRolePolicyDocument: 695 | Statement: 696 | - Action: 697 | - sts:AssumeRole 698 | Principal: 699 | Service: 700 | - ec2.amazonaws.com 701 | Effect: Allow 702 | Version: '2012-10-17' 703 | RDGWHostProfile: 704 | Type: AWS::IAM::InstanceProfile 705 | Properties: 706 | Roles: 707 | - !Ref 'RDGWHostRole' 708 | Path: / 709 | EventBridgeSSMAutoRole: 710 | Type: AWS::IAM::Role 711 | Properties: 712 | Policies: 713 | - PolicyDocument: 714 | Version: '2012-10-17' 715 | Statement: 716 | - Effect: Allow 717 | Action: 718 | - ssm:StartAutomationExecution 719 | Resource: 720 | - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${RemoveConfigurationDoc}:$DEFAULT' 721 | - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${SetupConfigurationDoc}:$DEFAULT' 722 | - Effect: Allow 723 | Action: 724 | - iam:PassRole 725 | Resource: 726 | - !GetAtt ExecutionResourceRole.Arn 727 | Condition: {"StringLikeIfExists": {"iam:PassedToService": "ssm.amazonaws.com"}} 728 | PolicyName: "EventBridge_Invoke_SSM_Automation" 729 | Path: /service-role/ 730 | AssumeRolePolicyDocument: 731 | Version: '2012-10-17' 732 | Statement: 733 | - Effect: Allow 734 | Principal: 735 | Service: 736 | - events.amazonaws.com 737 | Action: sts:AssumeRole 738 | ExecutionResourceRole: 739 | Type: AWS::IAM::Role 740 | Properties: 741 | Policies: 742 | - PolicyDocument: 743 | Version: '2012-10-17' 744 | Statement: 745 | - Effect: Allow 746 | Action: 747 | - autoscaling:CompleteLifecycleAction 748 | Resource: !Sub arn:${AWS::Partition}:autoscaling:*:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName} 749 | PolicyName: asg-lch-complete 750 | AssumeRolePolicyDocument: 751 | Version: '2012-10-17' 752 | Statement: 753 | - Effect: Allow 754 | Principal: 755 | Service: 756 | - ssm.amazonaws.com 757 | Action: 758 | - sts:AssumeRole 759 | Path: / 760 | ManagedPolicyArns: 761 | - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole 762 | Description: New IAM role to allow SSM access. 763 | ALBResource: 764 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 765 | Properties: 766 | Type: network 767 | Subnets: 768 | - !Ref 'PublicSubnet1ID' 769 | - !Ref 'PublicSubnet2ID' 770 | ALBTargetGroup3389: 771 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 772 | Properties: 773 | Port: 3389 774 | Protocol: TCP 775 | VpcId: !Ref 'VPCID' 776 | TargetGroupAttributes: 777 | - Key: deregistration_delay.timeout_seconds 778 | Value: '60' 779 | - Key: stickiness.enabled 780 | Value: 'true' 781 | ALBTargetGroup443: 782 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 783 | Properties: 784 | Port: 443 785 | Protocol: TCP 786 | VpcId: !Ref 'VPCID' 787 | TargetGroupAttributes: 788 | - Key: deregistration_delay.timeout_seconds 789 | Value: '60' 790 | - Key: stickiness.enabled 791 | Value: 'true' 792 | ALBTargetGroup3391: 793 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 794 | Properties: 795 | Port: 3391 796 | Protocol: UDP 797 | VpcId: !Ref 'VPCID' 798 | TargetGroupAttributes: 799 | - Key: deregistration_delay.timeout_seconds 800 | Value: '60' 801 | - Key: stickiness.enabled 802 | Value: 'true' 803 | ALBListener3389: 804 | Type: AWS::ElasticLoadBalancingV2::Listener 805 | Properties: 806 | LoadBalancerArn: !Ref 'ALBResource' 807 | Port: 3389 808 | Protocol: TCP 809 | DefaultActions: 810 | - Type: forward 811 | TargetGroupArn: !Ref 'ALBTargetGroup3389' 812 | ALBListener443: 813 | Type: AWS::ElasticLoadBalancingV2::Listener 814 | Properties: 815 | LoadBalancerArn: !Ref 'ALBResource' 816 | Port: 443 817 | Protocol: TCP 818 | DefaultActions: 819 | - Type: forward 820 | TargetGroupArn: !Ref 'ALBTargetGroup443' 821 | ALBListener3391: 822 | Type: AWS::ElasticLoadBalancingV2::Listener 823 | Properties: 824 | LoadBalancerArn: !Ref 'ALBResource' 825 | Port: 3391 826 | Protocol: UDP 827 | DefaultActions: 828 | - Type: forward 829 | TargetGroupArn: !Ref 'ALBTargetGroup3391' 830 | RDGWLaunchTemplate: 831 | Type: AWS::EC2::LaunchTemplate 832 | DeletionPolicy: Delete 833 | Properties: 834 | LaunchTemplateData: 835 | MetadataOptions: 836 | HttpEndpoint: enabled 837 | HttpTokens: required 838 | InstanceType: !Ref 'RDGWInstanceType' 839 | ImageId: !Ref 'LatestAmiId' 840 | SecurityGroupIds: 841 | - !Ref RemoteDesktopGatewaySG 842 | - !Ref DomainMemberSGID 843 | IamInstanceProfile: 844 | Name: !Ref 'RDGWHostProfile' 845 | BlockDeviceMappings: 846 | - DeviceName: /dev/sda1 847 | Ebs: 848 | VolumeSize: 50 849 | VolumeType: gp2 850 | KeyName: !Ref 'KeyPairName' 851 | TagSpecifications: 852 | - 853 | ResourceType: 'instance' 854 | Tags: 855 | - Key: "Name" 856 | Value: "RDGW" 857 | RDGWAutoScalingGroup: 858 | DependsOn: ScaleUpEventBridgeResource 859 | Type: AWS::AutoScaling::AutoScalingGroup 860 | Properties: 861 | AutoScalingGroupName: !Sub "${AWS::StackName}" 862 | LaunchTemplate: 863 | LaunchTemplateId: !Ref 'RDGWLaunchTemplate' 864 | Version: !GetAtt 'RDGWLaunchTemplate.LatestVersionNumber' 865 | VPCZoneIdentifier: 866 | - !Ref 'PublicSubnet1ID' 867 | - !Ref 'PublicSubnet2ID' 868 | MinSize: !Ref 'NumberOfRDGWHosts' 869 | MaxSize: !Ref 'NumberOfRDGWHosts' 870 | Cooldown: '300' 871 | DesiredCapacity: !Ref 'NumberOfRDGWHosts' 872 | TargetGroupARNs: 873 | - !Ref 'ALBTargetGroup3389' 874 | - !Ref 'ALBTargetGroup443' 875 | - !Ref 'ALBTargetGroup3391' 876 | LifecycleHookSpecificationList: 877 | - LifecycleTransition: autoscaling:EC2_INSTANCE_LAUNCHING 878 | LifecycleHookName: DomainJoinHook 879 | DefaultResult: ABANDON 880 | HeartbeatTimeout: 1200 881 | - LifecycleTransition: autoscaling:EC2_INSTANCE_TERMINATING 882 | LifecycleHookName: DomainUnjoinHook 883 | DefaultResult: ABANDON 884 | HeartbeatTimeout: 600 885 | RemoteDesktopGatewaySG: 886 | Type: AWS::EC2::SecurityGroup 887 | Properties: 888 | GroupDescription: Enable RDP access from the Internet 889 | VpcId: !Ref 'VPCID' 890 | SecurityGroupIngress: 891 | - IpProtocol: tcp 892 | FromPort: 3389 893 | ToPort: 3389 894 | CidrIp: !Ref 'RDGWCIDR' 895 | - IpProtocol: tcp 896 | FromPort: 3389 897 | ToPort: 3389 898 | CidrIp: !Ref 'RDGWCIDR' 899 | - IpProtocol: tcp 900 | FromPort: 443 901 | ToPort: 443 902 | CidrIp: !Ref 'RDGWCIDR' 903 | - IpProtocol: udp 904 | FromPort: 3391 905 | ToPort: 3391 906 | CidrIp: !Ref 'RDGWCIDR' 907 | - IpProtocol: icmp 908 | FromPort: -1 909 | ToPort: -1 910 | CidrIp: !Ref 'RDGWCIDR' 911 | ScaleUpEventBridgeResource: 912 | Type: AWS::Events::Rule 913 | Properties: 914 | State: ENABLED 915 | Description: Run configuration document that joins domain and configures RDGW. 916 | EventPattern: 917 | source: 918 | - aws.autoscaling 919 | detail-type: 920 | - EC2 Instance-launch Lifecycle Action 921 | detail: 922 | AutoScalingGroupName: 923 | - !Sub "${AWS::StackName}" 924 | Targets: 925 | - Arn: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${SetupConfigurationDoc}:$DEFAULT' 926 | Id: Windows-Scale-Out 927 | RoleArn: !GetAtt EventBridgeSSMAutoRole.Arn 928 | InputTransformer: 929 | InputPathsMap: 930 | InstanceId: $.detail.EC2InstanceId 931 | ASGName: $.detail.AutoScalingGroupName 932 | LCHName: $.detail.LifecycleHookName 933 | InputTemplate: !Sub '{"AutomationAssumeRole":["${ExecutionResourceRole.Arn}"],"InstanceId":[],"ASGName":[],"LCHName":[],"QSS3BucketName":["${QSS3BucketName}"],"QSS3KeyPrefix":["${QSS3KeyPrefix}"],"URLSuffix":["${AWS::URLSuffix}"], "DomainNetBIOSName":["${DomainNetBIOSName}"], "DomainDNSName":["${DomainDNSName}"]}' 934 | ScaleDownEventBridgeResource: 935 | Type: AWS::Events::Rule 936 | Properties: 937 | State: ENABLED 938 | Description: Run removal document that unjoins domain. 939 | EventPattern: 940 | source: 941 | - aws.autoscaling 942 | detail-type: 943 | - EC2 Instance-terminate Lifecycle Action 944 | detail: 945 | AutoScalingGroupName: 946 | - !Sub "${AWS::StackName}" 947 | Targets: 948 | - Arn: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${RemoveConfigurationDoc}:$DEFAULT' 949 | Id: Windows-Scale-In 950 | RoleArn: !GetAtt EventBridgeSSMAutoRole.Arn 951 | InputTransformer: 952 | InputPathsMap: 953 | InstanceId: $.detail.EC2InstanceId 954 | ASGName: $.detail.AutoScalingGroupName 955 | LCHName: $.detail.LifecycleHookName 956 | InputTemplate: !Sub '{"AutomationAssumeRole":["${ExecutionResourceRole.Arn}"],"InstanceId":[],"ASGName":[],"LCHName":[]}' 957 | Outputs: 958 | RDPURL: 959 | Description: ELB DNS name to connect to RDP Gateway. 960 | Value: !GetAtt 'ALBResource.DNSName' 961 | RemoteDesktopGatewaySGID: 962 | Value: !Ref 'RemoteDesktopGatewaySG' 963 | Description: Remote Desktop Gateway security group ID. 964 | -------------------------------------------------------------------------------- /templates/rdgw-master.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: >- 3 | This template creates a VPC infrastructure for a multi-AZ, multi-tier deployment 4 | of a Windows based Application infrastructure. It will deploy a Remote Desktop Gateway 5 | and managed NAT gateways into the public subnet for each Availability 6 | Zone. **WARNING** This template creates Amazon EC2 Windows instance and related resources. 7 | You will be billed for the AWS resources used if you create a stack from this template. (qs-1qup6rafe) 8 | Metadata: 9 | QuickStartDocumentation: 10 | EntrypointName: "Parameters for deploying into a new VPC" 11 | AWS::CloudFormation::Interface: 12 | ParameterGroups: 13 | - Label: 14 | default: Network configuration 15 | Parameters: 16 | - AvailabilityZones 17 | - VPCTenancy 18 | - VPCCIDR 19 | - PrivateSubnet1CIDR 20 | - PrivateSubnet2CIDR 21 | - PublicSubnet1CIDR 22 | - PublicSubnet2CIDR 23 | - RDGWCIDR 24 | - Label: 25 | default: Amazon EC2 configuration 26 | Parameters: 27 | - KeyPairName 28 | - RDGWInstanceType 29 | - Label: 30 | default: Microsoft Remote Desktop Gateway configuration 31 | Parameters: 32 | - NumberOfRDGWHosts 33 | - AdminUser 34 | - AdminPassword 35 | - SetupAppInsightsMonitoring 36 | - Label: 37 | default: AWS Quick Start configuration 38 | Parameters: 39 | - QSS3BucketName 40 | - QSS3KeyPrefix 41 | - QSS3BucketRegion 42 | ParameterLabels: 43 | AvailabilityZones: 44 | default: Availability Zones 45 | AdminPassword: 46 | default: Admin password 47 | AdminUser: 48 | default: Admin user name 49 | KeyPairName: 50 | default: Key pair name 51 | NumberOfRDGWHosts: 52 | default: Number of RDGW hosts 53 | PrivateSubnet1CIDR: 54 | default: Private subnet 1 CIDR 55 | PrivateSubnet2CIDR: 56 | default: Private subnet 2 CIDR 57 | PublicSubnet1CIDR: 58 | default: Public subnet 1 CIDR 59 | PublicSubnet2CIDR: 60 | default: Public subnet 2 CIDR 61 | QSS3BucketName: 62 | default: Quick Start S3 bucket name 63 | QSS3KeyPrefix: 64 | default: Quick Start S3 key prefix 65 | QSS3BucketRegion: 66 | default: Quick Start S3 bucket Region 67 | RDGWInstanceType: 68 | default: Remote Desktop Gateway instance type 69 | RDGWCIDR: 70 | default: Allowed Remote Desktop Gateway external access CIDR 71 | SetupAppInsightsMonitoring: 72 | default: Setup Application Insights Monitoring 73 | VPCCIDR: 74 | default: VPC CIDR 75 | VPCTenancy: 76 | default: VPC tenancy 77 | Parameters: 78 | AdminPassword: 79 | Description: Password for the administrative account. Must be at least 8 characters 80 | containing letters, numbers and symbols. 81 | Type: String 82 | MinLength: '8' 83 | MaxLength: '32' 84 | AllowedPattern: '(?=^.{8,32}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*' 85 | NoEcho: 'true' 86 | AdminUser: 87 | Description: User name for the new local administrator account. 88 | Type: String 89 | Default: StackAdmin 90 | MinLength: '5' 91 | MaxLength: '25' 92 | AllowedPattern: '^[a-zA-Z0-9]+$' 93 | AvailabilityZones: 94 | Description: 'List of Availability Zones (AZs) to use for the subnets in the VPC. Note: 95 | The logical order is preserved and only 2 AZs are used for this deployment.' 96 | Type: List 97 | KeyPairName: 98 | Description: Public/private key pairs allow you to securely connect to your instance 99 | after it launches. 100 | Type: AWS::EC2::KeyPair::KeyName 101 | NumberOfRDGWHosts: 102 | AllowedValues: 103 | - '1' 104 | - '2' 105 | - '3' 106 | - '4' 107 | Default: '1' 108 | Description: Enter the number of Remote Desktop Gateway hosts to create. 109 | Type: String 110 | PrivateSubnet1CIDR: 111 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$' 112 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28. 113 | Default: 10.0.0.0/19 114 | Description: CIDR block for private subnet 1 located in Availability Zone 1. 115 | Type: String 116 | PrivateSubnet2CIDR: 117 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$' 118 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28. 119 | Default: 10.0.32.0/19 120 | Description: CIDR block for private subnet 2 located in Availability Zone 2. 121 | Type: String 122 | PublicSubnet1CIDR: 123 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$' 124 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28. 125 | Default: 10.0.128.0/20 126 | Description: CIDR Block for the public DMZ subnet 1 located in Availability Zone 1. 127 | Type: String 128 | PublicSubnet2CIDR: 129 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$' 130 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28. 131 | Default: 10.0.144.0/20 132 | Description: CIDR Block for the public DMZ subnet 2 located in Availability Zone 2. 133 | Type: String 134 | QSS3BucketName: 135 | AllowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$' 136 | ConstraintDescription: The Quick Start bucket name can include numbers, lowercase 137 | letters, uppercase letters, and hyphens (-). It cannot start or end with a 138 | hyphen (-). 139 | Default: aws-quickstart 140 | Description: Name of the S3 bucket for your copy of the Quick Start assets. 141 | Keep the default name unless you are customizing the template. 142 | Changing the name updates code references to point to a new Quick 143 | Start location. This name can include numbers, lowercase letters, 144 | uppercase letters, and hyphens, but do not start or end with a hyphen (-). 145 | See https://aws-quickstart.github.io/option1.html. 146 | MinLength: '3' 147 | MaxLength: '63' 148 | Type: String 149 | QSS3KeyPrefix: 150 | AllowedPattern: '^[a-zA-Z0-9\-\/]+$' 151 | ConstraintDescription: The Quick Start S3 key prefix can include numbers, lowercase letters, 152 | uppercase letters, hyphens (-), and forward slash (/). 153 | Default: quickstart-microsoft-rdgateway/ 154 | Description: S3 key prefix that is used to simulate a directory for your copy of the 155 | Quick Start assets. Keep the default prefix unless you are customizing 156 | the template. Changing this prefix updates code references to point to 157 | a new Quick Start location. This prefix can include numbers, lowercase 158 | letters, uppercase letters, hyphens (-), and forward slashes (/). 159 | See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html 160 | and https://aws-quickstart.github.io/option1.html. 161 | Type: String 162 | QSS3BucketRegion: 163 | AllowedPattern: '^[a-z]+\-[a-z\-]+\-[0-9]{1}$' 164 | Default: 'us-east-1' 165 | Description: "Region of staging bucket (BucketName)." 166 | Type: String 167 | RDGWInstanceType: 168 | Description: Amazon EC2 instance type for the Remote Desktop Gateway instances. 169 | Type: String 170 | Default: t3.2xlarge 171 | AllowedValues: 172 | - t2.large 173 | - t3.micro 174 | - t3.small 175 | - t3.medium 176 | - t3.large 177 | - t3.xlarge 178 | - t3.2xlarge 179 | - t3a.micro 180 | - t3a.small 181 | - t3a.medium 182 | - t3a.large 183 | - t3a.xlarge 184 | - t3a.2xlarge 185 | - m5.large 186 | - m5.xlarge 187 | - m5.2xlarge 188 | - m5a.large 189 | - m5a.xlarge 190 | - m5a.2xlarge 191 | RDGWCIDR: 192 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' 193 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x. 194 | Description: Allowed CIDR block for external access to the Remote Desktop Gateways. 195 | Type: String 196 | SetupAppInsightsMonitoring: 197 | AllowedValues: 198 | - 'true' 199 | - 'false' 200 | Default: 'false' 201 | Description: Setup Application Insights monitoring for RDGW resources. 202 | Type: String 203 | VPCCIDR: 204 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$' 205 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28. 206 | Default: 10.0.0.0/16 207 | Description: CIDR block for the VPC. 208 | Type: String 209 | VPCTenancy: 210 | AllowedValues: 211 | - default 212 | - dedicated 213 | Default: default 214 | Description: The allowed tenancy of instances launched into the VPC. 215 | Type: String 216 | Rules: 217 | CheckSupportedInstances: 218 | RuleCondition: !Not 219 | - !Not 220 | - !Contains 221 | - - m4.xlarge 222 | - m4.2xlarge 223 | - m4.4xlarge 224 | - m4.large 225 | - !Ref 'RDGWInstanceType' 226 | Assertions: 227 | - Assert: !Not 228 | - !Contains 229 | - - eu-west-3 230 | - !Ref 'AWS::Region' 231 | AssertDescription: M4 instances are not available in the Paris region. 232 | Conditions: 233 | AppInsightsEnabled: !Equals [!Ref SetupAppInsightsMonitoring, true] 234 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 235 | Resources: 236 | VPCStack: 237 | Type: AWS::CloudFormation::Stack 238 | Properties: 239 | TemplateURL: 240 | Fn::Sub: 241 | - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-aws-vpc/templates/aws-vpc.template.yaml' 242 | - S3Region: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Ref QSS3BucketRegion] 243 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 244 | Parameters: 245 | AvailabilityZones: !Join 246 | - ',' 247 | - !Ref 'AvailabilityZones' 248 | NumberOfAZs: '2' 249 | PrivateSubnet1ACIDR: !Ref 'PrivateSubnet1CIDR' 250 | PrivateSubnet2ACIDR: !Ref 'PrivateSubnet2CIDR' 251 | PublicSubnet1CIDR: !Ref 'PublicSubnet1CIDR' 252 | PublicSubnet2CIDR: !Ref 'PublicSubnet2CIDR' 253 | VPCCIDR: !Ref 'VPCCIDR' 254 | VPCTenancy: !Ref 'VPCTenancy' 255 | RDGWStack: 256 | Type: AWS::CloudFormation::Stack 257 | Properties: 258 | TemplateURL: 259 | Fn::Sub: 260 | - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/rdgw-standalone.template' 261 | - S3Region: !If [UsingDefaultBucket, !Sub '${AWS::Region}', !Ref QSS3BucketRegion] 262 | S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 263 | Parameters: 264 | AdminPassword: !Ref 'AdminPassword' 265 | AdminUser: !Ref 'AdminUser' 266 | AppInsightsApplicationName: !If [AppInsightsEnabled, !Sub "ApplicationInsights-${AWS::StackName}", !Ref AWS::NoValue] 267 | KeyPairName: !Ref 'KeyPairName' 268 | NumberOfRDGWHosts: !Ref 'NumberOfRDGWHosts' 269 | PublicSubnet1ID: !GetAtt 'VPCStack.Outputs.PublicSubnet1ID' 270 | PublicSubnet2ID: !GetAtt 'VPCStack.Outputs.PublicSubnet2ID' 271 | QSS3BucketName: !Ref 'QSS3BucketName' 272 | QSS3KeyPrefix: !Ref 'QSS3KeyPrefix' 273 | QSS3BucketRegion: !Ref QSS3BucketRegion 274 | RDGWInstanceType: !Ref 'RDGWInstanceType' 275 | RDGWCIDR: !Ref 'RDGWCIDR' 276 | SetupAppInsightsMonitoring: !Ref SetupAppInsightsMonitoring 277 | VPCID: !GetAtt 'VPCStack.Outputs.VPCID' 278 | -------------------------------------------------------------------------------- /templates/rdgw-standalone.template: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: >- 3 | This template is intended to be installed into an existing VPC with two public subnets. 4 | It will create an auto-scaling group of RD Gateway instances in the public VPC subnets. 5 | **WARNING** This template creates Amazon EC2 Windows instance and related resources. 6 | You will be billed for the AWS resources used if you create a stack from this template. (qs-1qup6rag3) 7 | Metadata: 8 | QuickStartDocumentation: 9 | EntrypointName: "Parameters for launching into an existing VPC (standalone)" 10 | AWS::CloudFormation::Interface: 11 | ParameterGroups: 12 | - Label: 13 | default: Network configuration 14 | Parameters: 15 | - VPCID 16 | - PublicSubnet1ID 17 | - PublicSubnet2ID 18 | - RDGWCIDR 19 | - Label: 20 | default: Amazon EC2 configuration 21 | Parameters: 22 | - KeyPairName 23 | - RDGWInstanceType 24 | - LatestAmiId 25 | - Label: 26 | default: Microsoft Remote Desktop Gateway configuration 27 | Parameters: 28 | - NumberOfRDGWHosts 29 | - AdminUser 30 | - AdminPassword 31 | - DomainDNSName 32 | - SetupAppInsightsMonitoring 33 | - AppInsightsApplicationName 34 | - Label: 35 | default: AWS Quick Start configuration 36 | Parameters: 37 | - QSS3BucketName 38 | - QSS3KeyPrefix 39 | - QSS3BucketRegion 40 | ParameterLabels: 41 | AdminPassword: 42 | default: Admin password 43 | AdminUser: 44 | default: Admin user name 45 | AppInsightsApplicationName: 46 | default: Registered Application Name 47 | DomainDNSName: 48 | default: Domain DNS name 49 | KeyPairName: 50 | default: Key pair name 51 | LatestAmiId: 52 | default: SSM parameter to grab latest AMI ID 53 | NumberOfRDGWHosts: 54 | default: Number of RDGW hosts 55 | PublicSubnet1ID: 56 | default: Public subnet 1 ID 57 | PublicSubnet2ID: 58 | default: Public subnet 2 ID 59 | QSS3BucketName: 60 | default: Quick Start S3 bucket name 61 | QSS3KeyPrefix: 62 | default: Quick Start S3 key prefix 63 | QSS3BucketRegion: 64 | default: Quick Start S3 bucket Region 65 | RDGWInstanceType: 66 | default: Remote Desktop Gateway instance type 67 | RDGWCIDR: 68 | default: Allowed Remote Desktop Gateway external access CIDR 69 | SetupAppInsightsMonitoring: 70 | default: Setup Application Insights Monitoring 71 | VPCID: 72 | default: VPC ID 73 | Parameters: 74 | AdminPassword: 75 | Description: Password for the administrative account. Must be at least 8 characters 76 | containing letters, numbers, and symbols. 77 | Type: String 78 | MinLength: '8' 79 | MaxLength: '32' 80 | AllowedPattern: '(?=^.{8,32}$)((?=.*\d)(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[^A-Za-z0-9])(?=.*[a-z])|(?=.*[^A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])(?=.*[^A-Za-z0-9]))^.*' 81 | NoEcho: 'true' 82 | AdminUser: 83 | Description: User name for the new local administrator account. 84 | Type: String 85 | Default: StackAdmin 86 | MinLength: '5' 87 | MaxLength: '25' 88 | AllowedPattern: '^[a-zA-Z0-9]+$' 89 | AppInsightsApplicationName: 90 | Description: Application name to be used with AppInsights monitoring. 91 | Type: String 92 | Default: '' 93 | MaxLength: '300' 94 | AllowedPattern: '^[a-zA-Z0-9\-]*$' 95 | DomainDNSName: 96 | Description: Fully qualified domain name (FQDN), such as example.com. 97 | Type: String 98 | Default: example.com 99 | MinLength: '2' 100 | MaxLength: '255' 101 | AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\-\.]*[a-zA-Z0-9]+$' 102 | KeyPairName: 103 | Description: Public/private key pairs allow you to securely connect to your instance 104 | after it launches. 105 | Type: AWS::EC2::KeyPair::KeyName 106 | LatestAmiId: 107 | Type: AWS::SSM::Parameter::Value 108 | Default: /aws/service/ami-windows-latest/Windows_Server-2016-English-Full-Base 109 | NumberOfRDGWHosts: 110 | AllowedValues: 111 | - '1' 112 | - '2' 113 | - '3' 114 | - '4' 115 | Default: '1' 116 | Description: Enter the number of Remote Desktop Gateway hosts to create. 117 | Type: String 118 | PublicSubnet1ID: 119 | Description: ID of the public subnet 1 that you want to provision the first Remote 120 | Desktop Gateway into (for example, subnet-a0246dcd). 121 | Type: AWS::EC2::Subnet::Id 122 | PublicSubnet2ID: 123 | Description: ID of the public subnet 2 you want to provision the second Remote 124 | Desktop Gateway into (for example, subnet-e3246d8e). 125 | Type: AWS::EC2::Subnet::Id 126 | QSS3BucketName: 127 | AllowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$' 128 | ConstraintDescription: The Quick Start bucket name can include numbers, lowercase 129 | letters, uppercase letters, and hyphens (-). It cannot start or end with a 130 | hyphen (-). 131 | Default: aws-quickstart 132 | Description: Name of the S3 bucket for your copy of the Quick Start assets. 133 | Keep the default name unless you are customizing the template. 134 | Changing the name updates code references to point to a new Quick 135 | Start location. This name can include numbers, lowercase letters, 136 | uppercase letters, and hyphens, but do not start or end with a hyphen (-). 137 | See https://aws-quickstart.github.io/option1.html. 138 | MinLength: '3' 139 | MaxLength: '63' 140 | Type: String 141 | QSS3KeyPrefix: 142 | AllowedPattern: '^[a-zA-Z0-9\-\/]+$' 143 | ConstraintDescription: The Quick Start S3 key prefix can include numbers, lowercase letters, 144 | uppercase letters, hyphens (-), and forward slash (/). 145 | Default: quickstart-microsoft-rdgateway/ 146 | Description: S3 key prefix that is used to simulate a directory for your copy of the 147 | Quick Start assets. Keep the default prefix unless you are customizing 148 | the template. Changing this prefix updates code references to point to 149 | a new Quick Start location. This prefix can include numbers, lowercase 150 | letters, uppercase letters, hyphens (-), and forward slashes (/). 151 | See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html 152 | and https://aws-quickstart.github.io/option1.html. 153 | Type: String 154 | QSS3BucketRegion: 155 | AllowedPattern: '^[a-z]+\-[a-z\-]+\-[0-9]{1}$' 156 | Default: 'us-east-1' 157 | Description: 'AWS Region where the Quick Start S3 bucket (QSS3BucketName) is 158 | hosted. Keep the default Region unless you are customizing the template. 159 | Changing this Region updates code references to point to a new Quick Start location. 160 | When using your own bucket, specify the Region. 161 | See https://aws-quickstart.github.io/option1.html.' 162 | Type: String 163 | RDGWInstanceType: 164 | Description: Amazon EC2 instance type for the Remote Desktop Gateway instances. 165 | Type: String 166 | Default: t3.2xlarge 167 | AllowedValues: 168 | - t2.large 169 | - t3.micro 170 | - t3.small 171 | - t3.medium 172 | - t3.large 173 | - t3.xlarge 174 | - t3.2xlarge 175 | - t3a.micro 176 | - t3a.small 177 | - t3a.medium 178 | - t3a.large 179 | - t3a.xlarge 180 | - t3a.2xlarge 181 | - m5.large 182 | - m5.xlarge 183 | - m5.2xlarge 184 | - m5a.large 185 | - m5a.xlarge 186 | - m5a.2xlarge 187 | RDGWCIDR: 188 | AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$' 189 | Description: Allowed CIDR Block for external access to the Remote Desktop Gateways. 190 | Type: String 191 | SetupAppInsightsMonitoring: 192 | AllowedValues: 193 | - 'true' 194 | - 'false' 195 | Default: 'false' 196 | Description: Setup Application Insights monitoring for RDGW resources. 197 | Type: String 198 | VPCID: 199 | Description: ID of the VPC (for example, vpc-0343606e). 200 | Type: AWS::EC2::VPC::Id 201 | Rules: 202 | SubnetsInVPC: 203 | Assertions: 204 | - Assert: !EachMemberIn 205 | - !ValueOfAll 206 | - AWS::EC2::Subnet::Id 207 | - VpcId 208 | - !RefAll 'AWS::EC2::VPC::Id' 209 | AssertDescription: All subnets must be in the VPC. 210 | CheckSupportedInstances: 211 | RuleCondition: !Contains 212 | - - m4.large 213 | - m4.xlarge 214 | - m4.2xlarge 215 | - m4.4xlarge 216 | - !Ref 'RDGWInstanceType' 217 | Assertions: 218 | - Assert: !Not 219 | - !Contains 220 | - - eu-west-3 221 | - !Ref 'AWS::Region' 222 | AssertDescription: M4 instances are not available in the Paris region. 223 | Conditions: 224 | UsingDefaultAppName: !Equals [!Ref AppInsightsApplicationName, ''] 225 | UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] 226 | appInsightsMonitoring: !Equals [!Ref SetupAppInsightsMonitoring, 'true'] 227 | Resources: 228 | RDGWLocalUserSecrets: 229 | Type: AWS::SecretsManager::Secret 230 | Properties: 231 | Name: !Sub 'RDGWLocalUserSecrets-${AWS::StackName}' 232 | Description: Secrets to join AD domain 233 | SecretString: !Sub '{"username":"${AdminUser}","password":"${AdminPassword}"}' 234 | SetupConfigurationDoc: 235 | Type: AWS::SSM::Document 236 | Properties: 237 | DocumentType: Automation 238 | Content: 239 | schemaVersion: "0.3" 240 | description: "Configure instances on launch." 241 | assumeRole: "{{AutomationAssumeRole}}" 242 | parameters: 243 | InstanceId: 244 | allowedPattern: '^i-[a-f0-9]{8}(?:[a-f0-9]{9})?$' 245 | description: "ID of the instance." 246 | type: "String" 247 | ASGName: 248 | allowedPattern: '^[a-zA-Z0-9\_\W\s]+$' 249 | description: "Auto Scaling group name." 250 | type: "String" 251 | maxChars: '255' 252 | minChars: '2' 253 | LCHName: 254 | allowedPattern: '^[A-Za-z0-9\-\_\/]+$' 255 | description: "Life cycle hook name." 256 | type: "String" 257 | maxChars: '255' 258 | minChars: '2' 259 | DomainDNSName: 260 | allowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9\-\.]*[a-zA-Z0-9]+$' 261 | default: "example.com" 262 | description: "Fully qualified domain name (FQDN) of the forest root domain (for example, example.com)." 263 | type: "String" 264 | maxChars: '255' 265 | minChars: '2' 266 | AdminSecParam: 267 | allowedPattern: '^arn:[^:]+:secretsmanager:[^:]+:\d{12}:secret:[\w\/+=.@-]{1,512}$' 268 | description: "AWS Secrets parameter name that has password and user name for the domain administrator." 269 | type: "String" 270 | maxChars: '2048' 271 | minChars: '20' 272 | QSS3BucketName: 273 | allowedPattern: '^[a-z0-9]+[a-z0-9\.\-]*[a-z0-9]+$' 274 | default: "aws-quickstart" 275 | description: "Name of the S3 bucket for your copy of the Quick Start assets. 276 | Keep the default name unless you are customizing the template. 277 | Changing the name updates code references to point to a new Quick 278 | Start location. This name can include numbers, lowercase letters, 279 | uppercase letters, and hyphens, but do not start or end with a hyphen (-). 280 | See https://aws-quickstart.github.io/option1.html." 281 | minChars: '3' 282 | maxChars: '63' 283 | type: "String" 284 | QSS3KeyPrefix: 285 | allowedPattern: '^[a-zA-Z0-9\-\/]+$' 286 | default: "quickstart-microsoft-rdgateway/" 287 | description: "S3 key prefix that is used to simulate a directory for your copy of the 288 | Quick Start assets. Keep the default prefix unless you are customizing 289 | the template. Changing this prefix updates code references to point to 290 | a new Quick Start location. This prefix can include numbers, lowercase 291 | letters, uppercase letters, hyphens (-), and forward slashes (/). 292 | See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html 293 | and https://aws-quickstart.github.io/option1.html." 294 | type: "String" 295 | URLSuffix: 296 | allowedPattern: '^[a-zA-Z0-9\-\.]+$' 297 | default: 'amazonaws.com' 298 | description: 'AWS URL suffix' 299 | type: 'String' 300 | AutomationAssumeRole: 301 | allowedPattern: '^(arn:[^:]+:iam::\d{12}:role\/[\w\/\+\=\.\@\-]{1,512})?$' 302 | default: "" 303 | description: "(Optional) The ARN of the role that allows automation to perform actions on your behalf." 304 | type: "String" 305 | mainSteps: 306 | - name: waitUntilInstanceStateRunning 307 | action: aws:waitForAwsResourceProperty 308 | timeoutSeconds: 600 309 | inputs: 310 | Service: ec2 311 | Api: DescribeInstanceStatus 312 | InstanceIds: 313 | - "{{InstanceId}}" 314 | PropertySelector: "$.InstanceStatuses[0].InstanceState.Name" 315 | DesiredValues: 316 | - running 317 | - name: assertInstanceStateRunning 318 | action: aws:assertAwsResourceProperty 319 | inputs: 320 | Service: ec2 321 | Api: DescribeInstanceStatus 322 | InstanceIds: 323 | - "{{InstanceId}}" 324 | PropertySelector: "$.InstanceStatuses[0].InstanceState.Name" 325 | DesiredValues: 326 | - running 327 | - name: "installRDP" 328 | action: aws:runCommand 329 | onFailure: "step:abandonHookAction" 330 | inputs: 331 | DocumentName: AWS-RunPowerShellScript 332 | InstanceIds: 333 | - "{{InstanceId}}" 334 | CloudWatchOutputConfig: 335 | CloudWatchOutputEnabled: "true" 336 | CloudWatchLogGroupName: "/QuickStart/rdgateway/SetupConfiguration" 337 | Parameters: 338 | commands: 339 | - | 340 | Write-Output 'Installing RD Gateway components' 341 | Try { 342 | Install-WindowsFeature RDS-Gateway,RSAT-RDS-Gateway 343 | } Catch [System.Exception] { 344 | Write-Output "Failed to install RD Gateway components $_" 345 | Exit 1 346 | } 347 | - name: "createLocalUser" 348 | action: "aws:runCommand" 349 | onFailure: "step:abandonHookAction" 350 | inputs: 351 | DocumentName: "AWS-RunRemoteScript" 352 | InstanceIds: 353 | - "{{InstanceId}}" 354 | CloudWatchOutputConfig: 355 | CloudWatchOutputEnabled: "true" 356 | CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' 357 | Parameters: 358 | sourceType: "S3" 359 | sourceInfo: 360 | !Sub 361 | - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Create-LocalUser.ps1"}' 362 | - S3Bucket: !If 363 | - UsingDefaultBucket 364 | - !Sub '${QSS3BucketName}-${AWS::Region}' 365 | - !Ref QSS3BucketName 366 | S3Region: !If 367 | - UsingDefaultBucket 368 | - !Ref AWS::Region 369 | - !Ref QSS3BucketRegion 370 | commandLine: "./Create-LocalUser.ps1 -AdminSecParam {{AdminSecParam}}" 371 | - name: "configurerdgw" 372 | action: "aws:runCommand" 373 | onFailure: "step:abandonHookAction" 374 | inputs: 375 | DocumentName: "AWS-RunRemoteScript" 376 | InstanceIds: 377 | - "{{InstanceId}}" 378 | CloudWatchOutputConfig: 379 | CloudWatchOutputEnabled: "true" 380 | CloudWatchLogGroupName: !Sub '/aws/Quick_Start/${AWS::StackName}' 381 | Parameters: 382 | sourceType: "S3" 383 | sourceInfo: 384 | !Sub 385 | - '{"path": "https://${S3Bucket}.s3.${S3Region}.{{URLSuffix}}/{{QSS3KeyPrefix}}scripts/Initialize-RDGW.ps1"}' 386 | - S3Bucket: !If 387 | - UsingDefaultBucket 388 | - !Sub '${QSS3BucketName}-${AWS::Region}' 389 | - !Ref QSS3BucketName 390 | S3Region: !If 391 | - UsingDefaultBucket 392 | - !Ref AWS::Region 393 | - !Ref QSS3BucketRegion 394 | commandLine: "./Initialize-RDGW.ps1 -DomainDNSName {{DomainDNSName}} -DomainNetBiosName BUILTIN -GroupName administrators,RDGWUsers" 395 | - name: "completeHookAction" 396 | action: aws:executeAwsApi 397 | isEnd: true 398 | inputs: 399 | Service: autoscaling 400 | Api: CompleteLifecycleAction 401 | AutoScalingGroupName: "{{ASGName}}" 402 | InstanceId: "{{InstanceId}}" 403 | LifecycleActionResult: CONTINUE 404 | LifecycleHookName: "{{LCHName}}" 405 | - name: "abandonHookAction" 406 | action: aws:executeAwsApi 407 | isEnd: true 408 | inputs: 409 | Service: autoscaling 410 | Api: CompleteLifecycleAction 411 | AutoScalingGroupName: "{{ASGName}}" 412 | InstanceId: "{{InstanceId}}" 413 | LifecycleActionResult: ABANDON 414 | LifecycleHookName: "{{LCHName}}" 415 | RDGWHostRole: 416 | Type: AWS::IAM::Role 417 | Properties: 418 | ManagedPolicyArns: 419 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore' 420 | - !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy' 421 | Policies: 422 | - PolicyDocument: 423 | Version: '2012-10-17' 424 | Statement: 425 | - Action: 426 | - s3:GetObject 427 | Resource: !Sub 428 | - arn:${AWS::Partition}:s3:::${S3Bucket}/${QSS3KeyPrefix}* 429 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 430 | Effect: Allow 431 | - Action: 432 | - s3:ListBucket 433 | Resource: !Sub 434 | - arn:${AWS::Partition}:s3:::${S3Bucket} 435 | - S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName] 436 | Effect: Allow 437 | PolicyName: aws-quick-start-s3-policy 438 | - PolicyDocument: 439 | Version: '2012-10-17' 440 | Statement: 441 | - Action: 442 | - s3:GetObject 443 | Resource: 444 | - !Sub 'arn:${AWS::Partition}:s3:::aws-ssm-${AWS::Region}/*' 445 | - !Sub 'arn:${AWS::Partition}:s3:::aws-windows-downloads-${AWS::Region}/*' 446 | - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-${AWS::Region}/*' 447 | - !Sub 'arn:${AWS::Partition}:s3:::amazon-ssm-packages-${AWS::Region}/*' 448 | - !Sub 'arn:${AWS::Partition}:s3:::${AWS::Region}-birdwatcher-prod/*' 449 | - !Sub 'arn:${AWS::Partition}:s3:::patch-baseline-snapshot-${AWS::Region}/*' 450 | Effect: Allow 451 | PolicyName: ssm-custom-s3-policy 452 | - PolicyDocument: 453 | Version: '2012-10-17' 454 | Statement: 455 | - Effect: Allow 456 | Action: 457 | - secretsmanager:GetSecretValue 458 | - secretsmanager:DescribeSecret 459 | Resource: 460 | - !Ref 'RDGWLocalUserSecrets' 461 | PolicyName: ssm-secrets-policy 462 | Path: / 463 | AssumeRolePolicyDocument: 464 | Statement: 465 | - Action: 466 | - sts:AssumeRole 467 | Principal: 468 | Service: 469 | - ec2.amazonaws.com 470 | Effect: Allow 471 | Version: '2012-10-17' 472 | RDGWHostProfile: 473 | Type: AWS::IAM::InstanceProfile 474 | Properties: 475 | Roles: 476 | - !Ref 'RDGWHostRole' 477 | Path: / 478 | EventBridgeSSMAutoRole: 479 | Type: AWS::IAM::Role 480 | Properties: 481 | Policies: 482 | - PolicyDocument: 483 | Version: '2012-10-17' 484 | Statement: 485 | - Effect: Allow 486 | Action: 487 | - ssm:StartAutomationExecution 488 | Resource: 489 | - !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${SetupConfigurationDoc}:$DEFAULT' 490 | - Effect: Allow 491 | Action: 492 | - iam:PassRole 493 | Resource: 494 | - !GetAtt ExecutionResourceRole.Arn 495 | Condition: {"StringLikeIfExists": {"iam:PassedToService": "ssm.amazonaws.com"}} 496 | PolicyName: "EventBridge_Invoke_SSM_Automation" 497 | Path: /service-role/ 498 | AssumeRolePolicyDocument: 499 | Version: '2012-10-17' 500 | Statement: 501 | - Effect: Allow 502 | Principal: 503 | Service: 504 | - events.amazonaws.com 505 | Action: sts:AssumeRole 506 | ExecutionResourceRole: 507 | Type: AWS::IAM::Role 508 | Properties: 509 | Policies: 510 | - PolicyDocument: 511 | Version: '2012-10-17' 512 | Statement: 513 | - Effect: Allow 514 | Action: 515 | - autoscaling:CompleteLifecycleAction 516 | Resource: !Sub arn:${AWS::Partition}:autoscaling:*:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName} 517 | PolicyName: asg-lch-complete 518 | AssumeRolePolicyDocument: 519 | Version: '2012-10-17' 520 | Statement: 521 | - Effect: Allow 522 | Principal: 523 | Service: 524 | - ssm.amazonaws.com 525 | Action: 526 | - sts:AssumeRole 527 | Path: / 528 | ManagedPolicyArns: 529 | - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonSSMAutomationRole 530 | Description: New IAM Role to allow SSM access. 531 | ALBResource: 532 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 533 | Properties: 534 | Type: network 535 | Subnets: 536 | - !Ref 'PublicSubnet1ID' 537 | - !Ref 'PublicSubnet2ID' 538 | ALBTargetGroup3389: 539 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 540 | Properties: 541 | Port: 3389 542 | Protocol: TCP 543 | VpcId: !Ref 'VPCID' 544 | TargetGroupAttributes: 545 | - Key: deregistration_delay.timeout_seconds 546 | Value: '60' 547 | - Key: stickiness.enabled 548 | Value: 'true' 549 | ALBTargetGroup443: 550 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 551 | Properties: 552 | Port: 443 553 | Protocol: TCP 554 | VpcId: !Ref 'VPCID' 555 | TargetGroupAttributes: 556 | - Key: deregistration_delay.timeout_seconds 557 | Value: '60' 558 | - Key: stickiness.enabled 559 | Value: 'true' 560 | ALBTargetGroup3391: 561 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 562 | Properties: 563 | Port: 3391 564 | Protocol: UDP 565 | VpcId: !Ref 'VPCID' 566 | TargetGroupAttributes: 567 | - Key: deregistration_delay.timeout_seconds 568 | Value: '60' 569 | - Key: stickiness.enabled 570 | Value: 'true' 571 | ALBListener3389: 572 | Type: AWS::ElasticLoadBalancingV2::Listener 573 | Properties: 574 | LoadBalancerArn: !Ref 'ALBResource' 575 | Port: 3389 576 | Protocol: TCP 577 | DefaultActions: 578 | - Type: forward 579 | TargetGroupArn: !Ref 'ALBTargetGroup3389' 580 | ALBListener443: 581 | Type: AWS::ElasticLoadBalancingV2::Listener 582 | Properties: 583 | LoadBalancerArn: !Ref 'ALBResource' 584 | Port: 443 585 | Protocol: TCP 586 | DefaultActions: 587 | - Type: forward 588 | TargetGroupArn: !Ref 'ALBTargetGroup443' 589 | ALBListener3391: 590 | Type: AWS::ElasticLoadBalancingV2::Listener 591 | Properties: 592 | LoadBalancerArn: !Ref 'ALBResource' 593 | Port: 3391 594 | Protocol: UDP 595 | DefaultActions: 596 | - Type: forward 597 | TargetGroupArn: !Ref 'ALBTargetGroup3391' 598 | RDGWLaunchTemplate: 599 | Type: AWS::EC2::LaunchTemplate 600 | DeletionPolicy: Delete 601 | Properties: 602 | LaunchTemplateData: 603 | MetadataOptions: 604 | HttpEndpoint: enabled 605 | HttpTokens: required 606 | InstanceType: !Ref 'RDGWInstanceType' 607 | ImageId: !Ref 'LatestAmiId' 608 | SecurityGroupIds: 609 | - !Ref RemoteDesktopGatewaySG 610 | IamInstanceProfile: 611 | Name: !Ref 'RDGWHostProfile' 612 | BlockDeviceMappings: 613 | - DeviceName: /dev/sda1 614 | Ebs: 615 | VolumeSize: 50 616 | VolumeType: gp2 617 | KeyName: !Ref 'KeyPairName' 618 | TagSpecifications: 619 | - 620 | ResourceType: 'instance' 621 | Tags: 622 | - Key: "Name" 623 | Value: "RDGW" 624 | RDGWAutoScalingGroup: 625 | DependsOn: ScaleUpEventBridgeResource 626 | Type: AWS::AutoScaling::AutoScalingGroup 627 | Properties: 628 | AutoScalingGroupName: !Sub "${AWS::StackName}" 629 | LaunchTemplate: 630 | LaunchTemplateId: !Ref 'RDGWLaunchTemplate' 631 | Version: !GetAtt 'RDGWLaunchTemplate.LatestVersionNumber' 632 | VPCZoneIdentifier: 633 | - !Ref 'PublicSubnet1ID' 634 | - !Ref 'PublicSubnet2ID' 635 | MinSize: !Ref 'NumberOfRDGWHosts' 636 | MaxSize: !Ref 'NumberOfRDGWHosts' 637 | Cooldown: '300' 638 | DesiredCapacity: !Ref 'NumberOfRDGWHosts' 639 | TargetGroupARNs: 640 | - !Ref 'ALBTargetGroup3389' 641 | - !Ref 'ALBTargetGroup443' 642 | - !Ref 'ALBTargetGroup3391' 643 | LifecycleHookSpecificationList: 644 | - LifecycleTransition: autoscaling:EC2_INSTANCE_LAUNCHING 645 | LifecycleHookName: DomainJoinHook 646 | DefaultResult: ABANDON 647 | HeartbeatTimeout: 1200 648 | RemoteDesktopGatewaySG: 649 | Type: AWS::EC2::SecurityGroup 650 | Properties: 651 | GroupDescription: Enable RDP access from the internet. 652 | VpcId: !Ref 'VPCID' 653 | SecurityGroupIngress: 654 | - IpProtocol: tcp 655 | FromPort: 3389 656 | ToPort: 3389 657 | CidrIp: !Ref 'RDGWCIDR' 658 | - IpProtocol: tcp 659 | FromPort: 3389 660 | ToPort: 3389 661 | CidrIp: !Ref 'RDGWCIDR' 662 | - IpProtocol: tcp 663 | FromPort: 443 664 | ToPort: 443 665 | CidrIp: !Ref 'RDGWCIDR' 666 | - IpProtocol: udp 667 | FromPort: 3391 668 | ToPort: 3391 669 | CidrIp: !Ref 'RDGWCIDR' 670 | - IpProtocol: icmp 671 | FromPort: -1 672 | ToPort: -1 673 | CidrIp: !Ref 'RDGWCIDR' 674 | ScaleUpEventBridgeResource: 675 | Type: AWS::Events::Rule 676 | Properties: 677 | State: ENABLED 678 | Description: Run configuration document that joins domain and configures RDGW instances. 679 | EventPattern: 680 | source: 681 | - aws.autoscaling 682 | detail-type: 683 | - EC2 Instance-launch Lifecycle Action 684 | detail: 685 | AutoScalingGroupName: 686 | - !Sub "${AWS::StackName}" 687 | Targets: 688 | - Arn: !Sub 'arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${SetupConfigurationDoc}:$DEFAULT' 689 | Id: Windows-Scale-Out 690 | RoleArn: !GetAtt EventBridgeSSMAutoRole.Arn 691 | InputTransformer: 692 | InputPathsMap: 693 | InstanceId: $.detail.EC2InstanceId 694 | ASGName: $.detail.AutoScalingGroupName 695 | LCHName: $.detail.LifecycleHookName 696 | InputTemplate: !Sub '{"AutomationAssumeRole":["${ExecutionResourceRole.Arn}"],"InstanceId":[],"ASGName":[],"LCHName":[],"DomainDNSName":["${DomainDNSName}"],"AdminSecParam":["${RDGWLocalUserSecrets}"],"QSS3BucketName":["${QSS3BucketName}"],"QSS3KeyPrefix":["${QSS3KeyPrefix}"],"URLSuffix":["${AWS::URLSuffix}"]}' 697 | RDGWResourceGroup: 698 | Condition: appInsightsMonitoring 699 | Type: AWS::ResourceGroups::Group 700 | Properties: 701 | Name: !If [UsingDefaultAppName, !Sub "ApplicationInsights-${AWS::StackName}", !Ref AppInsightsApplicationName] 702 | ResourceQuery: 703 | Query: 704 | TagFilters: 705 | - Key: 'aws:cloudformation:stack-name' 706 | Values: 707 | - !Sub "${AWS::StackName}" 708 | Type: 'TAG_FILTERS_1_0' 709 | ApplicationInsightsRDGW: 710 | Condition: appInsightsMonitoring 711 | Type: AWS::ApplicationInsights::Application 712 | Properties: 713 | ResourceGroupName: !Ref 'RDGWResourceGroup' 714 | AutoConfigurationEnabled: true 715 | DependsOn: RDGWResourceGroup 716 | Outputs: 717 | RDPURL: 718 | Description: ELB DNS name to connect to RDP Gateway. 719 | Value: !GetAtt 'ALBResource.DNSName' 720 | RemoteDesktopGatewaySGID: 721 | Value: !Ref 'RemoteDesktopGatewaySG' 722 | Description: Remote Desktop Gateway Security Group ID --------------------------------------------------------------------------------