├── .gitignore ├── Build.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── NOTICE ├── README.md ├── assets └── architecture.png ├── deployment ├── 00-master.yaml ├── 01-newvpc.yaml ├── 02-securitygroups.yaml ├── 03-bastion.yaml ├── 03-efsfilesystem.yaml ├── 03-elasticache.yaml ├── 03-publicalb.yaml ├── 03-rds.yaml ├── 04-cloudfront.yaml ├── 04-web.yaml ├── build-s3-dist.sh ├── regional-s3-assets │ └── tmp └── run-unit-tests.sh ├── launch-stack.png └── launch-stack.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | **/dist 3 | **/global-s3-assets 4 | **/regional-s3-assets 5 | **/open-source 6 | **/.zip 7 | **/tmp 8 | **/out-tsc 9 | 10 | # dependencies 11 | **/node_modules 12 | 13 | # e2e 14 | **/e2e/*.js 15 | **/e2e/*.map 16 | 17 | # misc 18 | **/npm-debug.log 19 | **/testem.log 20 | **/package-lock.json 21 | **/.vscode/settings.json 22 | 23 | # System Files 24 | **/.DS_Store 25 | **/.vscode 26 | -------------------------------------------------------------------------------- /Build.md: -------------------------------------------------------------------------------- 1 | # Build Your Own Solution 2 | 3 | ## Running unit tests for customization 4 | * Clone the repository, then make the desired code changes 5 | * Next, run unit tests to make sure added customization passes the tests 6 | ``` 7 | cd ./deployment 8 | chmod +x ./run-unit-tests.sh \n 9 | ./run-unit-tests.sh \n 10 | ``` 11 | 12 | ## Building distributable for customization 13 | * Configure the bucket name of your target Amazon S3 distribution bucket 14 | ``` 15 | export DIST_OUTPUT_BUCKET=my-bucket-name # bucket where customized code will reside 16 | export SOLUTION_NAME=my-solution-name 17 | export VERSION=my-version # version number for the customized code 18 | ``` 19 | _Note:_ You would have to create an S3 bucket with the prefix 'my-bucket-name-'; aws_region is where you are testing the customized solution. Also, the assets in bucket should be publicly accessible. 20 | 21 | * Now build the distributable: 22 | ``` 23 | chmod +x ./build-s3-dist.sh \n 24 | ./build-s3-dist.sh $DIST_OUTPUT_BUCKET $SOLUTION_NAME $VERSION \n 25 | ``` 26 | 27 | * Deploy the distributable to an Amazon S3 bucket in your account. _Note:_ you must have the AWS Command Line Interface installed. 28 | ``` 29 | aws s3 cp ./dist/ s3://my-bucket-name-/$SOLUTION_NAME/$VERSION/ --recursive --acl bucket-owner-full-control --profile aws-cred-profile-name \n 30 | ``` 31 | 32 | * Get the link of the solution template uploaded to your Amazon S3 bucket. 33 | * Deploy the solution to your account by launching a new AWS CloudFormation stack using the link of the solution template in Amazon S3. 34 | 35 | *** 36 | 37 | ## File Structure 38 | 39 | ``` 40 | |-deployment/ 41 | |-build-s3-dist.sh [ shell script for packaging distribution assets ] 42 | |-run-unit-tests.sh [ shell script for executing unit tests ] 43 | |-solution.yaml [ solution CloudFormation deployment template ] 44 | |-source/ 45 | |-example-function-js [ Example microservice function in javascript ] 46 | |- lib/ [ Example function libraries ] 47 | |-example-function-py [ Example microservice function in python ] 48 | 49 | ``` 50 | 51 | Each microservice follows the structure of: 52 | 53 | ``` 54 | |-service-name/ 55 | |-lib/ 56 | |-[service module libraries and unit tests] 57 | |-index.js [injection point for microservice] 58 | |-package.json 59 | ``` 60 | 61 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 62 | 63 | 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 64 | 65 | http://www.apache.org/licenses/ 66 | 67 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. 68 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.0.0] - 2019-07-27 8 | ### Added 9 | - example-function-js sample microservice 10 | - added unit tests for example-function-js 11 | 12 | ### Changed 13 | - example.template to yaml file example with JS. 14 | - updated build-s3-dist.sh script to include soltion-name parameter 15 | - updated build-open-source.sh script to include soltion-name parameter 16 | - updated run-unit-tests.sh script to execute example-function-js unit tests 17 | 18 | ### Removed 19 | - deployment/buildspec files. 20 | - helper function 21 | 22 | ## [0.0.1] - 2019-04-15 23 | ### Added 24 | - CHANGELOG templated file 25 | - README templated file 26 | - NOTICE file 27 | - LICENSE file 28 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moodle on AWS 2 | 3 | Moodle是一个学习平台,旨在为教育工作者、管理人员和学习者提供一个强大、安全、完整的系统来创建个性化的学习环境。 4 | 5 | ## 架构 6 | 7 | ![Architect](assets/architecture.png) 8 | 9 | 10 | ## 部署说明 11 | 12 | 请认证阅读以下说明来了解架构。 13 | 14 | 1. 准备 ICP 备案过的域名。AWS 中国区 CloudFront 需要配置 ICP 备案的域名才能进行访问,如果您选择启用 CloudFront,请提前准备好 ICP 备案过的域名。 15 | 16 | 1. 确认弹性IP剩余额度。该 CloudFormation 堆栈会在您所选择的每一个 Availability Zone(AZ) 启动一个 NAT Gateway 来确保高可用架构。每一个 NAT Gateway 需要绑定一个弹性 IP. 默认情况下一个账号只能申请5个弹性IP。请在启动 CloudFormation 堆栈前确保您还有足够的弹性IP剩余额度。若弹性IP剩余额度不足,可通过工单来申请提升。 17 | 18 | 1. 上传 SSL 证书至 AWS Identity and Access Management(IAM)。在生产环境中,我们建议您同时为 CloudFront 和 ALB(Application Load Balancer) 配置 SSL 证书来开启 HTTPS. 若您计划启用 SSL, 在部署本方案前,请先完成上传 SSL 证书到 AWS IAM。您可以通过如下 AWS CLI 命令来查看 SSL 证书的 ID 及 ARN。 19 | ```bash 20 | aws iam list-server-certificates 21 | ``` 22 | 23 | 2. 启动 CloudFormation 堆栈时, **将最小和最大 Auto Scaling Group(ASG)的值都设置为1**。 如果您配置了会话缓存,则 Moodle 初始化安装中可能会遇到如下错误 24 | ``` 25 | Installation must be finished from the original IP address, sorry. 26 | ``` 27 | 28 | 3. 堆栈部署完成后,导航至网站以完成 Moodle 安装。 注意:在安装向导的最后一步(设置管理员密码之后),您可能会遇到504网关超时或CloudFront错误。 您只需刷新页面即可完成安装。 29 | 30 | 4. 在 Moodle 站点配置中配置 Application Cache。 31 | 32 | 5. 现在,您可以**更新**刚刚部署的堆栈, 根据需要设置 **最小和最大Auto Scaling Group** 的值。 33 | 34 | 6. 如果您希望 Application Cache 和 Session Cache 共享一个 Redis 集群以节省成本,您可以在 CloudFormation 堆栈中选择不启用 Application Cache。在 Moodle 站点配置中将 Application Cache 和 Session Cache 配置到同一个 Cache Store. 35 | 36 | ## 步骤1: 启动 CloudFormation 堆栈 37 | 38 | 此自动化 AWS CloudFormation 模板在 AWS Cloud 上部署 Moodle 应用程序。 39 | 40 | 您负责运行此解决方案时使用的AWS服务的成本。 有关更多详细信息,请参见“费用”部分。 有关完整详细信息,请参阅此解决方案中将使用的每个AWS服务的定价页面。 41 | 42 | 如果您希望 Moodle Application Cache 和 Session Cache 使用同一组 Redis Cluster, 您可以选择只部署 Session Cache, 然后在 Moodle 站点内通过控制台进行配置。 43 | 44 | 如果您选择部署了 Application Cache, 待 Moodle 站点部署完成后,登录 Moodle 控制台进行配置。 45 | 46 | 1. 登录到AWS管理控制台,然后单击下面的按钮以启动 AWS CloudFormation 模板。 47 | 48 | [![Launch Stack](launch-stack.png)](https://cn-northwest-1.console.amazonaws.cn/cloudformation/home?region=cn-northwest-1#/stacks/create/template?stackName=Moodle&templateURL=https:%2F%2Faws-solutions-reference.s3.cn-north-1.amazonaws.com.cn%2Fmoodle-on-aws%2Flatest%2F00-master.template) 49 | 50 | 1. 默认情况下,该模板在 AWS 宁夏区域启动。 要在其他AWS区域中启动该解决方案,请使用控制台导航栏中的区域选择器。 51 | 52 | 1. 在**创建堆栈**页面上,确认 **Amazon S3 URL** 文本框中显示正确的模板URL,然后选择**下一步**。 53 | 54 | 1. 在**指定堆栈详细信息**页面上,为解决方案堆栈分配名称。 55 | 56 | 1. 在**参数**下,查看模板的参数并根据需要进行修改。 此解决方案使用以下默认值。 57 | 58 | **General AWS** 59 | 60 | | 参数 | 默认值 | 描述 | 61 | | --------------- | --------- | ----------------------------------------------------- | 62 | | EC2 Key Pair | | EC2 Key Pair 名称,用于登录 Web 实例 | 63 | | SSH Access From | 0.0.0.0/0 | 允许登录 Bastion 的 IP 地址段 (CIDR格式) | 64 | | Site Domain | | Moodle 站点域名 | 65 | 66 | **Network** 67 | 68 | | 参数 | 默认值 | 描述 | 69 | | ---------------------------- | ------------- | ------------------------------------------------------------ | 70 | | Number of Availability Zones | 3 | 创建 VPC 时使用的 AZ 数量. 这个数量必须与 Availability Zones 中选择的数量一致 | 71 | | Availability Zones | | Subnet 使用的 AZ. | 72 | | VpcCidr | 10.0.0.0/16 | VPC CIDR | 73 | | VpcTenancy | default | | 74 | | Public Subnet 0 | 10.0.200.0/24 | Public Subnet 0 在 AZ0 中的 CIDR | 75 | | Public Subnet 1 | 10.0.201.0/24 | Public Subnet 1 在 AZ1 中的 CIDR | 76 | | Public Subnet 2 | 10.0.202.0/24 | Public Subnet 2 在 AZ2 中的 CIDR | 77 | | Web Subnet 0 | 10.0.0.0/24 | Web Subnet 0 在 AZ0 中的 CIDR | 78 | | Web Subnet 1 | 10.0.4.0/24 | Web Subnet 1 在 AZ1 中的 CIDR | 79 | | Web Subnet 2 | 10.0.8.0/24 | Web Subnet 2 在 AZ2 中的 CIDR | 80 | | Data Subnet 0 | 10.0.100.0/24 | Data Subnet 0 在 AZ0 中的 CIDR | 81 | | Data Subnet 1 | 10.0.101.0/24 | Data Subnet 1 在 AZ1 中的 CIDR | 82 | | Data Subnet 2 | 10.0.102.0/24 | Data Subnet 2 在 AZ2 中的 CIDR | 83 | 84 | **File System Tier** 85 | 86 | | 参数 | 默认值 | 描述 | 87 | | -------------------- | -------------- | ---------------------------------------- | 88 | | EFS Performance Mode | generalPurpose | 文件系统的性能模式 | 89 | | Encrpyted EFS? | false | 是否加密 EFS | 90 | | Add dummy data (GiB) | 0 | 在 EFS 中增加 dummy data | 91 | | Instance Type | r4.large | 在 EFS 中增加 dummy data 的 EC2 实例大小 | 92 | 93 | **Database Tier** 94 | 95 | | 参数 | 默认值 | 描述 | 96 | | ------------------ | ----------- | ------------------ | 97 | | DB Instance Class | db.r5.large | 数据库实例大小 | 98 | | DB Master Username | moodle | Master 用户名 | 99 | | DB Master Password | | Master 用户密码 | 100 | | DB Name | moodle | RDS 默认数据库名称 | 101 | 102 | **Caching Tier** 103 | 104 | | 参数 | 默认值 | 描述 | 105 | | --------------------------------------------- | -------------- | ---------------------------------------------------- | 106 | | Use Session Cache | true | Moodle 是否启用 Session Cache | 107 | | Session Cache Node Type | cache.r5.large | ElastiCache 实例大小 | 108 | | Use Application Cache | false | Moodle 是否启用 Application Cache | 109 | | Application Cache Node Type | cache.r5.large | ElastiCache 实例大小 | 110 | | Use CloudFront | false | 是否创建 CloudFront | 111 | | CloudFront Certificate ID uploaded in AWS IAM | | CloudFront 使用的 SSL 证书ID, 必须提前上传到 AWS IAM | 112 | 113 | **Web Tier** 114 | 115 | | 参数 | 默认值 | 描述 | 116 | | ---------------------- | -------- | --------------------------------------------- | 117 | | Public ALB Domain Name | | ALB 自定义域名 | 118 | | ALB Certificate ARN | | ALB 使用的 SSL 证书ID, 必须提前上传到 AWS IAM | 119 | | Web Tier Instance Type | c5.large | Web 实例大小 | 120 | | Web ASG Max | 1 | Web Auto Scaling Group 最大值 | 121 | | Web ASG Min | 1 | Web Auto Scaling Group 最小值 | 122 | 123 | **Moodle** 124 | 125 | | 参数 | 默认值 | 描述 | 126 | | ------------- | ------ | ------------------- | 127 | | Language Code | en | Moodle 站点默认语言 | 128 | 129 | 2. 选择**下一步**。 130 | 131 | 3. 在**配置堆栈选项**页面上,选择“下一步”。 132 | 133 | 4. 在**审核**页面上,查看并确认设置。 确保选中确认模板将创建 AWS Identity and Access Management(IAM)资源的框。 134 | 135 | 5. 选择**创建堆栈**以部署堆栈。 136 | 137 | 您可以在AWS CloudFormation控制台的**状态**列中查看堆栈的状态。 您应该在大约30分钟内看到状态为CREATE_COMPLETE。 138 | 139 | ## 步骤2: 配置 CloudFront 和 ALB CNAME 140 | 141 | 通过如下步骤找到 ALB 和 CloudFront 的 DNS Name. 如何配置 CNAME 记录取决于您使用的 DNS Resolver. 142 | 143 | 1. 登录到 AWS 管理控制台,并选择进入 CloudFormation 控制台。 144 | 145 | 1. 选择步骤1中创建的 CloudFormation 主堆栈(请不要选择 NESTED 堆栈)。 146 | 147 | 1. 点击**输出**。 148 | 149 | 1. **PublicAlbDnsName** 为 ALB DNS Name, **CloudFrontDnsName** 为 CloudFront DNS Name. 150 | 151 | 登录到您的 DNS Resolver 控制台,配置域名指向这两处 DNS Name。 152 | 153 | 154 | ## 构建自己的解决方案 155 | 156 | 点击查看[文档](build.md) 157 | 158 | -------------------------------------------------------------------------------- /assets/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/moodle-on-aws-cn/edf9c8bacaaa1f50754d472e7c7480e39fa81a0d/assets/architecture.png -------------------------------------------------------------------------------- /deployment/00-master.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Stack to deploy a highly available, elastic, scalable Moodle environment. This master stack launches multiple nested stacks for different tiers. !! This can only be run in certain AWS Regions - 'cn-north-1, 'cn-northwest-1'. 5 | 6 | 7 | Metadata: 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: General AWS 12 | Parameters: 13 | - EC2KeyName 14 | - SshAccessCidr 15 | - DomainName 16 | - Label: 17 | default: Network 18 | Parameters: 19 | - NumberOfAZs 20 | - AvailabilityZones 21 | - VpcCidr 22 | - VpcTenancy 23 | - PublicSubnet0Cidr 24 | - PublicSubnet1Cidr 25 | - PublicSubnet2Cidr 26 | - WebSubnet0Cidr 27 | - WebSubnet1Cidr 28 | - WebSubnet2Cidr 29 | - DataSubnet0Cidr 30 | - DataSubnet1Cidr 31 | - DataSubnet2Cidr 32 | - Label: 33 | default: File System Tier 34 | Parameters: 35 | - EfsPerformanceMode 36 | - EfsEncrpytedBoolean 37 | - EfsGrowth 38 | - EfsGrowthInstanceType 39 | - Label: 40 | default: Database Tier 41 | Parameters: 42 | - DatabaseInstanceType 43 | # - DatabaseEncrpytedBoolean 44 | # - DatabaseCmk 45 | - DatabaseMasterUsername 46 | - DatabaseMasterPassword 47 | - DatabaseName 48 | - Label: 49 | default: Caching Tier 50 | Parameters: 51 | - UseSessionCacheBoolean 52 | - SessionCacheNodeType 53 | - UseApplicationCacheBoolean 54 | - ApplicationCacheNodeType 55 | - UseCloudFrontBoolean 56 | - CloudFrontIamCertificateId 57 | - Label: 58 | default: Web Tier 59 | Parameters: 60 | - PublicAlbDomainName 61 | - PublicAlbCertificateArn 62 | - WebInstanceType 63 | - WebAsgMax 64 | - WebAsgMin 65 | - Label: 66 | default: Moodle 67 | Parameters: 68 | - MoodleLocale 69 | ParameterLabels: 70 | CloudFrontIamCertificateId: 71 | default: CloudFront Certificate ID uploaded in AWS IAM 72 | # DatabaseCmk: 73 | # default: AWS KMS CMK for RDS 74 | # DatabaseEncrpytedBoolean: 75 | # default: Encrypted DB Cluster 76 | DatabaseInstanceType: 77 | default: DB Instance Class 78 | DatabaseMasterUsername: 79 | default: DB Master Username 80 | DatabaseMasterPassword: 81 | default: DB Master Password 82 | DatabaseName: 83 | default: DB Name 84 | EfsEncrpytedBoolean: 85 | default: Encrpyted EFS? 86 | EfsPerformanceMode: 87 | default: EFS Performance Mode 88 | EfsGrowth: 89 | default: Add dummy data (GiB) 90 | EfsGrowthInstanceType: 91 | default: Instance Type 92 | EC2KeyName: 93 | default: EC2 Key Pair 94 | PublicAlbDomainName: 95 | default: Public ALB Domain Name 96 | PublicAlbCertificateArn: 97 | default: ALB Certificate ARN 98 | SshAccessCidr: 99 | default: SSH Access From 100 | UseApplicationCacheBoolean: 101 | default: Use Application Cache 102 | ApplicationCacheNodeType: 103 | default: Application Cache Node Type 104 | UseSessionCacheBoolean: 105 | default: Use Session Cache 106 | SessionCacheNodeType: 107 | default: Session Cache Node Type 108 | WebAsgMax: 109 | default: Web ASG Max 110 | WebAsgMin: 111 | default: Web ASG Min 112 | WebInstanceType: 113 | default: Web Tier Instance Type 114 | DomainName: 115 | default: Site Domain 116 | MoodleLocale: 117 | default: Language Code 118 | AvailabilityZones: 119 | default: Availability Zones 120 | NumberOfAZs: 121 | default: Number of Availability Zones 122 | VpcCidr: 123 | default: VpcCidr 124 | VpcTenancy: 125 | default: VpcTenancy 126 | PublicSubnet0Cidr: 127 | default: Public Subnet 0 128 | PublicSubnet1Cidr: 129 | default: Public Subnet 1 130 | PublicSubnet2Cidr: 131 | default: Public Subnet 2 132 | WebSubnet0Cidr: 133 | default: Web Subnet 0 134 | WebSubnet1Cidr: 135 | default: Web Subnet 1 136 | WebSubnet2Cidr: 137 | default: Web Subnet 2 138 | DataSubnet0Cidr: 139 | default: Data Subnet 0 140 | DataSubnet1Cidr: 141 | default: Data Subnet 1 142 | DataSubnet2Cidr: 143 | default: Data Subnet 2 144 | UseCloudFrontBoolean: 145 | default: Use CloudFront 146 | 147 | 148 | Parameters: 149 | PublicAlbCertificateArn: 150 | AllowedPattern: ^$|(arn:aws-cn:iam)::([0-9]{12}):server-certificate/(.*)$ 151 | Description: '[ Optional ] The certificate ARN for the ALB certificate - this certificate should be created in the region you wish to run the ALB and must reference the Moodle domain name you use below.' 152 | Type: String 153 | Default: '' 154 | CloudFrontIamCertificateId: 155 | AllowedPattern: ^$|(arn:aws-cn:iam)::([0-9]{12}):server-certificate/(.*)$ 156 | Description: '[ Optional ] The AWS Certification Manager certificate ARN for the CloudFront distribution certificate - this certificate should be created in the us-east-1 (N. Virginia) region and must reference the Moodle domain name you use below.' 157 | Type: String 158 | Default: '' 159 | # DatabaseCmk: 160 | # Description: AWS KMS Customer Master Key (CMK) to encrypt database cluster 161 | # Type: String 162 | # DatabaseEncrpytedBoolean: 163 | # AllowedValues: 164 | # - true 165 | # - false 166 | # Default: true 167 | # Description: Indicates whether the DB instances in the cluster are encrypted. 168 | # Type: String 169 | DatabaseInstanceType: 170 | AllowedValues: 171 | - db.r5.large 172 | - db.r5.xlarge 173 | - db.r5.2xlarge 174 | - db.r5.4xlarge 175 | - db.r5.12xlarge 176 | ConstraintDescription: Must be a valid RDS instance class. 177 | Default: db.r5.large 178 | Description: The Amazon RDS database instance class. 179 | Type: String 180 | DatabaseMasterUsername: 181 | AllowedPattern: ^([a-zA-Z0-9]*)$ 182 | Description: The Amazon RDS master username. 183 | ConstraintDescription: Must contain only alphanumeric characters and be at least 8 characters. 184 | MaxLength: 16 185 | MinLength: 1 186 | Type: String 187 | Default: moodle 188 | DatabaseMasterPassword: 189 | AllowedPattern: ^([a-zA-Z0-9`~!#$%^&*()_+,\\-])*$ 190 | ConstraintDescription: Must be letters (upper or lower), numbers, spaces, and these special characters `~!#$%^&*()_+,- 191 | Description: The Amazon RDS master password. Letters, numbers, spaces, and these special characters `~!#$%^&*()_+,- 192 | MaxLength: 41 193 | MinLength: 8 194 | NoEcho: true 195 | Type: String 196 | DatabaseName: 197 | AllowedPattern: ^([a-zA-Z0-9]*)$ 198 | Description: The Amazon RDS master database name. 199 | Type: String 200 | Default: moodle 201 | EfsEncrpytedBoolean: 202 | AllowedValues: 203 | - true 204 | - false 205 | Default: false 206 | Description: Create an encrypted Amazon EFS file system. 207 | Type: String 208 | EfsPerformanceMode: 209 | AllowedValues: 210 | - generalPurpose 211 | - maxIO 212 | Default: generalPurpose 213 | Description: Select the performance mode of the file system. 214 | Type: String 215 | EfsGrowth: 216 | Default: 0 217 | ConstraintDescription: Must be an integer. 218 | Description: Amount of dummy data (GiB) to add to the file system (max 6144 GiB). Amazon EFS storage charges apply. 219 | MaxValue: 6144 220 | MinValue: 0 221 | Type: Number 222 | EfsGrowthInstanceType: 223 | AllowedValues: 224 | - t2.medium 225 | - t2.large 226 | - t2.xlarge 227 | - t2.2xlarge 228 | - m4.large 229 | - m4.xlarge 230 | - m4.2xlarge 231 | - m4.4xlarge 232 | - m4.10xlarge 233 | - m4.16xlarge 234 | - m5.large 235 | - m5.xlarge 236 | - m5.2xlarge 237 | - m5.4xlarge 238 | - m5.12xlarge 239 | - m5.24xlarge 240 | - c4.large 241 | - c4.xlarge 242 | - c4.2xlarge 243 | - c4.4xlarge 244 | - c4.8xlarge 245 | - c5.large 246 | - c5.xlarge 247 | - c5.2xlarge 248 | - c5.4xlarge 249 | - c5.9xlarge 250 | - c5.18xlarge 251 | - r4.large 252 | - r4.xlarge 253 | - r4.2xlarge 254 | - r4.4xlarge 255 | - r4.8xlarge 256 | - r4.16xlarge 257 | - r5.large 258 | - r5.2xlarge 259 | - r5.4xlarge 260 | - r5.8xlarge 261 | - r5.12xlarge 262 | - r5.16xlarge 263 | - r5.24xlarge 264 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 265 | Default: r4.large 266 | Description: The Amazon EC2 instance type that adds data to the file system. 267 | Type: String 268 | UseSessionCacheBoolean: 269 | AllowedValues: 270 | - true 271 | - false 272 | Default: true 273 | Description: Specifies whether an ElastiCache Cache Cluster should be created for sessions. Do not create a session cache during he inital deploy. You should update the stack to use session cache after completing the installation wizard. 274 | Type: String 275 | SessionCacheNodeType: 276 | AllowedValues: 277 | - cache.r4.large 278 | - cache.r4.xlarge 279 | - cache.r4.2xlarge 280 | - cache.r4.4xlarge 281 | - cache.r4.8xlarge 282 | - cache.r4.16xlarge 283 | - cache.r5.large 284 | - cache.r5.xlarge 285 | - cache.r5.2xlarge 286 | - cache.r5.4xlarge 287 | - cache.r5.12xlarge 288 | - cache.r5.24xlarge 289 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 290 | Default: cache.r5.large 291 | Description: The Amazon ElastiCache cluster node type. 292 | Type: String 293 | UseApplicationCacheBoolean: 294 | AllowedValues: 295 | - true 296 | - false 297 | Default: false 298 | Description: Specifies whether an ElastiCache Cache Cluster should be created to cache application content. 299 | Type: String 300 | ApplicationCacheNodeType: 301 | AllowedValues: 302 | - cache.r4.large 303 | - cache.r4.xlarge 304 | - cache.r4.2xlarge 305 | - cache.r4.4xlarge 306 | - cache.r4.8xlarge 307 | - cache.r4.16xlarge 308 | - cache.r5.large 309 | - cache.r5.xlarge 310 | - cache.r5.2xlarge 311 | - cache.r5.4xlarge 312 | - cache.r5.12xlarge 313 | - cache.r5.24xlarge 314 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 315 | Default: cache.r5.large 316 | Description: The Amazon ElastiCache cluster node type. 317 | Type: String 318 | EC2KeyName: 319 | ConstraintDescription: Must be letters (upper or lower), numbers, and special characters. 320 | Description: Name of an EC2 KeyPair. Your Web instances will launch with this KeyPair. 321 | Type: AWS::EC2::KeyPair::KeyName 322 | SshAccessCidr: 323 | 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]))$ 324 | Description: The CIDR IP range that is permitted to SSH to bastion instance. Note - a value of 0.0.0.0/0 will allow access from ANY IP address. 325 | Type: String 326 | Default: 0.0.0.0/0 327 | WebAsgMax: 328 | AllowedPattern: ^((?!0$)[1-2]?[0-9]|30)$ 329 | ConstraintDescription: Must be a number between 1 and 30. 330 | Default: 1 331 | Description: Specifies the maximum number of EC2 instances in the Web Autoscaling Group. 332 | Type: String 333 | WebAsgMin: 334 | AllowedPattern: ^([0-0]?[0-9]|10)$ 335 | ConstraintDescription: Must be a number between 0 and 10. 336 | Default: 1 337 | Description: Specifies the minimum number of EC2 instances in the Web Autoscaling Group. 338 | Type: String 339 | WebInstanceType: 340 | AllowedValues: 341 | - t2.medium 342 | - t2.large 343 | - t2.xlarge 344 | - t2.2xlarge 345 | - m4.large 346 | - m4.xlarge 347 | - m4.2xlarge 348 | - m4.4xlarge 349 | - m4.10xlarge 350 | - m4.16xlarge 351 | - m5.large 352 | - m5.xlarge 353 | - m5.2xlarge 354 | - m5.4xlarge 355 | - m5.12xlarge 356 | - m5.24xlarge 357 | - c4.large 358 | - c4.xlarge 359 | - c4.2xlarge 360 | - c4.4xlarge 361 | - c4.8xlarge 362 | - c5.large 363 | - c5.xlarge 364 | - c5.2xlarge 365 | - c5.4xlarge 366 | - c5.9xlarge 367 | - c5.18xlarge 368 | - r4.large 369 | - r4.xlarge 370 | - r4.2xlarge 371 | - r4.4xlarge 372 | - r4.8xlarge 373 | - r4.16xlarge 374 | - r5.large 375 | - r5.2xlarge 376 | - r5.4xlarge 377 | - r5.8xlarge 378 | - r5.12xlarge 379 | - r5.16xlarge 380 | - r5.24xlarge 381 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 382 | Default: c5.large 383 | Description: The Amazon EC2 instance type for your web instances. 384 | Type: String 385 | DomainName: 386 | AllowedPattern: ^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ 387 | Description: 'The main domain name of the Moodle site (e.g. moodle.example.edu).' 388 | Type: String 389 | Default: '' 390 | MoodleLocale: 391 | Description: "The main language of the Moodle site, during initial configuration." 392 | Type: String 393 | Default: en 394 | AvailabilityZones: 395 | Description: 'List of Availability Zones to use for the subnets in the VPC. Note: 396 | The logical order is preserved.' 397 | Type: List 398 | NumberOfAZs: 399 | AllowedValues: 400 | - 2 401 | - 3 402 | Default: 3 403 | Description: Number of Availability Zones to use in the VPC. This must match your 404 | selections in the list of Availability Zones parameter. 405 | Type: Number 406 | VpcCidr: 407 | 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]))$" 408 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 409 | Default: 10.0.0.0/16 410 | Description: CIDR block for the VPC 411 | Type: String 412 | VpcTenancy: 413 | AllowedValues: 414 | - default 415 | - dedicated 416 | Default: default 417 | Description: The allowed tenancy of instances launched into the VPC 418 | Type: String 419 | DataSubnet0Cidr: 420 | 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]))$" 421 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 422 | Default: 10.0.100.0/24 423 | Description: CIDR block for data subnet 0 located in Availability Zone 0 424 | Type: String 425 | DataSubnet1Cidr: 426 | 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]))$" 427 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 428 | Default: 10.0.101.0/24 429 | Description: CIDR block for data subnet 1 located in Availability Zone 1 430 | Type: String 431 | DataSubnet2Cidr: 432 | 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]))$" 433 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 434 | Default: 10.0.102.0/24 435 | Description: CIDR block for data subnet 2 located in Availability Zone 2 436 | Type: String 437 | PublicSubnet0Cidr: 438 | 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]))$" 439 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 440 | Default: 10.0.200.0/24 441 | Description: CIDR block for Public subnet 0 located in Availability Zone 0 442 | Type: String 443 | PublicSubnet1Cidr: 444 | 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]))$" 445 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 446 | Default: 10.0.201.0/24 447 | Description: CIDR block for Public subnet 1 located in Availability Zone 1 448 | Type: String 449 | PublicSubnet2Cidr: 450 | 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]))$" 451 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 452 | Default: 10.0.202.0/24 453 | Description: CIDR block for Public subnet 2 located in Availability Zone 2 454 | Type: String 455 | WebSubnet0Cidr: 456 | 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]))$" 457 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 458 | Default: 10.0.0.0/22 459 | Description: CIDR block for Web subnet 0 located in Availability Zone 0 460 | Type: String 461 | WebSubnet1Cidr: 462 | 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]))$" 463 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 464 | Default: 10.0.4.0/22 465 | Description: CIDR block for Web subnet 1 located in Availability Zone 1 466 | Type: String 467 | WebSubnet2Cidr: 468 | 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]))$" 469 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 470 | Default: 10.0.8.0/22 471 | Description: CIDR block for Web subnet 2 located in Availability Zone 2 472 | Type: String 473 | UseCloudFrontBoolean: 474 | AllowedValues: 475 | - true 476 | - false 477 | Default: false 478 | Description: Specifies whether a CloudFront Distribution should be created to serve the Moodle website content. 479 | Type: String 480 | PublicAlbDomainName: 481 | Type: String 482 | AllowedPattern: ^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ 483 | Description: Domain Name of Public Load Balancer, e.g. moodle.lb.example.net 484 | Default: '' 485 | 486 | 487 | Conditions: 488 | DeployWithoutSessionCache: 489 | !Equals [ false, !Ref UseSessionCacheBoolean ] 490 | DeployWithSessionCache: 491 | !Equals [ true, !Ref UseSessionCacheBoolean ] 492 | DeployApplicationCache: 493 | !Equals [ true, !Ref UseApplicationCacheBoolean ] 494 | DeployCloudFront: 495 | !Equals [ true, !Ref UseCloudFrontBoolean ] 496 | PublicAlbCertificate: 497 | !Not [!Equals [ '', !Ref PublicAlbCertificateArn ] ] 498 | PublicAlbDomainName: 499 | !Not [!Equals [ '', !Ref PublicAlbDomainName ] ] 500 | DomainHttps: 501 | !Not [ !Equals ['', !Ref CloudFrontIamCertificateId] ] 502 | 503 | Resources: 504 | vpc: 505 | Type: AWS::CloudFormation::Stack 506 | Properties: 507 | Parameters: 508 | NumberOfAZs: 509 | !Ref NumberOfAZs 510 | AvailabilityZones: 511 | !Join 512 | - ',' 513 | - !Ref AvailabilityZones 514 | VpcCidr: 515 | !Ref VpcCidr 516 | VpcTenancy: 517 | !Ref VpcTenancy 518 | PublicSubnet0Cidr: 519 | !Ref PublicSubnet0Cidr 520 | PublicSubnet1Cidr: 521 | !Ref PublicSubnet1Cidr 522 | PublicSubnet2Cidr: 523 | !Ref PublicSubnet2Cidr 524 | WebSubnet0Cidr: 525 | !Ref WebSubnet0Cidr 526 | WebSubnet1Cidr: 527 | !Ref WebSubnet1Cidr 528 | WebSubnet2Cidr: 529 | !Ref WebSubnet2Cidr 530 | DataSubnet0Cidr: 531 | !Ref DataSubnet0Cidr 532 | DataSubnet1Cidr: 533 | !Ref DataSubnet1Cidr 534 | DataSubnet2Cidr: 535 | !Ref DataSubnet2Cidr 536 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/01-newvpc.template 537 | securitygroups: 538 | DependsOn: vpc 539 | Type: AWS::CloudFormation::Stack 540 | Properties: 541 | Parameters: 542 | SshAccessCidr: 543 | !Ref SshAccessCidr 544 | Vpc: 545 | !GetAtt [ vpc, Outputs.Vpc ] 546 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/02-securitygroups.template 547 | publicalb: 548 | DependsOn: securitygroups 549 | Type: AWS::CloudFormation::Stack 550 | Properties: 551 | Parameters: 552 | NumberOfSubnets: 553 | !Ref NumberOfAZs 554 | Subnet: 555 | !GetAtt [ vpc, Outputs.PublicSubnet ] 556 | PublicAlbCertificateArn: 557 | !Ref PublicAlbCertificateArn 558 | PublicAlbSecurityGroup: 559 | !GetAtt [ securitygroups, Outputs.PublicAlbSecurityGroup ] 560 | Vpc: 561 | !GetAtt [ vpc, Outputs.Vpc ] 562 | TemplateURL: hhttps://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/03-publicalb.template 563 | rds: 564 | DependsOn: [ securitygroups, securitygroups ] 565 | Type: AWS::CloudFormation::Stack 566 | Properties: 567 | Parameters: 568 | DatabaseInstanceType: 569 | !Ref DatabaseInstanceType 570 | DatabaseMasterUsername: 571 | !Ref DatabaseMasterUsername 572 | DatabaseMasterPassword: 573 | !Ref DatabaseMasterPassword 574 | DatabaseName: 575 | !Ref DatabaseName 576 | # DatabaseEncrpytedBoolean: 577 | # !Ref DatabaseEncrpytedBoolean 578 | # DatabaseCmk: 579 | # !Ref DatabaseCmk 580 | DatabaseSecurityGroup: 581 | !GetAtt [ securitygroups, Outputs.DatabaseSecurityGroup ] 582 | Subnet: 583 | !GetAtt [ vpc, Outputs.DataSubnet ] 584 | NumberOfSubnets: 585 | !Ref NumberOfAZs 586 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/03-rds.template 587 | efsfilesystem: 588 | DependsOn: securitygroups 589 | Type: AWS::CloudFormation::Stack 590 | Properties: 591 | Parameters: 592 | EncrpytedBoolean: 593 | !Ref EfsEncrpytedBoolean 594 | Growth: 595 | !Ref EfsGrowth 596 | InstanceType: 597 | !Ref EfsGrowthInstanceType 598 | EC2KeyName: 599 | !Ref EC2KeyName 600 | SecurityGroup: 601 | !GetAtt [ securitygroups, Outputs.EfsSecurityGroup ] 602 | NumberOfSubnets: 603 | !Ref NumberOfAZs 604 | PerformanceMode: 605 | !Ref EfsPerformanceMode 606 | Subnet: 607 | !GetAtt [ vpc, Outputs.DataSubnet ] 608 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/03-efsfilesystem.template 609 | sessioncache: 610 | Condition: DeployWithSessionCache 611 | DependsOn: securitygroups 612 | Type: AWS::CloudFormation::Stack 613 | Properties: 614 | Parameters: 615 | Subnet: 616 | !GetAtt [ vpc, Outputs.DataSubnet ] 617 | ElastiCacheClusterName: 618 | !Sub '${AWS::StackName}session' 619 | ElastiCacheNodeType: 620 | !Ref SessionCacheNodeType 621 | ElastiCacheSecurityGroup: 622 | !GetAtt [ securitygroups, Outputs.ElastiCacheSecurityGroup ] 623 | NumberOfSubnets: 624 | !Ref NumberOfAZs 625 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/03-elasticache.template 626 | applicationcache: 627 | Condition: DeployApplicationCache 628 | DependsOn: securitygroups 629 | Type: AWS::CloudFormation::Stack 630 | Properties: 631 | Parameters: 632 | Subnet: 633 | !GetAtt [ vpc, Outputs.DataSubnet ] 634 | ElastiCacheClusterName: 635 | !Sub '${AWS::StackName}application' 636 | ElastiCacheNodeType: 637 | !Ref ApplicationCacheNodeType 638 | ElastiCacheSecurityGroup: 639 | !GetAtt [ securitygroups, Outputs.ElastiCacheSecurityGroup ] 640 | NumberOfSubnets: 641 | !Ref NumberOfAZs 642 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/03-elasticache.template 643 | webnocache: 644 | DependsOn: [ publicalb, rds, efsfilesystem ] 645 | Condition: DeployWithoutSessionCache 646 | Type: AWS::CloudFormation::Stack 647 | Properties: 648 | Parameters: 649 | DatabaseClusterEndpointAddress: 650 | !GetAtt [ rds, Outputs.DatabaseClusterEndpointAddress ] 651 | DatabaseMasterUsername: 652 | !Ref DatabaseMasterUsername 653 | DatabaseMasterPassword: 654 | !Ref DatabaseMasterPassword 655 | DatabaseName: 656 | !Ref DatabaseName 657 | ElasticFileSystem: 658 | !GetAtt [ efsfilesystem, Outputs.ElasticFileSystem ] 659 | EC2KeyName: 660 | !Ref EC2KeyName 661 | NumberOfSubnets: 662 | !Ref NumberOfAZs 663 | PublicAlbTargetGroupArn: 664 | !GetAtt [ publicalb, Outputs.PublicAlbTargetGroupArn ] 665 | PublicAlbHostname: 666 | !GetAtt [ publicalb, Outputs.PublicAlbHostname ] 667 | SslCertificate: 668 | !GetAtt [ publicalb, Outputs.SslCertificate ] 669 | WebAsgMax: 670 | !Ref WebAsgMax 671 | WebAsgMin: 672 | !Ref WebAsgMin 673 | WebInstanceType: 674 | !Ref WebInstanceType 675 | WebSecurityGroup: 676 | !GetAtt [ securitygroups, Outputs.WebSecurityGroup ] 677 | Subnet: 678 | !GetAtt [ vpc, Outputs.WebSubnet ] 679 | DomainName: 680 | !Ref DomainName 681 | DomainHttps: 682 | !If [ DomainHttps, True, False ] 683 | MoodleLocale: 684 | !Ref MoodleLocale 685 | ElastiCacheClusterEndpointAddress: 686 | '' 687 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/04-web.template 688 | webcached: 689 | DependsOn: [ publicalb, rds, efsfilesystem, sessioncache ] 690 | Condition: DeployWithSessionCache 691 | Type: AWS::CloudFormation::Stack 692 | Properties: 693 | Parameters: 694 | DatabaseClusterEndpointAddress: 695 | !GetAtt [ rds, Outputs.DatabaseClusterEndpointAddress ] 696 | DatabaseMasterUsername: 697 | !Ref DatabaseMasterUsername 698 | DatabaseMasterPassword: 699 | !Ref DatabaseMasterPassword 700 | DatabaseName: 701 | !Ref DatabaseName 702 | ElasticFileSystem: 703 | !GetAtt [ efsfilesystem, Outputs.ElasticFileSystem ] 704 | EC2KeyName: 705 | !Ref EC2KeyName 706 | NumberOfSubnets: 707 | !Ref NumberOfAZs 708 | PublicAlbTargetGroupArn: 709 | !GetAtt [ publicalb, Outputs.PublicAlbTargetGroupArn ] 710 | PublicAlbHostname: 711 | !GetAtt [ publicalb, Outputs.PublicAlbHostname ] 712 | SslCertificate: 713 | !GetAtt [ publicalb, Outputs.SslCertificate ] 714 | WebAsgMax: 715 | !Ref WebAsgMax 716 | WebAsgMin: 717 | !Ref WebAsgMin 718 | WebInstanceType: 719 | !Ref WebInstanceType 720 | WebSecurityGroup: 721 | !GetAtt [ securitygroups, Outputs.WebSecurityGroup ] 722 | Subnet: 723 | !GetAtt [ vpc, Outputs.WebSubnet ] 724 | DomainName: 725 | !Ref DomainName 726 | DomainHttps: 727 | !If [ DomainHttps, True, False ] 728 | MoodleLocale: 729 | !Ref MoodleLocale 730 | ElastiCacheClusterEndpointAddress: 731 | !GetAtt sessioncache.Outputs.ElastiCacheClusterEndpointAddress 732 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/04-web.template 733 | cloudfront: 734 | Condition: DeployCloudFront 735 | DependsOn: publicalb 736 | Type: AWS::CloudFormation::Stack 737 | Properties: 738 | Parameters: 739 | CloudFrontIamCertificateId: 740 | !Ref CloudFrontIamCertificateId 741 | PublicAlbDomainName: 742 | !If [PublicAlbDomainName, !Ref PublicAlbDomainName , !GetAtt [ publicalb, Outputs.PublicAlbDnsName ] ] 743 | DomainName: 744 | !Ref DomainName 745 | PublicAlbCertificate: 746 | !If [PublicAlbCertificate, True, False] 747 | 748 | TemplateURL: https://s3.cn-north-1.amazonaws.com.cn/%%TEMPLATE_BUCKET_NAME%%/%%SOLUTION_NAME%%/%%VERSION%%/04-cloudfront.template 749 | 750 | Outputs: 751 | PublicAlbDnsName: 752 | Description: DNS name of the new load balancer for Moodle. Create DNS record and point to this. 753 | Value: 754 | !GetAtt publicalb.Outputs.PublicAlbDnsName 755 | CloudFrontDnsName: 756 | Condition: DeployCloudFront 757 | Description: DNS name of the CloudFront. Create DNS record and point to this. 758 | Value: !GetAtt cloudfront.Outputs.DnsEndpoint 759 | -------------------------------------------------------------------------------- /deployment/01-newvpc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates New VPC 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | 10 | ParameterGroups: 11 | - Label: 12 | default: Amazon VPC Parameters 13 | Parameters: 14 | - NumberOfAZs 15 | - AvailabilityZones 16 | - VpcCidr 17 | - VpcTenancy 18 | - PublicSubnet0Cidr 19 | - PublicSubnet1Cidr 20 | - PublicSubnet2Cidr 21 | - PublicSubnet3Cidr 22 | - PublicSubnet4Cidr 23 | - PublicSubnet5Cidr 24 | - WebSubnet0Cidr 25 | - WebSubnet1Cidr 26 | - WebSubnet2Cidr 27 | - WebSubnet3Cidr 28 | - WebSubnet4Cidr 29 | - WebSubnet5Cidr 30 | - DataSubnet0Cidr 31 | - DataSubnet1Cidr 32 | - DataSubnet2Cidr 33 | - DataSubnet3Cidr 34 | - DataSubnet4Cidr 35 | - DataSubnet5Cidr 36 | ParameterLabels: 37 | AvailabilityZones: 38 | default: Availability Zones 39 | NumberOfAZs: 40 | default: Number of Availability Zones 41 | VpcCidr: 42 | default: VpcCidr 43 | VpcTenancy: 44 | default: VpcTenancy 45 | PublicSubnet0Cidr: 46 | default: Public Subnet 0 47 | PublicSubnet1Cidr: 48 | default: Public Subnet 1 49 | PublicSubnet2Cidr: 50 | default: Public Subnet 2 51 | PublicSubnet3Cidr: 52 | default: Public Subnet 3 53 | PublicSubnet4Cidr: 54 | default: Public Subnet 4 55 | PublicSubnet5Cidr: 56 | default: Public Subnet 5 57 | WebSubnet0Cidr: 58 | default: Web Subnet 0 59 | WebSubnet1Cidr: 60 | default: Web Subnet 1 61 | WebSubnet2Cidr: 62 | default: Web Subnet 2 63 | WebSubnet3Cidr: 64 | default: Web Subnet 3 65 | WebSubnet4Cidr: 66 | default: Web Subnet 4 67 | WebSubnet5Cidr: 68 | default: Web Subnet 5 69 | DataSubnet0Cidr: 70 | default: Data Subnet 0 71 | DataSubnet1Cidr: 72 | default: Data Subnet 1 73 | DataSubnet2Cidr: 74 | default: Data Subnet 2 75 | DataSubnet3Cidr: 76 | default: Data Subnet 3 77 | DataSubnet4Cidr: 78 | default: Data Subnet 4 79 | DataSubnet5Cidr: 80 | default: Data Subnet 5 81 | 82 | Parameters: 83 | 84 | AvailabilityZones: 85 | Description: 'List of Availability Zones to use for the subnets in the VPC. Note: 86 | The logical order is preserved.' 87 | Type: List 88 | NumberOfAZs: 89 | AllowedValues: 90 | - 2 91 | - 3 92 | - 4 93 | - 5 94 | - 6 95 | Default: 3 96 | Description: Number of Availability Zones to use in the VPC. This must match your 97 | selections in the list of Availability Zones parameter. 98 | Type: Number 99 | VpcCidr: 100 | 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]))$" 101 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 102 | Default: 10.0.0.0/16 103 | Description: CIDR block for the VPC 104 | Type: String 105 | VpcTenancy: 106 | AllowedValues: 107 | - default 108 | - dedicated 109 | Default: default 110 | Description: The allowed tenancy of instances launched into the VPC 111 | Type: String 112 | DataSubnet0Cidr: 113 | 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]))$" 114 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 115 | Default: 10.0.100.0/24 116 | Description: CIDR block for data subnet 0 located in Availability Zone 0 117 | Type: String 118 | DataSubnet1Cidr: 119 | 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]))$" 120 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 121 | Default: 10.0.101.0/24 122 | Description: CIDR block for data subnet 1 located in Availability Zone 1 123 | Type: String 124 | DataSubnet2Cidr: 125 | 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]))$" 126 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 127 | Default: 10.0.102.0/24 128 | Description: CIDR block for data subnet 2 located in Availability Zone 2 129 | Type: String 130 | DataSubnet3Cidr: 131 | 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]))$" 132 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 133 | Default: 10.0.103.0/24 134 | Description: CIDR block for data subnet 3 located in Availability Zone 3 135 | Type: String 136 | DataSubnet4Cidr: 137 | 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]))$" 138 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 139 | Default: 10.0.104.0/24 140 | Description: CIDR block for data subnet 4 located in Availability Zone 4 141 | Type: String 142 | DataSubnet5Cidr: 143 | 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]))$" 144 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 145 | Default: 10.0.105.0/24 146 | Description: CIDR block for data subnet 5 located in Availability Zone 5 147 | Type: String 148 | PublicSubnet0Cidr: 149 | 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]))$" 150 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 151 | Default: 10.0.200.0/24 152 | Description: CIDR block for Public subnet 0 located in Availability Zone 0 153 | Type: String 154 | PublicSubnet1Cidr: 155 | 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]))$" 156 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 157 | Default: 10.0.201.0/24 158 | Description: CIDR block for Public subnet 1 located in Availability Zone 1 159 | Type: String 160 | PublicSubnet2Cidr: 161 | 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]))$" 162 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 163 | Default: 10.0.202.0/24 164 | Description: CIDR block for Public subnet 2 located in Availability Zone 2 165 | Type: String 166 | PublicSubnet3Cidr: 167 | 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]))$" 168 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 169 | Default: 10.0.203.0/24 170 | Description: CIDR block for Public subnet 3 located in Availability Zone 3 171 | Type: String 172 | PublicSubnet4Cidr: 173 | 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]))$" 174 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 175 | Default: 10.0.204.0/24 176 | Description: CIDR block for Public subnet 4 located in Availability Zone 4 177 | Type: String 178 | PublicSubnet5Cidr: 179 | 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]))$" 180 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 181 | Default: 10.0.205.0/24 182 | Description: CIDR block for Public subnet 5 located in Availability Zone 5 183 | Type: String 184 | WebSubnet0Cidr: 185 | 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]))$" 186 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 187 | Default: 10.0.0.0/22 188 | Description: CIDR block for Web subnet 0 located in Availability Zone 0 189 | Type: String 190 | WebSubnet1Cidr: 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])(\\/(1[6-9]|2[0-8]))$" 192 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 193 | Default: 10.0.4.0/22 194 | Description: CIDR block for Web subnet 1 located in Availability Zone 1 195 | Type: String 196 | WebSubnet2Cidr: 197 | 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]))$" 198 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 199 | Default: 10.0.8.0/22 200 | Description: CIDR block for Web subnet 2 located in Availability Zone 2 201 | Type: String 202 | WebSubnet3Cidr: 203 | 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]))$" 204 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 205 | Default: 10.0.12.0/22 206 | Description: CIDR block for Web subnet 3 located in Availability Zone 3 207 | Type: String 208 | WebSubnet4Cidr: 209 | 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]))$" 210 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 211 | Default: 10.0.16.0/22 212 | Description: CIDR block for Web subnet 4 located in Availability Zone 4 213 | Type: String 214 | WebSubnet5Cidr: 215 | 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]))$" 216 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 217 | Default: 10.0.20.0/22 218 | Description: CIDR block for Web subnet 5 located in Availability Zone 5 219 | Type: String 220 | 221 | Conditions: 222 | 223 | NumberOfAZs1: 224 | !Equals [ '1', !Ref NumberOfAZs ] 225 | NumberOfAZs2: 226 | !Equals [ '2', !Ref NumberOfAZs ] 227 | NumberOfAZs3: 228 | !Equals [ '3', !Ref NumberOfAZs ] 229 | NumberOfAZs4: 230 | !Equals [ '4', !Ref NumberOfAZs ] 231 | NumberOfAZs5: 232 | !Equals [ '5', !Ref NumberOfAZs ] 233 | NumberOfAZs6: 234 | !Equals [ '6', !Ref NumberOfAZs ] 235 | AZ0: !Or 236 | - !Condition NumberOfAZs1 237 | - !Condition NumberOfAZs2 238 | - !Condition NumberOfAZs3 239 | - !Condition NumberOfAZs4 240 | - !Condition NumberOfAZs5 241 | - !Condition NumberOfAZs6 242 | AZ1: !Or 243 | - !Condition NumberOfAZs2 244 | - !Condition NumberOfAZs3 245 | - !Condition NumberOfAZs4 246 | - !Condition NumberOfAZs5 247 | - !Condition NumberOfAZs6 248 | AZ2: !Or 249 | - !Condition NumberOfAZs3 250 | - !Condition NumberOfAZs4 251 | - !Condition NumberOfAZs5 252 | - !Condition NumberOfAZs6 253 | AZ3: !Or 254 | - !Condition NumberOfAZs4 255 | - !Condition NumberOfAZs5 256 | - !Condition NumberOfAZs6 257 | AZ4: !Or 258 | - !Condition NumberOfAZs5 259 | - !Condition NumberOfAZs6 260 | AZ5: !Condition NumberOfAZs6 261 | 262 | Resources: 263 | 264 | WebSubnet0: 265 | Condition: AZ0 266 | Type: AWS::EC2::Subnet 267 | Properties: 268 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 269 | CidrBlock: !Ref WebSubnet0Cidr 270 | MapPublicIpOnLaunch: false 271 | Tags: 272 | - Key: Name 273 | Value: !Join [ '', [ 'WebSubnet0 / ', !Ref 'AWS::StackName' ] ] 274 | - Key: SubnetType 275 | Value: Private 276 | VpcId: !Ref Vpc 277 | WebSubnet1: 278 | Condition: AZ1 279 | Type: AWS::EC2::Subnet 280 | Properties: 281 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 282 | CidrBlock: !Ref WebSubnet1Cidr 283 | MapPublicIpOnLaunch: false 284 | Tags: 285 | - Key: Name 286 | Value: !Join [ '', [ 'WebSubnet1 / ', !Ref 'AWS::StackName' ] ] 287 | - Key: SubnetType 288 | Value: Private 289 | VpcId: !Ref Vpc 290 | WebSubnet2: 291 | Condition: AZ2 292 | Type: AWS::EC2::Subnet 293 | Properties: 294 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 295 | CidrBlock: !Ref WebSubnet2Cidr 296 | MapPublicIpOnLaunch: false 297 | Tags: 298 | - Key: Name 299 | Value: !Join [ '', [ 'WebSubnet2 / ', !Ref 'AWS::StackName' ] ] 300 | - Key: SubnetType 301 | Value: Private 302 | VpcId: !Ref Vpc 303 | WebSubnet3: 304 | Condition: AZ3 305 | Type: AWS::EC2::Subnet 306 | Properties: 307 | AvailabilityZone: !Select [ 3, !Ref AvailabilityZones ] 308 | CidrBlock: !Ref WebSubnet3Cidr 309 | MapPublicIpOnLaunch: false 310 | Tags: 311 | - Key: Name 312 | Value: !Join [ '', [ 'WebSubnet3 / ', !Ref 'AWS::StackName' ] ] 313 | - Key: SubnetType 314 | Value: Private 315 | VpcId: !Ref Vpc 316 | WebSubnet4: 317 | Condition: AZ4 318 | Type: AWS::EC2::Subnet 319 | Properties: 320 | AvailabilityZone: !Select [ 4, !Ref AvailabilityZones ] 321 | CidrBlock: !Ref WebSubnet4Cidr 322 | MapPublicIpOnLaunch: false 323 | Tags: 324 | - Key: Name 325 | Value: !Join [ '', [ 'WebSubnet4 / ', !Ref 'AWS::StackName' ] ] 326 | - Key: SubnetType 327 | Value: Private 328 | VpcId: !Ref Vpc 329 | WebSubnet5: 330 | Condition: AZ5 331 | Type: AWS::EC2::Subnet 332 | Properties: 333 | AvailabilityZone: !Select [ 5, !Ref AvailabilityZones ] 334 | CidrBlock: !Ref WebSubnet5Cidr 335 | MapPublicIpOnLaunch: false 336 | Tags: 337 | - Key: Name 338 | Value: !Join [ '', [ 'WebSubnet5 / ', !Ref 'AWS::StackName' ] ] 339 | - Key: SubnetType 340 | Value: Private 341 | VpcId: !Ref Vpc 342 | 343 | WebSubnetRouteTableAssociation0: 344 | Condition: AZ0 345 | Type: AWS::EC2::SubnetRouteTableAssociation 346 | Properties: 347 | RouteTableId: !Ref NatRouteTable0 348 | SubnetId: !Ref WebSubnet0 349 | WebSubnetRouteTableAssociation1: 350 | Condition: AZ1 351 | Type: AWS::EC2::SubnetRouteTableAssociation 352 | Properties: 353 | RouteTableId: !Ref NatRouteTable1 354 | SubnetId: !Ref WebSubnet1 355 | WebSubnetRouteTableAssociation2: 356 | Condition: AZ2 357 | Type: AWS::EC2::SubnetRouteTableAssociation 358 | Properties: 359 | RouteTableId: !Ref NatRouteTable2 360 | SubnetId: !Ref WebSubnet2 361 | WebSubnetRouteTableAssociation3: 362 | Condition: AZ3 363 | Type: AWS::EC2::SubnetRouteTableAssociation 364 | Properties: 365 | RouteTableId: !Ref NatRouteTable3 366 | SubnetId: !Ref WebSubnet3 367 | WebSubnetRouteTableAssociation4: 368 | Condition: AZ4 369 | Type: AWS::EC2::SubnetRouteTableAssociation 370 | Properties: 371 | RouteTableId: !Ref NatRouteTable4 372 | SubnetId: !Ref WebSubnet4 373 | WebSubnetRouteTableAssociation5: 374 | Condition: AZ5 375 | Type: AWS::EC2::SubnetRouteTableAssociation 376 | Properties: 377 | RouteTableId: !Ref NatRouteTable5 378 | SubnetId: !Ref WebSubnet5 379 | 380 | DataSubnet0: 381 | Condition: AZ0 382 | Type: AWS::EC2::Subnet 383 | Properties: 384 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 385 | CidrBlock: !Ref DataSubnet0Cidr 386 | MapPublicIpOnLaunch: false 387 | Tags: 388 | - Key: Name 389 | Value: !Join [ '', [ 'DataSubnet0 / ', !Ref 'AWS::StackName' ] ] 390 | - Key: SubnetType 391 | Value: Private 392 | VpcId: !Ref Vpc 393 | DataSubnet1: 394 | Condition: AZ1 395 | Type: AWS::EC2::Subnet 396 | Properties: 397 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 398 | CidrBlock: !Ref DataSubnet1Cidr 399 | MapPublicIpOnLaunch: false 400 | Tags: 401 | - Key: Name 402 | Value: !Join [ '', [ 'DataSubnet1 / ', !Ref 'AWS::StackName' ] ] 403 | - Key: SubnetType 404 | Value: Private 405 | VpcId: !Ref Vpc 406 | DataSubnet2: 407 | Condition: AZ2 408 | Type: AWS::EC2::Subnet 409 | Properties: 410 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 411 | CidrBlock: !Ref DataSubnet2Cidr 412 | MapPublicIpOnLaunch: false 413 | Tags: 414 | - Key: Name 415 | Value: !Join [ '', [ 'DataSubnet2 / ', !Ref 'AWS::StackName' ] ] 416 | - Key: SubnetType 417 | Value: Private 418 | VpcId: !Ref Vpc 419 | DataSubnet3: 420 | Condition: AZ3 421 | Type: AWS::EC2::Subnet 422 | Properties: 423 | AvailabilityZone: !Select [ 3, !Ref AvailabilityZones ] 424 | CidrBlock: !Ref DataSubnet3Cidr 425 | MapPublicIpOnLaunch: false 426 | Tags: 427 | - Key: Name 428 | Value: !Join [ '', [ 'DataSubnet3 / ', !Ref 'AWS::StackName' ] ] 429 | - Key: SubnetType 430 | Value: Private 431 | VpcId: !Ref Vpc 432 | DataSubnet4: 433 | Condition: AZ4 434 | Type: AWS::EC2::Subnet 435 | Properties: 436 | AvailabilityZone: !Select [ 4, !Ref AvailabilityZones ] 437 | CidrBlock: !Ref DataSubnet4Cidr 438 | MapPublicIpOnLaunch: false 439 | Tags: 440 | - Key: Name 441 | Value: !Join [ '', [ 'DataSubnet4 / ', !Ref 'AWS::StackName' ] ] 442 | - Key: SubnetType 443 | Value: Private 444 | VpcId: !Ref Vpc 445 | DataSubnet5: 446 | Condition: AZ5 447 | Type: AWS::EC2::Subnet 448 | Properties: 449 | AvailabilityZone: !Select [ 5, !Ref AvailabilityZones ] 450 | CidrBlock: !Ref DataSubnet5Cidr 451 | MapPublicIpOnLaunch: false 452 | Tags: 453 | - Key: Name 454 | Value: !Join [ '', [ 'DataSubnet5 / ', !Ref 'AWS::StackName' ] ] 455 | - Key: SubnetType 456 | Value: Private 457 | VpcId: !Ref Vpc 458 | 459 | DataSubnetRouteTableAssociation0: 460 | Condition: AZ0 461 | Type: AWS::EC2::SubnetRouteTableAssociation 462 | Properties: 463 | RouteTableId: !Ref NatRouteTable0 464 | SubnetId: !Ref DataSubnet0 465 | DataSubnetRouteTableAssociation1: 466 | Condition: AZ1 467 | Type: AWS::EC2::SubnetRouteTableAssociation 468 | Properties: 469 | RouteTableId: !Ref NatRouteTable1 470 | SubnetId: !Ref DataSubnet1 471 | DataSubnetRouteTableAssociation2: 472 | Condition: AZ2 473 | Type: AWS::EC2::SubnetRouteTableAssociation 474 | Properties: 475 | RouteTableId: !Ref NatRouteTable2 476 | SubnetId: !Ref DataSubnet2 477 | DataSubnetRouteTableAssociation3: 478 | Condition: AZ3 479 | Type: AWS::EC2::SubnetRouteTableAssociation 480 | Properties: 481 | RouteTableId: !Ref NatRouteTable3 482 | SubnetId: !Ref DataSubnet3 483 | DataSubnetRouteTableAssociation4: 484 | Condition: AZ4 485 | Type: AWS::EC2::SubnetRouteTableAssociation 486 | Properties: 487 | RouteTableId: !Ref NatRouteTable4 488 | SubnetId: !Ref DataSubnet4 489 | DataSubnetRouteTableAssociation5: 490 | Condition: AZ5 491 | Type: AWS::EC2::SubnetRouteTableAssociation 492 | Properties: 493 | RouteTableId: !Ref NatRouteTable5 494 | SubnetId: !Ref DataSubnet5 495 | 496 | InternetGateway: 497 | Type: AWS::EC2::InternetGateway 498 | Properties: 499 | Tags: 500 | - Key: Name 501 | Value: !Join [ '', [ 'InternetGateway / ', !Ref 'AWS::StackName' ] ] 502 | AttachInternetGateway: 503 | Type: AWS::EC2::VPCGatewayAttachment 504 | Properties: 505 | InternetGatewayId: !Ref InternetGateway 506 | VpcId: !Ref Vpc 507 | 508 | NatEIP0: 509 | Condition: AZ0 510 | Type: AWS::EC2::EIP 511 | Properties: 512 | Domain: vpc 513 | NatGateway0: 514 | Condition: AZ0 515 | Type: AWS::EC2::NatGateway 516 | DependsOn: AttachInternetGateway 517 | Properties: 518 | AllocationId: !GetAtt NatEIP0.AllocationId 519 | SubnetId: !Ref PublicSubnet0 520 | NatRoute0: 521 | Condition: AZ0 522 | Type: AWS::EC2::Route 523 | Properties: 524 | RouteTableId: !Ref NatRouteTable0 525 | DestinationCidrBlock: 0.0.0.0/0 526 | NatGatewayId: !Ref NatGateway0 527 | NatRouteTable0: 528 | Condition: AZ0 529 | Type: AWS::EC2::RouteTable 530 | Properties: 531 | Tags: 532 | - Key: Name 533 | Value: !Join [ '', ['NatRouteTable0 / ', !Ref 'AWS::StackName' ] ] 534 | - Key: Network 535 | Value: Public 536 | VpcId: !Ref Vpc 537 | 538 | NatEIP1: 539 | Condition: AZ1 540 | Type: AWS::EC2::EIP 541 | Properties: 542 | Domain: vpc 543 | NatGateway1: 544 | Condition: AZ1 545 | Type: AWS::EC2::NatGateway 546 | DependsOn: AttachInternetGateway 547 | Properties: 548 | AllocationId: !GetAtt NatEIP1.AllocationId 549 | SubnetId: !Ref PublicSubnet1 550 | NatRoute1: 551 | Condition: AZ1 552 | Type: AWS::EC2::Route 553 | Properties: 554 | RouteTableId: !Ref NatRouteTable1 555 | DestinationCidrBlock: 0.0.0.0/0 556 | NatGatewayId: !Ref NatGateway1 557 | NatRouteTable1: 558 | Condition: AZ1 559 | Type: AWS::EC2::RouteTable 560 | Properties: 561 | Tags: 562 | - Key: Name 563 | Value: !Join [ '', [ 'NatRouteTable1 / ', !Ref 'AWS::StackName' ] ] 564 | - Key: Network 565 | Value: Public 566 | VpcId: !Ref Vpc 567 | 568 | NatEIP2: 569 | Condition: AZ2 570 | Type: AWS::EC2::EIP 571 | Properties: 572 | Domain: vpc 573 | NatGateway2: 574 | Condition: AZ2 575 | Type: AWS::EC2::NatGateway 576 | DependsOn: AttachInternetGateway 577 | Properties: 578 | AllocationId: !GetAtt NatEIP2.AllocationId 579 | SubnetId: !Ref PublicSubnet2 580 | NatRoute2: 581 | Condition: AZ2 582 | Type: AWS::EC2::Route 583 | Properties: 584 | RouteTableId: !Ref NatRouteTable2 585 | DestinationCidrBlock: 0.0.0.0/0 586 | NatGatewayId: !Ref NatGateway2 587 | NatRouteTable2: 588 | Condition: AZ2 589 | Type: AWS::EC2::RouteTable 590 | Properties: 591 | Tags: 592 | - Key: Name 593 | Value: !Join [ '', [ 'NatRouteTable2 / ', !Ref 'AWS::StackName' ] ] 594 | - Key: Network 595 | Value: Public 596 | VpcId: !Ref Vpc 597 | 598 | NatEIP3: 599 | Condition: AZ3 600 | Type: AWS::EC2::EIP 601 | Properties: 602 | Domain: vpc 603 | NatGateway3: 604 | Condition: AZ3 605 | Type: AWS::EC2::NatGateway 606 | DependsOn: AttachInternetGateway 607 | Properties: 608 | AllocationId: !GetAtt NatEIP3.AllocationId 609 | SubnetId: !Ref PublicSubnet3 610 | NatRoute3: 611 | Condition: AZ3 612 | Type: AWS::EC2::Route 613 | Properties: 614 | RouteTableId: !Ref NatRouteTable3 615 | DestinationCidrBlock: 0.0.0.0/0 616 | NatGatewayId: !Ref NatGateway3 617 | NatRouteTable3: 618 | Condition: AZ3 619 | Type: AWS::EC2::RouteTable 620 | Properties: 621 | Tags: 622 | - Key: Name 623 | Value: !Join [ '', [ 'NatRouteTable3 / ', !Ref 'AWS::StackName' ] ] 624 | - Key: Network 625 | Value: Public 626 | VpcId: !Ref Vpc 627 | 628 | NatEIP4: 629 | Condition: AZ4 630 | Type: AWS::EC2::EIP 631 | Properties: 632 | Domain: vpc 633 | NatGateway4: 634 | Condition: AZ4 635 | Type: AWS::EC2::NatGateway 636 | DependsOn: AttachInternetGateway 637 | Properties: 638 | AllocationId: !GetAtt NatEIP4.AllocationId 639 | SubnetId: !Ref PublicSubnet4 640 | NatRoute4: 641 | Condition: AZ4 642 | Type: AWS::EC2::Route 643 | Properties: 644 | RouteTableId: !Ref NatRouteTable4 645 | DestinationCidrBlock: 0.0.0.0/0 646 | NatGatewayId: !Ref NatGateway4 647 | NatRouteTable4: 648 | Condition: AZ4 649 | Type: AWS::EC2::RouteTable 650 | Properties: 651 | Tags: 652 | - Key: Name 653 | Value: !Join [ '', [ 'NatRouteTable4 / ', !Ref 'AWS::StackName' ] ] 654 | - Key: Network 655 | Value: Public 656 | VpcId: !Ref Vpc 657 | 658 | NatEIP5: 659 | Condition: AZ5 660 | Type: AWS::EC2::EIP 661 | Properties: 662 | Domain: vpc 663 | NatGateway5: 664 | Condition: AZ5 665 | Type: AWS::EC2::NatGateway 666 | DependsOn: AttachInternetGateway 667 | Properties: 668 | AllocationId: !GetAtt NatEIP5.AllocationId 669 | SubnetId: !Ref PublicSubnet5 670 | NatRoute5: 671 | Condition: AZ5 672 | Type: AWS::EC2::Route 673 | Properties: 674 | RouteTableId: !Ref NatRouteTable5 675 | DestinationCidrBlock: 0.0.0.0/0 676 | NatGatewayId: !Ref NatGateway5 677 | NatRouteTable5: 678 | Condition: AZ5 679 | Type: AWS::EC2::RouteTable 680 | Properties: 681 | Tags: 682 | - Key: Name 683 | Value: !Join [ '', [ 'NatRouteTable5 / ', !Ref 'AWS::StackName' ] ] 684 | - Key: Network 685 | Value: Public 686 | VpcId: !Ref Vpc 687 | 688 | PublicRoute: 689 | Type: AWS::EC2::Route 690 | DependsOn: AttachInternetGateway 691 | Properties: 692 | RouteTableId: !Ref PublicRouteTable 693 | DestinationCidrBlock: 0.0.0.0/0 694 | GatewayId: !Ref InternetGateway 695 | PublicRouteTable: 696 | Type: AWS::EC2::RouteTable 697 | Properties: 698 | Tags: 699 | - Key: Name 700 | Value: !Join [ '', [ 'PublicRouteTable / ', !Ref 'AWS::StackName' ] ] 701 | - Key: Network 702 | Value: Public 703 | VpcId: !Ref Vpc 704 | PublicRouteTableAssociation0: 705 | Condition: AZ0 706 | Type: AWS::EC2::SubnetRouteTableAssociation 707 | Properties: 708 | SubnetId: !Ref PublicSubnet0 709 | RouteTableId: !Ref PublicRouteTable 710 | PublicRouteTableAssociation1: 711 | Condition: AZ1 712 | Type: AWS::EC2::SubnetRouteTableAssociation 713 | Properties: 714 | SubnetId: !Ref PublicSubnet1 715 | RouteTableId: !Ref PublicRouteTable 716 | PublicRouteTableAssociation2: 717 | Condition: AZ2 718 | Type: AWS::EC2::SubnetRouteTableAssociation 719 | Properties: 720 | SubnetId: !Ref PublicSubnet2 721 | RouteTableId: !Ref PublicRouteTable 722 | PublicRouteTableAssociation3: 723 | Condition: AZ3 724 | Type: AWS::EC2::SubnetRouteTableAssociation 725 | Properties: 726 | SubnetId: !Ref PublicSubnet3 727 | RouteTableId: !Ref PublicRouteTable 728 | PublicRouteTableAssociation4: 729 | Condition: AZ4 730 | Type: AWS::EC2::SubnetRouteTableAssociation 731 | Properties: 732 | SubnetId: !Ref PublicSubnet4 733 | RouteTableId: !Ref PublicRouteTable 734 | PublicRouteTableAssociation5: 735 | Condition: AZ5 736 | Type: AWS::EC2::SubnetRouteTableAssociation 737 | Properties: 738 | SubnetId: !Ref PublicSubnet5 739 | RouteTableId: !Ref PublicRouteTable 740 | 741 | PublicSubnet0: 742 | Metadata: 743 | cfn_nag: 744 | rules_to_suppress: 745 | - id: W33 746 | reason: "This is a public subnet" 747 | Condition: AZ0 748 | Type: AWS::EC2::Subnet 749 | Properties: 750 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 751 | CidrBlock: !Ref PublicSubnet0Cidr 752 | MapPublicIpOnLaunch: true 753 | Tags: 754 | - Key: Name 755 | Value: !Join [ '', [ 'PublicSubnet0 / ', !Ref 'AWS::StackName' ] ] 756 | - Key: SubnetType 757 | Value: Public 758 | VpcId: !Ref Vpc 759 | PublicSubnet1: 760 | Metadata: 761 | cfn_nag: 762 | rules_to_suppress: 763 | - id: W33 764 | reason: "This is a public subnet" 765 | Condition: AZ1 766 | Type: AWS::EC2::Subnet 767 | Properties: 768 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 769 | CidrBlock: !Ref PublicSubnet1Cidr 770 | MapPublicIpOnLaunch: true 771 | Tags: 772 | - Key: Name 773 | Value: !Join [ '', [ 'PublicSubnet1 / ', !Ref 'AWS::StackName' ] ] 774 | - Key: SubnetType 775 | Value: Public 776 | VpcId: !Ref Vpc 777 | PublicSubnet2: 778 | Metadata: 779 | cfn_nag: 780 | rules_to_suppress: 781 | - id: W33 782 | reason: "This is a public subnet" 783 | Condition: AZ2 784 | Type: AWS::EC2::Subnet 785 | Properties: 786 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 787 | CidrBlock: !Ref PublicSubnet2Cidr 788 | MapPublicIpOnLaunch: true 789 | Tags: 790 | - Key: Name 791 | Value: !Join [ '', [ 'PublicSubnet2 / ', !Ref 'AWS::StackName' ] ] 792 | - Key: SubnetType 793 | Value: Public 794 | VpcId: !Ref Vpc 795 | PublicSubnet3: 796 | Metadata: 797 | cfn_nag: 798 | rules_to_suppress: 799 | - id: W33 800 | reason: "This is a public subnet" 801 | Condition: AZ3 802 | Type: AWS::EC2::Subnet 803 | Properties: 804 | AvailabilityZone: !Select [ 3, !Ref AvailabilityZones ] 805 | CidrBlock: !Ref PublicSubnet3Cidr 806 | MapPublicIpOnLaunch: true 807 | Tags: 808 | - Key: Name 809 | Value: !Join [ '', [ 'PublicSubnet3 / ', !Ref 'AWS::StackName' ] ] 810 | - Key: SubnetType 811 | Value: Public 812 | VpcId: !Ref Vpc 813 | PublicSubnet4: 814 | Metadata: 815 | cfn_nag: 816 | rules_to_suppress: 817 | - id: W33 818 | reason: "This is a public subnet" 819 | Condition: AZ4 820 | Type: AWS::EC2::Subnet 821 | Properties: 822 | AvailabilityZone: !Select [ 4, !Ref AvailabilityZones ] 823 | CidrBlock: !Ref PublicSubnet4Cidr 824 | MapPublicIpOnLaunch: true 825 | Tags: 826 | - Key: Name 827 | Value: !Join [ '', [ 'PublicSubnet4 / ', !Ref 'AWS::StackName' ] ] 828 | - Key: SubnetType 829 | Value: Public 830 | VpcId: !Ref Vpc 831 | PublicSubnet5: 832 | Metadata: 833 | cfn_nag: 834 | rules_to_suppress: 835 | - id: W33 836 | reason: "This is a public subnet" 837 | Condition: AZ5 838 | Type: AWS::EC2::Subnet 839 | Properties: 840 | AvailabilityZone: !Select [ 5, !Ref AvailabilityZones ] 841 | CidrBlock: !Ref PublicSubnet5Cidr 842 | MapPublicIpOnLaunch: true 843 | Tags: 844 | - Key: Name 845 | Value: !Join [ '', [ 'PublicSubnet5 / ', !Ref 'AWS::StackName' ] ] 846 | - Key: SubnetType 847 | Value: Public 848 | VpcId: !Ref Vpc 849 | 850 | Vpc: 851 | Type: AWS::EC2::VPC 852 | Properties: 853 | CidrBlock: !Ref VpcCidr 854 | EnableDnsHostnames: true 855 | EnableDnsSupport: true 856 | Tags: 857 | - Key: Name 858 | Value: !Join [ '', [ 'Vpc / ', !Ref 'AWS::StackName' ] ] 859 | VpcFlowLog: 860 | Type: AWS::EC2::FlowLog 861 | Properties: 862 | DeliverLogsPermissionArn: !GetAtt VpcFlowLogsRole.Arn 863 | LogGroupName: !Join [ '', [ !Ref 'AWS::StackName', '-FlowLog' ] ] 864 | ResourceId: !Ref Vpc 865 | ResourceType: VPC 866 | TrafficType: ALL 867 | VpcFlowLogsRole: 868 | Type: AWS::IAM::Role 869 | Properties: 870 | AssumeRolePolicyDocument: 871 | Version: 2012-10-17 872 | Statement: 873 | - Action: 874 | - sts:AssumeRole 875 | Effect: Allow 876 | Principal: 877 | Service: 878 | - vpc-flow-logs.amazonaws.com 879 | Path: '/' 880 | Policies: 881 | - PolicyName: root 882 | PolicyDocument: 883 | Version: 2012-10-17 884 | Statement: 885 | - Action: 886 | - logs:CreateLogGroup 887 | - logs:CreateLogStream 888 | - logs:DescribeLogGroups 889 | - logs:DescribeLogStreams 890 | - logs:PutLogEvents 891 | Effect: Allow 892 | Resource: 'arn:aws-cn:logs:*:*:*' 893 | 894 | Outputs: 895 | 896 | Vpc: 897 | Value: !Ref Vpc 898 | VpcCidr: 899 | Value: !Ref VpcCidr 900 | PublicSubnet0: 901 | Condition: AZ0 902 | Value: !Ref PublicSubnet0 903 | PublicSubnet1: 904 | Condition: AZ1 905 | Value: !Ref PublicSubnet1 906 | PublicSubnet2: 907 | Condition: AZ2 908 | Value: !Ref PublicSubnet2 909 | PublicSubnet3: 910 | Condition: AZ3 911 | Value: !Ref PublicSubnet3 912 | PublicSubnet4: 913 | Condition: AZ4 914 | Value: !Ref PublicSubnet4 915 | PublicSubnet5: 916 | Condition: AZ5 917 | Value: !Ref PublicSubnet5 918 | WebSubnet0: 919 | Condition: AZ0 920 | Value: !Ref WebSubnet0 921 | WebSubnet1: 922 | Condition: AZ1 923 | Value: !Ref WebSubnet1 924 | WebSubnet2: 925 | Condition: AZ2 926 | Value: !Ref WebSubnet2 927 | WebSubnet3: 928 | Condition: AZ3 929 | Value: !Ref WebSubnet3 930 | WebSubnet4: 931 | Condition: AZ4 932 | Value: !Ref WebSubnet4 933 | WebSubnet5: 934 | Condition: AZ5 935 | Value: !Ref WebSubnet5 936 | DataSubnet0: 937 | Condition: AZ0 938 | Value: !Ref DataSubnet0 939 | DataSubnet1: 940 | Condition: AZ1 941 | Value: !Ref DataSubnet1 942 | DataSubnet2: 943 | Condition: AZ2 944 | Value: !Ref DataSubnet2 945 | DataSubnet3: 946 | Condition: AZ3 947 | Value: !Ref DataSubnet3 948 | DataSubnet4: 949 | Condition: AZ4 950 | Value: !Ref DataSubnet4 951 | DataSubnet5: 952 | Condition: AZ5 953 | Value: !Ref DataSubnet5 954 | DataSubnet: 955 | Value: 956 | !If 957 | [ NumberOfAZs1, 958 | !Ref DataSubnet0, 959 | !If 960 | [ NumberOfAZs2, 961 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1 ] ], 962 | !If 963 | [ NumberOfAZs3, 964 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1, !Ref DataSubnet2 ] ], 965 | !If 966 | [ NumberOfAZs4, 967 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1, !Ref DataSubnet2, !Ref DataSubnet3 ] ], 968 | !If 969 | [ NumberOfAZs5, 970 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1, !Ref DataSubnet2, !Ref DataSubnet3, !Ref DataSubnet4 ] ], 971 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1, !Ref DataSubnet2, !Ref DataSubnet3, !Ref DataSubnet4, !Ref DataSubnet5 ] ] 972 | ] 973 | ] 974 | ] 975 | ] 976 | ] 977 | WebSubnet: 978 | Value: 979 | !If 980 | [ NumberOfAZs1, 981 | !Ref WebSubnet0, 982 | !If 983 | [ NumberOfAZs2, 984 | !Join [ ',', [ !Ref WebSubnet0, !Ref WebSubnet1 ] ], 985 | !If 986 | [ NumberOfAZs3, 987 | !Join [ ',', [ !Ref WebSubnet0, !Ref WebSubnet1, !Ref WebSubnet2 ] ], 988 | !If 989 | [ NumberOfAZs4, 990 | !Join [ ',', [ !Ref WebSubnet0, !Ref WebSubnet1, !Ref WebSubnet2, !Ref WebSubnet3 ] ], 991 | !If 992 | [ NumberOfAZs5, 993 | !Join [ ',', [ !Ref WebSubnet0, !Ref WebSubnet1, !Ref WebSubnet2, !Ref WebSubnet3, !Ref WebSubnet4 ] ], 994 | !Join [ ',', [ !Ref WebSubnet0, !Ref WebSubnet1, !Ref WebSubnet2, !Ref WebSubnet3, !Ref WebSubnet4, !Ref WebSubnet5 ] ] 995 | ] 996 | ] 997 | ] 998 | ] 999 | ] 1000 | PublicSubnet: 1001 | Value: 1002 | !If 1003 | [ NumberOfAZs1, 1004 | !Ref PublicSubnet0, 1005 | !If 1006 | [ NumberOfAZs2, 1007 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1 ] ], 1008 | !If 1009 | [ NumberOfAZs3, 1010 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1, !Ref PublicSubnet2 ] ], 1011 | !If 1012 | [ NumberOfAZs4, 1013 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1, !Ref PublicSubnet2, !Ref PublicSubnet3 ] ], 1014 | !If 1015 | [ NumberOfAZs5, 1016 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1, !Ref PublicSubnet2, !Ref PublicSubnet3, !Ref PublicSubnet4 ] ], 1017 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1, !Ref PublicSubnet2, !Ref PublicSubnet3, !Ref PublicSubnet4, !Ref PublicSubnet5 ] ] 1018 | ] 1019 | ] 1020 | ] 1021 | ] 1022 | ] 1023 | 1024 | -------------------------------------------------------------------------------- /deployment/02-securitygroups.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | 3 | Description: Reference Architecture to host Moodle on AWS - Creates VPC security groups 4 | 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: AWS Parameters 10 | Parameters: 11 | - SshAccessCidr 12 | - Vpc 13 | ParameterLabels: 14 | SshAccessCidr: 15 | default: SSH Access From 16 | Vpc: 17 | default: Vpc Id 18 | 19 | Parameters: 20 | SshAccessCidr: 21 | 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]))$ 22 | Description: The CIDR IP range that is permitted to SSH to bastion instance. Note - a value of 0.0.0.0/0 will allow access from ANY IP address. 23 | Type: String 24 | Default: 0.0.0.0/0 25 | Vpc: 26 | AllowedPattern: ^(vpc-)([a-z0-9]{8}|[a-z0-9]{17})$ 27 | Description: The VPC Id of an existing VPC. 28 | Type: AWS::EC2::VPC::Id 29 | 30 | Resources: 31 | 32 | BastionSecurityGroup: 33 | Metadata: 34 | cfn_nag: 35 | rules_to_suppress: 36 | - id: W40 37 | reason: Default egress rule 38 | - id: W5 39 | reason: Default egress rule 40 | Type: AWS::EC2::SecurityGroup 41 | Properties: 42 | GroupDescription: Security group for Bastion instances 43 | SecurityGroupIngress: 44 | - IpProtocol: tcp 45 | FromPort: 22 46 | ToPort: 22 47 | CidrIp: !Ref SshAccessCidr 48 | Description: SSH origin IP range 49 | SecurityGroupEgress: 50 | - IpProtocol: -1 51 | FromPort: -1 52 | ToPort: -1 53 | CidrIp: 0.0.0.0/0 54 | Description: To all 55 | VpcId: 56 | !Ref Vpc 57 | 58 | DatabaseSecurityGroup: 59 | Metadata: 60 | cfn_nag: 61 | rules_to_suppress: 62 | - id: W40 63 | reason: Default egress rule 64 | - id: W5 65 | reason: Default egress rule 66 | Type: "AWS::EC2::SecurityGroup" 67 | Properties: 68 | GroupDescription: Security group for Amazon RDS cluster 69 | SecurityGroupIngress: 70 | - IpProtocol: tcp 71 | FromPort: 3306 72 | ToPort: 3306 73 | SourceSecurityGroupId: !Ref WebSecurityGroup 74 | Description: From Web Security Group 75 | SecurityGroupEgress: 76 | - IpProtocol: -1 77 | FromPort: -1 78 | ToPort: -1 79 | CidrIp: 0.0.0.0/0 80 | Description: to all 81 | VpcId: !Ref Vpc 82 | 83 | ElastiCacheSecurityGroup: 84 | Metadata: 85 | cfn_nag: 86 | rules_to_suppress: 87 | - id: W40 88 | reason: Default egress rule 89 | - id: W5 90 | reason: Default egress rule 91 | Type: "AWS::EC2::SecurityGroup" 92 | Properties: 93 | GroupDescription: Security group for ElastiCache cache cluster 94 | SecurityGroupIngress: 95 | - IpProtocol: tcp 96 | FromPort: 6379 97 | ToPort: 6379 98 | SourceSecurityGroupId: !Ref WebSecurityGroup 99 | Description: From Web Security Group 100 | SecurityGroupEgress: 101 | - IpProtocol: -1 102 | FromPort: -1 103 | ToPort: -1 104 | CidrIp: 0.0.0.0/0 105 | Description: to all 106 | VpcId: !Ref Vpc 107 | 108 | EfsSecurityGroup: 109 | Metadata: 110 | cfn_nag: 111 | rules_to_suppress: 112 | - id: W40 113 | reason: Default egress rule 114 | - id: W5 115 | reason: Default egress rule 116 | Type: "AWS::EC2::SecurityGroup" 117 | Properties: 118 | GroupDescription: Security group for EFS cluster 119 | VpcId: !Ref Vpc 120 | SecurityGroupIngress: 121 | - IpProtocol: tcp 122 | FromPort: 2049 123 | ToPort: 2049 124 | SourceSecurityGroupId: !Ref WebSecurityGroup 125 | Description: From Web Security Group 126 | - IpProtocol: tcp 127 | FromPort: 22 128 | ToPort: 22 129 | SourceSecurityGroupId: !Ref BastionSecurityGroup 130 | Description: From Bastion 131 | SecurityGroupEgress: 132 | - IpProtocol: -1 133 | FromPort: -1 134 | ToPort: -1 135 | CidrIp: 0.0.0.0/0 136 | Description: to all 137 | EfsSecurityGroupIngress: 138 | Type: "AWS::EC2::SecurityGroupIngress" 139 | Properties: 140 | IpProtocol: tcp 141 | FromPort: 2049 142 | ToPort: 2049 143 | SourceSecurityGroupId: !GetAtt EfsSecurityGroup.GroupId 144 | GroupId: !GetAtt EfsSecurityGroup.GroupId 145 | Description: From self 146 | 147 | PublicAlbSecurityGroup: 148 | Metadata: 149 | cfn_nag: 150 | rules_to_suppress: 151 | - id: W9 152 | reason: "This is a security group for public facing ALB" 153 | - id: W2 154 | reason: "This is a security group for public facing ALB" 155 | - id: W40 156 | reason: "Default egress rule" 157 | - id: W5 158 | reason: "Default egress rule" 159 | Type: AWS::EC2::SecurityGroup 160 | Properties: 161 | GroupDescription: Security group for ALB 162 | SecurityGroupIngress: 163 | - IpProtocol: tcp 164 | FromPort: 443 165 | ToPort: 443 166 | CidrIp: 0.0.0.0/0 167 | Description: From public 168 | - IpProtocol: tcp 169 | FromPort: 80 170 | ToPort: 80 171 | CidrIp: 0.0.0.0/0 172 | Description: From public 173 | SecurityGroupEgress: 174 | - IpProtocol: -1 175 | FromPort: -1 176 | ToPort: -1 177 | CidrIp: 0.0.0.0/0 178 | Description: to all 179 | VpcId: 180 | !Ref Vpc 181 | 182 | WebSecurityGroup: 183 | Metadata: 184 | cfn_nag: 185 | rules_to_suppress: 186 | - id: W40 187 | reason: Default egress rule 188 | - id: W5 189 | reason: Default egress rule 190 | Type: AWS::EC2::SecurityGroup 191 | Properties: 192 | GroupDescription: Security group for web instances 193 | SecurityGroupIngress: 194 | - IpProtocol: tcp 195 | FromPort: 80 196 | ToPort: 80 197 | SourceSecurityGroupId: !Ref PublicAlbSecurityGroup 198 | Description: From public ALB 199 | - IpProtocol: tcp 200 | FromPort: 443 201 | ToPort: 443 202 | SourceSecurityGroupId: !Ref PublicAlbSecurityGroup 203 | Description: From public ALB 204 | - IpProtocol: tcp 205 | FromPort: 22 206 | ToPort: 22 207 | SourceSecurityGroupId: !Ref BastionSecurityGroup 208 | Description: From Bastion 209 | SecurityGroupEgress: 210 | - IpProtocol: -1 211 | FromPort: -1 212 | ToPort: -1 213 | CidrIp: 0.0.0.0/0 214 | Description: To all 215 | VpcId: 216 | !Ref Vpc 217 | 218 | Outputs: 219 | BastionSecurityGroup: 220 | Value: !Ref BastionSecurityGroup 221 | DatabaseSecurityGroup: 222 | Value: !Ref DatabaseSecurityGroup 223 | EfsSecurityGroup: 224 | Value: !Ref EfsSecurityGroup 225 | ElastiCacheSecurityGroup: 226 | Value: !Ref ElastiCacheSecurityGroup 227 | PublicAlbSecurityGroup: 228 | Value: !Ref PublicAlbSecurityGroup 229 | WebSecurityGroup: 230 | Value: !Ref WebSecurityGroup -------------------------------------------------------------------------------- /deployment/03-bastion.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates bastion (desired:0; min:0; max:1) Auto Scaling group 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: AWS Parameters 12 | Parameters: 13 | - EC2KeyName 14 | - BastionInstanceType 15 | - BastionSecurityGroup 16 | - NumberOfSubnets 17 | - Subnet 18 | ParameterLabels: 19 | BastionSecurityGroup: 20 | default: Bastion Security Group 21 | BastionInstanceType: 22 | default: Instance Type 23 | EC2KeyName: 24 | default: Existing Key Pair 25 | NumberOfSubnets: 26 | default: Number of subnets 27 | Subnet: 28 | default: Subnets 29 | 30 | Parameters: 31 | 32 | BastionSecurityGroup: 33 | Description: Select the bastion security group. 34 | Type: AWS::EC2::SecurityGroup::Id 35 | BastionInstanceType: 36 | AllowedValues: 37 | - t2.nano 38 | - t2.micro 39 | - t2.small 40 | - t2.medium 41 | - t2.large 42 | - t2.xlarge 43 | - t2.2xlarge 44 | - m4.large 45 | - m4.xlarge 46 | - m4.2xlarge 47 | - m4.4xlarge 48 | - m4.10xlarge 49 | - m4.16xlarge 50 | - m5.large 51 | - m5.xlarge 52 | - m5.2xlarge 53 | - m5.4xlarge 54 | - m5.12xlarge 55 | - m5.24xlarge 56 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 57 | Default: t2.nano 58 | Description: Bastion EC2 instance type. 59 | Type: String 60 | EC2KeyName: 61 | Description: Name of an EC2 KeyPair. Your bastion instances will launch with this KeyPair. 62 | Type: AWS::EC2::KeyPair::KeyName 63 | NumberOfSubnets: 64 | AllowedValues: 65 | - 2 66 | - 3 67 | - 4 68 | - 5 69 | - 6 70 | Default: 3 71 | Description: Number of subnets. This must match your selections in the list of subnets below. 72 | Type: String 73 | Subnet: 74 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 75 | Type: List 76 | LatestAmiId : 77 | Type : AWS::SSM::Parameter::Value 78 | Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 79 | 80 | 81 | Conditions: 82 | 83 | NumberOfSubnets1: 84 | !Equals [ 1, !Ref NumberOfSubnets ] 85 | NumberOfSubnets2: 86 | !Equals [ 2, !Ref NumberOfSubnets ] 87 | NumberOfSubnets3: 88 | !Equals [ 3, !Ref NumberOfSubnets ] 89 | NumberOfSubnets4: 90 | !Equals [ 4, !Ref NumberOfSubnets ] 91 | NumberOfSubnets5: 92 | !Equals [ 5, !Ref NumberOfSubnets ] 93 | NumberOfSubnets6: 94 | !Equals [ 6, !Ref NumberOfSubnets ] 95 | Subnet0: !Or 96 | - !Condition NumberOfSubnets1 97 | - !Condition NumberOfSubnets2 98 | - !Condition NumberOfSubnets3 99 | - !Condition NumberOfSubnets4 100 | - !Condition NumberOfSubnets5 101 | - !Condition NumberOfSubnets6 102 | Subnet1: !Or 103 | - !Condition NumberOfSubnets2 104 | - !Condition NumberOfSubnets3 105 | - !Condition NumberOfSubnets4 106 | - !Condition NumberOfSubnets5 107 | - !Condition NumberOfSubnets6 108 | Subnet2: !Or 109 | - !Condition NumberOfSubnets3 110 | - !Condition NumberOfSubnets4 111 | - !Condition NumberOfSubnets5 112 | - !Condition NumberOfSubnets6 113 | Subnet3: !Or 114 | - !Condition NumberOfSubnets4 115 | - !Condition NumberOfSubnets5 116 | - !Condition NumberOfSubnets6 117 | Subnet4: !Or 118 | - !Condition NumberOfSubnets5 119 | - !Condition NumberOfSubnets6 120 | Subnet5: !Condition NumberOfSubnets6 121 | 122 | 123 | Resources: 124 | 125 | BastionAutoScalingGroup: 126 | Type: AWS::AutoScaling::AutoScalingGroup 127 | Properties: 128 | Cooldown: 60 129 | HealthCheckGracePeriod: 120 130 | HealthCheckType: EC2 131 | LaunchConfigurationName: !Ref BastionLaunchConfiguration 132 | MaxSize: 1 133 | MinSize: 0 134 | Tags: 135 | - Key: Name 136 | Value: !Join [ '', [ 'Bastion / ', !Ref 'AWS::StackName' ] ] 137 | PropagateAtLaunch: true 138 | VPCZoneIdentifier: 139 | !If 140 | [ NumberOfSubnets1, 141 | [ !Select [ 0, !Ref Subnet ] ], 142 | !If 143 | [ NumberOfSubnets2, 144 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 145 | !If 146 | [ NumberOfSubnets3, 147 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ], 148 | !If 149 | [ NumberOfSubnets4, 150 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ] ], 151 | !If 152 | [ NumberOfSubnets5, 153 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ] ], 154 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ], !Select [ 5, !Ref Subnet ] ] 155 | ] 156 | ] 157 | ] 158 | ] 159 | ] 160 | BastionLaunchConfiguration: 161 | Type: AWS::AutoScaling::LaunchConfiguration 162 | Properties: 163 | IamInstanceProfile: !Ref BastionInstanceProfile 164 | ImageId: !Ref LatestAmiId 165 | InstanceMonitoring: true 166 | InstanceType: !Ref BastionInstanceType 167 | KeyName: !Ref EC2KeyName 168 | SecurityGroups: 169 | - !Ref BastionSecurityGroup 170 | BastionInstanceProfile: 171 | Type: AWS::IAM::InstanceProfile 172 | Properties: 173 | Path: '/' 174 | Roles: 175 | - !Ref BastionInstanceRole 176 | BastionInstanceRole: 177 | Type: AWS::IAM::Role 178 | Properties: 179 | AssumeRolePolicyDocument: 180 | Version: 2012-10-17 181 | Statement: 182 | - Effect: Allow 183 | Principal: 184 | Service: 185 | - ec2.amazonaws.com.cn 186 | Action: 187 | - sts:AssumeRole 188 | Path: '/' 189 | Policies: 190 | - PolicyName: logs 191 | PolicyDocument: 192 | Version: 2012-10-17 193 | Statement: 194 | - Effect: Allow 195 | Action: 196 | - logs:CreateLogGroup 197 | - logs:CreateLogStream 198 | - logs:PutLogEvents 199 | - logs:DescribeLogStreams 200 | Resource: 201 | - arn:aws-cn:logs:*:*:* 202 | -------------------------------------------------------------------------------- /deployment/03-efsfilesystem.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates EFS file system 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | 10 | ParameterGroups: 11 | - Label: 12 | default: Amazon EFS Parameters 13 | Parameters: 14 | - Growth 15 | - InstanceType 16 | - EC2KeyName 17 | - PerformanceMode 18 | - EncrpytedBoolean 19 | - SecurityGroup 20 | - NumberOfSubnets 21 | - Subnet 22 | ParameterLabels: 23 | EncrpytedBoolean: 24 | default: Encryption state 25 | Growth: 26 | default: Add data (GiB) 27 | InstanceType: 28 | default: Instance Type 29 | EC2KeyName: 30 | default: Existing Key Pair 31 | NumberOfSubnets: 32 | default: Number of subnets 33 | PerformanceMode: 34 | default: Performance Mode 35 | SecurityGroup: 36 | default: EFS Security Group 37 | Subnet: 38 | default: Subnets 39 | 40 | Parameters: 41 | 42 | EncrpytedBoolean: 43 | AllowedValues: 44 | - True 45 | - False 46 | Default: True 47 | Description: Create an encrypted Amazon EFS file system. 48 | Type: String 49 | Growth: 50 | ConstraintDescription: Must be an integer. 51 | Default: 0 52 | Description: Amount of dummy data (GiB) to add to the file system (max 6144 GiB). Amazon EFS storage charges apply. 53 | MaxValue: 6144 54 | MinValue: 0 55 | Type: Number 56 | InstanceType: 57 | AllowedValues: 58 | - t2.medium 59 | - t2.large 60 | - t2.xlarge 61 | - t2.2xlarge 62 | - m4.large 63 | - m4.xlarge 64 | - m4.2xlarge 65 | - m4.4xlarge 66 | - m4.10xlarge 67 | - m4.16xlarge 68 | - m5.large 69 | - m5.xlarge 70 | - m5.2xlarge 71 | - m5.4xlarge 72 | - m5.12xlarge 73 | - m5.24xlarge 74 | - c4.large 75 | - c4.xlarge 76 | - c4.2xlarge 77 | - c4.4xlarge 78 | - c4.8xlarge 79 | - c5.large 80 | - c5.xlarge 81 | - c5.2xlarge 82 | - c5.4xlarge 83 | - c5.9xlarge 84 | - c5.18xlarge 85 | - r4.large 86 | - r4.xlarge 87 | - r4.2xlarge 88 | - r4.4xlarge 89 | - r4.8xlarge 90 | - r4.16xlarge 91 | - r5.large 92 | - r5.2xlarge 93 | - r5.4xlarge 94 | - r5.8xlarge 95 | - r5.12xlarge 96 | - r5.16xlarge 97 | - r5.24xlarge 98 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 99 | Default: r4.large 100 | Description: The Amazon EC2 instance type that adds data to the file system. 101 | Type: String 102 | EC2KeyName: 103 | Description: Name of an existing EC2 key pair 104 | Type: AWS::EC2::KeyPair::KeyName 105 | NumberOfSubnets: 106 | AllowedValues: 107 | - 2 108 | - 3 109 | - 4 110 | - 5 111 | - 6 112 | Default: 3 113 | Description: Number of subnets. This must match your selections in the list of Subnets below. 114 | Type: String 115 | PerformanceMode: 116 | AllowedValues: 117 | - generalPurpose 118 | - maxIO 119 | Default: generalPurpose 120 | Description: Select the performance mode of the file system. 121 | Type: String 122 | SecurityGroup: 123 | Description: Select the Amazon EFS security group. 124 | Type: AWS::EC2::SecurityGroup::Id 125 | Subnet: 126 | Description: Select existing subnets. 127 | Type: List 128 | LatestAmiId : 129 | Type : AWS::SSM::Parameter::Value 130 | Default: /aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2 131 | 132 | 133 | Conditions: 134 | 135 | NumberOfSubnets1: 136 | !Equals [ 1, !Ref NumberOfSubnets ] 137 | NumberOfSubnets2: 138 | !Equals [ 2, !Ref NumberOfSubnets ] 139 | NumberOfSubnets3: 140 | !Equals [ 3, !Ref NumberOfSubnets ] 141 | NumberOfSubnets4: 142 | !Equals [ 4, !Ref NumberOfSubnets ] 143 | NumberOfSubnets5: 144 | !Equals [ 5, !Ref NumberOfSubnets ] 145 | NumberOfSubnets6: 146 | !Equals [ 6, !Ref NumberOfSubnets ] 147 | Subnet0: !Or 148 | - !Condition NumberOfSubnets1 149 | - !Condition NumberOfSubnets2 150 | - !Condition NumberOfSubnets3 151 | - !Condition NumberOfSubnets4 152 | - !Condition NumberOfSubnets5 153 | - !Condition NumberOfSubnets6 154 | Subnet1: !Or 155 | - !Condition NumberOfSubnets2 156 | - !Condition NumberOfSubnets3 157 | - !Condition NumberOfSubnets4 158 | - !Condition NumberOfSubnets5 159 | - !Condition NumberOfSubnets6 160 | Subnet2: !Or 161 | - !Condition NumberOfSubnets3 162 | - !Condition NumberOfSubnets4 163 | - !Condition NumberOfSubnets5 164 | - !Condition NumberOfSubnets6 165 | Subnet3: !Or 166 | - !Condition NumberOfSubnets4 167 | - !Condition NumberOfSubnets5 168 | - !Condition NumberOfSubnets6 169 | Subnet4: !Or 170 | - !Condition NumberOfSubnets5 171 | - !Condition NumberOfSubnets6 172 | Subnet5: !Condition NumberOfSubnets6 173 | 174 | 175 | Resources: 176 | 177 | ElasticFileSystem: 178 | Type: AWS::EFS::FileSystem 179 | Properties: 180 | Encrypted: !Ref EncrpytedBoolean 181 | FileSystemTags: 182 | - Key: Name 183 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 184 | PerformanceMode: !Ref PerformanceMode 185 | ElasticFileSystemMountTarget0: 186 | Condition: Subnet0 187 | Type: AWS::EFS::MountTarget 188 | Properties: 189 | FileSystemId: !Ref ElasticFileSystem 190 | SecurityGroups: 191 | - !Ref SecurityGroup 192 | SubnetId: !Select [ 0, !Ref Subnet ] 193 | ElasticFileSystemMountTarget1: 194 | Condition: Subnet1 195 | Type: AWS::EFS::MountTarget 196 | Properties: 197 | FileSystemId: !Ref ElasticFileSystem 198 | SecurityGroups: 199 | - !Ref SecurityGroup 200 | SubnetId: !Select [ 1, !Ref Subnet ] 201 | ElasticFileSystemMountTarget2: 202 | Condition: Subnet2 203 | Type: AWS::EFS::MountTarget 204 | Properties: 205 | FileSystemId: !Ref ElasticFileSystem 206 | SecurityGroups: 207 | - !Ref SecurityGroup 208 | SubnetId: !Select [ 2, !Ref Subnet ] 209 | ElasticFileSystemMountTarget3: 210 | Condition: Subnet3 211 | Type: AWS::EFS::MountTarget 212 | Properties: 213 | FileSystemId: !Ref ElasticFileSystem 214 | SecurityGroups: 215 | - !Ref SecurityGroup 216 | SubnetId: !Select [ 3, !Ref Subnet ] 217 | ElasticFileSystemMountTarget4: 218 | Condition: Subnet4 219 | Type: AWS::EFS::MountTarget 220 | Properties: 221 | FileSystemId: !Ref ElasticFileSystem 222 | SecurityGroups: 223 | - !Ref SecurityGroup 224 | SubnetId: !Select [ 4, !Ref Subnet ] 225 | ElasticFileSystemMountTarget5: 226 | Condition: Subnet5 227 | Type: AWS::EFS::MountTarget 228 | Properties: 229 | FileSystemId: !Ref ElasticFileSystem 230 | SecurityGroups: 231 | - !Ref SecurityGroup 232 | SubnetId: !Select [ 5, !Ref Subnet ] 233 | InstanceProfile: 234 | Type: AWS::IAM::InstanceProfile 235 | Properties: 236 | Path: / 237 | Roles: 238 | - !Ref InstanceRole 239 | InstanceRole: 240 | Type: AWS::IAM::Role 241 | Properties: 242 | AssumeRolePolicyDocument: 243 | Version: 2012-10-17 244 | Statement: 245 | - Effect: Allow 246 | Principal: 247 | Service: 248 | - ec2.amazonaws.com.cn 249 | Action: 250 | - sts:AssumeRole 251 | Path: / 252 | Policies: 253 | - PolicyName: efs-create-file-system-with-storage 254 | PolicyDocument: 255 | Version: 2012-10-17 256 | Statement: 257 | - Effect: Allow 258 | Action: 259 | - autoscaling:DescribeAutoScalingGroups 260 | - autoscaling:DescribeAutoScalingInstances 261 | - autoscaling:DescribePolicies 262 | - autoscaling:UpdateAutoScalingGroup 263 | Resource: 'arn:aws-cn:autoscaling:*:*:*:*' 264 | AutoScalingGroup: 265 | Type: AWS::AutoScaling::AutoScalingGroup 266 | Properties: 267 | Cooldown: 60 268 | HealthCheckGracePeriod: 120 269 | HealthCheckType: EC2 270 | LaunchConfigurationName: !Ref LaunchConfiguration 271 | MaxSize: 1 272 | MinSize: 0 273 | DesiredCapacity: 1 274 | Tags: 275 | - Key: Name 276 | Value: !Join [ '', [ 'EFS ', !Ref ElasticFileSystem, ' data load... will auto terminate.' ] ] 277 | PropagateAtLaunch: true 278 | VPCZoneIdentifier: 279 | !If 280 | [ NumberOfSubnets1, 281 | [ !Select [ 0, !Ref Subnet ] ], 282 | !If 283 | [ NumberOfSubnets2, 284 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 285 | !If 286 | [ NumberOfSubnets3, 287 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ], 288 | !If 289 | [ NumberOfSubnets4, 290 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ] ], 291 | !If 292 | [ NumberOfSubnets5, 293 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ] ], 294 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ], !Select [ 5, !Ref Subnet ] ] 295 | ] 296 | ] 297 | ] 298 | ] 299 | ] 300 | CreationPolicy: 301 | ResourceSignal: 302 | Count: 0 303 | Timeout: PT12H 304 | UpdatePolicy: 305 | AutoScalingReplacingUpdate: 306 | WillReplace: true 307 | AutoScalingPolicy: 308 | Type: AWS::AutoScaling::ScalingPolicy 309 | Properties: 310 | AdjustmentType: ChangeInCapacity 311 | AutoScalingGroupName: !Ref AutoScalingGroup 312 | Cooldown: 60 313 | PolicyType: SimpleScaling 314 | ScalingAdjustment: 1 315 | LaunchConfiguration: 316 | Type: AWS::AutoScaling::LaunchConfiguration 317 | Metadata: 318 | AWS::CloudFormation::Init: 319 | configSets: 320 | efs_init: 321 | - install_efs_utils 322 | - efs-add-storage 323 | efs-add-storage: 324 | packages: 325 | yum: 326 | #Only needed to set permissions on the moodle folder 327 | httpd: [] 328 | files: 329 | /tmp/efs-add-storage.sh: 330 | content: !Sub | 331 | #!/bin/bash -x 332 | 333 | FILE_SYSTEM_ID=$1 334 | DATA_DIRECTORY=$2 335 | GROWTH=$3 336 | 337 | if [ $# -lt 3 ]; then 338 | echo "Invalid # of arguments. Require: file system id, data directory, file system growth (GiB) " 339 | exit 0 340 | fi 341 | 342 | # get region from instance meta-data 343 | availabilityzone=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone) 344 | region=${!availabilityzone:0:-1} 345 | 346 | # get instance id 347 | instance_id=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) 348 | 349 | # get autoscaling group name 350 | asg_name=$(aws autoscaling describe-auto-scaling-instances --instance-ids $instance_id --region $region --output text --query 'AutoScalingInstances[0].AutoScalingGroupName') 351 | 352 | # set the number of threads to the number of vcpus 353 | threads=$(( $(nproc --all) * 8 )) 354 | 355 | # wait for file system DNS name to be propagated 356 | results=1 357 | while [[ $results != 0 ]]; do 358 | nslookup $FILE_SYSTEM_ID.efs.$region.amazonaws.com.cn 359 | results=$? 360 | if [[ results = 1 ]]; then 361 | sleep 30 362 | fi 363 | done 364 | 365 | # mount file system 366 | sudo mkdir -p /$FILE_SYSTEM_ID 367 | sudo chown ec2-user:ec2-user /$FILE_SYSTEM_ID 368 | sudo mount -t efs $FILE_SYSTEM_ID:/ /$FILE_SYSTEM_ID 369 | #Create directories for Moodle 370 | sudo mkdir -p /$FILE_SYSTEM_ID/data 371 | sudo mkdir -p /$FILE_SYSTEM_ID/cache 372 | sudo mkdir -p /$FILE_SYSTEM_ID/temp 373 | chown apache:apache /$FILE_SYSTEM_ID/data/ 374 | chown apache:apache /$FILE_SYSTEM_ID/cache/ 375 | chown apache:apache /$FILE_SYSTEM_ID/temp/ 376 | 377 | # create data directory if not exists 378 | sudo mkdir -p /$FILE_SYSTEM_ID/$DATA_DIRECTORY 379 | sudo chown ec2-user:ec2-user /$FILE_SYSTEM_ID/$DATA_DIRECTORY 380 | 381 | # dd 1GiB files to file system to match DATA_SIZE 382 | files=$GROWTH 383 | if [ $(( $files / $threads )) == 0 ]; 384 | then 385 | runs=0 386 | parallel_threads=$(( $files % $threads )) 387 | else 388 | runs=$(( $files / $threads )) 389 | parallel_threads=$threads 390 | fi 391 | while [ $runs -ge 0 ]; do 392 | if [ $runs == 0 ]; 393 | then 394 | parallel_threads=$(( $files % $threads )) 395 | seq 0 $(( $parallel_threads - 1 )) | parallel --will-cite -j $parallel_threads --compress dd if=/dev/zero of=/$FILE_SYSTEM_ID/$DATA_DIRECTORY/1G-dd-$(date +%Y%m%d%H%M%S.%3N)-{} bs=1M count=1024 oflag=sync 396 | runs=$(($runs-1)) 397 | else 398 | seq 0 $(( $parallel_threads - 1 )) | parallel --will-cite -j $parallel_threads --compress dd if=/dev/zero of=/$FILE_SYSTEM_ID/$DATA_DIRECTORY/1G-dd-$(date +%Y%m%d%H%M%S.%3N)-{} bs=1M count=1024 oflag=sync 399 | runs=$(($runs-1)) 400 | fi 401 | done 402 | 403 | # set ASG to zero which terminates instance 404 | aws autoscaling update-auto-scaling-group --auto-scaling-group-name $asg_name --desired-capacity 0 --region $region 405 | 406 | mode: 000777 407 | owner: root 408 | group: root 409 | 410 | install_efs_utils: 411 | packages: 412 | yum: 413 | git: [] 414 | rpm-build: [] 415 | files: 416 | /tmp/install_efs_utils.sh: 417 | content: !Sub | 418 | #!/bin/bash -xe 419 | git clone https://github.com/aws/efs-utils.git 420 | cd efs-utils 421 | make rpm 422 | yum -y install build/amazon-efs-utils*rpm 423 | mode: 000500 424 | owner: root 425 | group: root 426 | commands: 427 | install_efs_utils: 428 | command: ./install_efs_utils.sh 429 | cwd: /tmp 430 | ignoreErrors: false 431 | 432 | Properties: 433 | BlockDeviceMappings: 434 | - DeviceName: /dev/xvda 435 | Ebs: 436 | DeleteOnTermination: true 437 | VolumeSize: 10 438 | VolumeType: gp2 439 | IamInstanceProfile: !Ref InstanceProfile 440 | ImageId: !Ref LatestAmiId 441 | InstanceMonitoring: true 442 | InstanceType: !Ref InstanceType 443 | KeyName: !Ref EC2KeyName 444 | SecurityGroups: 445 | - !Ref SecurityGroup 446 | UserData: 447 | "Fn::Base64": 448 | !Join [ 449 | "",[ 450 | "#cloud-config\n", 451 | "repo_update: true\n", 452 | "repo_upgrade: all\n", 453 | "\n", 454 | "packages:\n", 455 | "- parallel\n", 456 | "\n", 457 | "runcmd:\n", 458 | "- yum --enablerepo=epel install nload -y\n", 459 | "- ntpstat\n", 460 | "- /opt/aws/bin/cfn-init --configsets efs_init --verbose --stack ", !Ref 'AWS::StackName', " --resource LaunchConfiguration --region ", !Ref 'AWS::Region',"\n", 461 | "- /tmp/efs-add-storage.sh ", !Ref ElasticFileSystem, " throughput_data ", !Ref Growth,"\n", 462 | "- /opt/aws/bin/cfn-signal -e $? --stack ", !Ref 'AWS::StackName', " --resource AutoScalingGroup --region ", !Ref 'AWS::Region',"\n" 463 | ] 464 | ] 465 | EfsSizeMonitorFunction: 466 | Metadata: 467 | cfn_nag: 468 | rules_to_suppress: 469 | - id: W58 470 | reason: Attached AWS-managed Policies 471 | Type: AWS::Lambda::Function 472 | Properties: 473 | Code: 474 | ZipFile: !Sub | 475 | import boto3 476 | import os 477 | import sys 478 | 479 | def handler(event, context): 480 | if not os.environ.get('filesystemid'): 481 | print "Unable to get the environment variable filesystemid" 482 | sys.exit(1) 483 | else: 484 | filesystemid = os.environ.get('filesystemid') 485 | 486 | if not os.environ.get('region'): 487 | print "Unable to get the environment variable region" 488 | sys.exit(1) 489 | else: 490 | region = os.environ.get('region') 491 | 492 | def efs_get_size(): 493 | client = boto3.client('efs') 494 | response = client.describe_file_systems(FileSystemId=filesystemid) 495 | k = response['FileSystems'][0]['SizeInBytes']['Value'] 496 | return k 497 | 498 | def cloudwatch_put_metric(): 499 | client = boto3.client('cloudwatch') 500 | client.put_metric_data( 501 | MetricData=[ 502 | { 503 | 'MetricName': 'SizeInBytes', 504 | 'Dimensions': [ 505 | { 506 | 'Name': 'FileSystemId', 507 | 'Value': filesystemid 508 | }, 509 | ], 510 | 'Unit': 'None', 511 | 'Value': efs_get_size() 512 | }, 513 | ], 514 | Namespace='Custom/EFS' 515 | ) 516 | print('CloudWatch metric SizeInBytes sucessfully updated.') 517 | 518 | cloudwatch_put_metric() 519 | Description: Lambda function to update the SizeInBytes EFS CloudWatch metric 520 | Environment: 521 | Variables: 522 | filesystemid: !Ref ElasticFileSystem 523 | region: !Ref 'AWS::Region' 524 | FunctionName: !Join [ '', [ 'efs-', !Ref ElasticFileSystem, '-size-monitor' ] ] 525 | Handler: index.handler 526 | Role: !GetAtt LambdaRole.Arn 527 | Runtime: python2.7 528 | Timeout: 60 529 | LambdaRole: 530 | Type: AWS::IAM::Role 531 | Properties: 532 | AssumeRolePolicyDocument: 533 | Version: 2012-10-17 534 | Statement: 535 | - Effect: Allow 536 | Principal: 537 | Service: 538 | - lambda.amazonaws.com 539 | Action: 540 | - sts:AssumeRole 541 | Path: / 542 | ManagedPolicyArns: 543 | - arn:aws-cn:iam::aws:policy/CloudWatchFullAccess 544 | - arn:aws-cn:iam::aws:policy/AmazonElasticFileSystemReadOnlyAccess 545 | EfsLambdaPermission: 546 | Type: AWS::Lambda::Permission 547 | Properties: 548 | FunctionName: !Ref EfsSizeMonitorFunction 549 | Action: lambda:InvokeFunction 550 | Principal: events.amazonaws.com 551 | SourceArn: !GetAtt EfsSizeMonitorEvent.Arn 552 | EfsSizeMonitorEvent: 553 | Type: AWS::Events::Rule 554 | Properties: 555 | Description: Scheduled event to update SizeInBytes EFS CloudWatch metric 556 | Name: !Join [ '', [ 'efs-', !Ref ElasticFileSystem, '-size-monitor-scheduled-event' ] ] 557 | ScheduleExpression: rate(1 minute) 558 | State: ENABLED 559 | Targets: 560 | - Arn: !GetAtt EfsSizeMonitorFunction.Arn 561 | Id: 1 562 | 563 | Outputs: 564 | ElasticFileSystem: 565 | Value: !Ref ElasticFileSystem 566 | ElasticFileSystemDnsName: 567 | Description: DNS name for the Amazon EFS file system. 568 | Value: !Join [ '.', [ !Ref ElasticFileSystem, 'efs', !Ref 'AWS::Region', 'amazonaws', 'com.cn' ] ] 569 | -------------------------------------------------------------------------------- /deployment/03-elasticache.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates ElastiCache cache cluster 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: Cache Parameters 12 | Parameters: 13 | - ElastiCacheNodeType 14 | - ElastiCacheClusterName 15 | - ElastiCacheSecurityGroup 16 | - NumberOfSubnets 17 | - Subnet 18 | ParameterLabels: 19 | ElastiCacheClusterName: 20 | default: Cache Cluster Name 21 | ElastiCacheNodeType: 22 | default: Cache Cluster Node Type 23 | ElastiCacheSecurityGroup: 24 | default: ElastiCache Security Group 25 | NumberOfSubnets: 26 | default: Number of subnets 27 | Subnet: 28 | default: Subnets 29 | 30 | Parameters: 31 | 32 | ElastiCacheClusterName: 33 | AllowedPattern: ^[a-zA-Z]{1}[0-9a-zA-Z\-]{1,38}[^\-]{1}$ 34 | Description: The ElastiCache cluster name. 35 | Type: String 36 | ElastiCacheNodeType: 37 | AllowedValues: 38 | - cache.r4.large 39 | - cache.r4.xlarge 40 | - cache.r4.2xlarge 41 | - cache.r4.4xlarge 42 | - cache.r4.8xlarge 43 | - cache.r4.16xlarge 44 | - cache.r5.large 45 | - cache.r5.xlarge 46 | - cache.r5.2xlarge 47 | - cache.r5.4xlarge 48 | - cache.r5.12xlarge 49 | - cache.r5.24xlarge 50 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 51 | Default: cache.r5.large 52 | Description: The Amazon ElastiCache cluster node type. 53 | Type: String 54 | ElastiCacheSecurityGroup: 55 | Description: Select the ElastiCache security group. 56 | Type: AWS::EC2::SecurityGroup::Id 57 | NumberOfSubnets: 58 | AllowedValues: 59 | - 2 60 | - 3 61 | Default: 3 62 | Description: Number of subnets. This must match your selections in the list of subnets below. 63 | Type: String 64 | Subnet: 65 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 66 | Type: List 67 | 68 | Conditions: 69 | HasThreeAZs: 70 | !Not [!Equals [2, !Ref NumberOfSubnets]] 71 | 72 | Resources: 73 | 74 | ElastiCacheReplicationGroup: 75 | Metadata: 76 | cfn_nag: 77 | rules_to_suppress: 78 | - id: F33 79 | reason: Use Redis without encryption in transit 80 | - id: F25 81 | reason: Not supported in AWS China regions 82 | Type: "AWS::ElastiCache::ReplicationGroup" 83 | Properties: 84 | CacheNodeType: !Ref ElastiCacheNodeType 85 | CacheParameterGroupName: !Ref ElastiCacheParameterGroup 86 | CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup 87 | Engine: redis 88 | EngineVersion: 5.0.6 89 | NumCacheClusters: !Ref NumberOfSubnets 90 | ReplicationGroupDescription: Redis cluster for Moodle 91 | ReplicationGroupId: !Ref ElastiCacheClusterName 92 | SecurityGroupIds: 93 | - !Ref ElastiCacheSecurityGroup 94 | SnapshotRetentionLimit: 4 95 | AtRestEncryptionEnabled: false 96 | Tags: 97 | - Key: Name 98 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 99 | 100 | ElastiCacheSubnetGroup: 101 | Type: AWS::ElastiCache::SubnetGroup 102 | Properties: 103 | CacheSubnetGroupName: !Join [ '', [ !Ref ElastiCacheClusterName, SubnetGroup ] ] 104 | Description: ElastiCache Subnet Group for Moodle 105 | SubnetIds: 106 | !If 107 | - HasThreeAZs 108 | - [!Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ]] 109 | - [!Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ]] 110 | 111 | ElastiCacheParameterGroup: 112 | Type: "AWS::ElastiCache::ParameterGroup" 113 | Properties: 114 | CacheParameterGroupFamily: redis5.0 115 | Description: ElastiCache Parameter Group for Moodle 116 | Properties: 117 | timeout: '0' 118 | 119 | Outputs: 120 | ElastiCacheClusterEndpointAddress: 121 | Description: ElastiCache Redis endpoint address 122 | Value: !GetAtt ElastiCacheReplicationGroup.PrimaryEndPoint.Address -------------------------------------------------------------------------------- /deployment/03-publicalb.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates Application Load Balancer 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: ALB Parameters 12 | Parameters: 13 | - Vpc 14 | - PublicAlbCertificateArn 15 | - PublicAlbSecurityGroup 16 | - NumberOfSubnets 17 | - Subnet 18 | ParameterLabels: 19 | Vpc: 20 | default: Vpc Id 21 | PublicAlbCertificateArn: 22 | default: ALB Certificate ARN 23 | PublicAlbSecurityGroup: 24 | default: Public ALB Security Group 25 | NumberOfSubnets: 26 | default: Number of subnets 27 | Subnet: 28 | default: Subnets 29 | 30 | Parameters: 31 | 32 | NumberOfSubnets: 33 | AllowedValues: 34 | - 2 35 | - 3 36 | - 4 37 | - 5 38 | - 6 39 | Default: 3 40 | Description: Number of subnets. This must match your selections in the list of subnets below. 41 | Type: String 42 | PublicAlbCertificateArn: 43 | AllowedPattern: ^$|(arn:aws-cn:iam)::([0-9]{12}):server-certificate/(.*)$ 44 | Description: '[ Optional ] The certificate ARN for the ALB certificate - this certificate should be created in the region you wish to run the ALB and must reference the Moodle domain name you use below.' 45 | Type: String 46 | Default: '' 47 | PublicAlbSecurityGroup: 48 | Description: Select the ALB security group. 49 | Type: AWS::EC2::SecurityGroup::Id 50 | Subnet: 51 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 52 | Type: List 53 | Vpc: 54 | Description: Select an existing Vpc 55 | Type: AWS::EC2::VPC::Id 56 | 57 | Conditions: 58 | 59 | SslCertificate: 60 | !Not [!Equals [ '', !Ref PublicAlbCertificateArn ] ] 61 | NoSslCertificate: 62 | !Equals [ '', !Ref PublicAlbCertificateArn ] 63 | NumberOfSubnets1: 64 | !Equals [ 1, !Ref NumberOfSubnets ] 65 | NumberOfSubnets2: 66 | !Equals [ 2, !Ref NumberOfSubnets ] 67 | NumberOfSubnets3: 68 | !Equals [ 3, !Ref NumberOfSubnets ] 69 | NumberOfSubnets4: 70 | !Equals [ 4, !Ref NumberOfSubnets ] 71 | NumberOfSubnets5: 72 | !Equals [ 5, !Ref NumberOfSubnets ] 73 | NumberOfSubnets6: 74 | !Equals [ 6, !Ref NumberOfSubnets ] 75 | Subnet0: !Or 76 | - !Condition NumberOfSubnets1 77 | - !Condition NumberOfSubnets2 78 | - !Condition NumberOfSubnets3 79 | - !Condition NumberOfSubnets4 80 | - !Condition NumberOfSubnets5 81 | - !Condition NumberOfSubnets6 82 | Subnet1: !Or 83 | - !Condition NumberOfSubnets2 84 | - !Condition NumberOfSubnets3 85 | - !Condition NumberOfSubnets4 86 | - !Condition NumberOfSubnets5 87 | - !Condition NumberOfSubnets6 88 | Subnet2: !Or 89 | - !Condition NumberOfSubnets3 90 | - !Condition NumberOfSubnets4 91 | - !Condition NumberOfSubnets5 92 | - !Condition NumberOfSubnets6 93 | Subnet3: !Or 94 | - !Condition NumberOfSubnets4 95 | - !Condition NumberOfSubnets5 96 | - !Condition NumberOfSubnets6 97 | Subnet4: !Or 98 | - !Condition NumberOfSubnets5 99 | - !Condition NumberOfSubnets6 100 | Subnet5: !Condition NumberOfSubnets6 101 | 102 | Resources: 103 | 104 | PublicAlbListenerNoSslCertificate: 105 | Metadata: 106 | cfn_nag: 107 | rules_to_suppress: 108 | - id: W56 109 | reason: This is NOT HTTPS listener 110 | Type : AWS::ElasticLoadBalancingV2::Listener 111 | Properties: 112 | DefaultActions: 113 | - Type: forward 114 | TargetGroupArn: !Ref PublicAlbTargetGroup 115 | LoadBalancerArn: !Ref PublicApplicationLoadBalancer 116 | Port: 80 117 | Protocol: HTTP 118 | PublicAlbListenerSslCertificate: 119 | Metadata: 120 | cfn_nag: 121 | rules_to_suppress: 122 | - id: W55 123 | reason: use default SslPolicy 124 | Condition: SslCertificate 125 | Type : AWS::ElasticLoadBalancingV2::Listener 126 | Properties: 127 | Certificates: 128 | - CertificateArn: !Ref PublicAlbCertificateArn 129 | DefaultActions: 130 | - Type: forward 131 | TargetGroupArn: !Ref PublicAlbTargetGroup 132 | LoadBalancerArn: !Ref PublicApplicationLoadBalancer 133 | Port: 443 134 | Protocol: HTTPS 135 | PublicApplicationLoadBalancer: 136 | Metadata: 137 | cfn_nag: 138 | rules_to_suppress: 139 | - id: W52 140 | reason: No need logging 141 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 142 | Properties: 143 | Scheme: internet-facing 144 | Subnets: 145 | !If 146 | [ NumberOfSubnets1, 147 | [ !Select [ 0, !Ref Subnet ] ], 148 | !If 149 | [ NumberOfSubnets2, 150 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 151 | !If 152 | [ NumberOfSubnets3, 153 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ], 154 | !If 155 | [ NumberOfSubnets4, 156 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ] ], 157 | !If 158 | [ NumberOfSubnets5, 159 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ] ], 160 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ], !Select [ 5, !Ref Subnet ] ] 161 | ] 162 | ] 163 | ] 164 | ] 165 | ] 166 | LoadBalancerAttributes: 167 | - Key: idle_timeout.timeout_seconds 168 | Value: 60 169 | SecurityGroups: 170 | - !Ref PublicAlbSecurityGroup 171 | Tags: 172 | - Key: Name 173 | Value: !Join [ '', [ 'Public ALB / ', !Ref 'AWS::StackName' ] ] 174 | PublicAlbTargetGroup: 175 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 176 | Properties: 177 | HealthCheckIntervalSeconds: 30 178 | HealthCheckPath: /README.txt 179 | HealthCheckTimeoutSeconds: 5 180 | Name: !Join [ '', [ 'PublicALB-', !Ref Vpc ] ] 181 | Port: 80 182 | Protocol: HTTP 183 | Tags: 184 | - Key: Name 185 | Value: !Join [ '', [ 'Public ALB / ', !Ref 'AWS::StackName' ] ] 186 | UnhealthyThresholdCount: 5 187 | VpcId: !Ref Vpc 188 | 189 | Outputs: 190 | 191 | PublicAlbTargetGroupArn: 192 | Value: 193 | !Ref PublicAlbTargetGroup 194 | PublicAlbCanonicalHostedZoneId: 195 | Value: 196 | !GetAtt PublicApplicationLoadBalancer.CanonicalHostedZoneID 197 | PublicAlbDnsName: 198 | Value: 199 | !GetAtt PublicApplicationLoadBalancer.DNSName 200 | PublicAlbFullName: 201 | Value: 202 | !GetAtt PublicApplicationLoadBalancer.LoadBalancerFullName 203 | PublicAlbHostname: 204 | Value: 205 | !If [ NoSslCertificate, !Join [ '', [ 'http://', !GetAtt PublicApplicationLoadBalancer.DNSName ] ], !Join [ '', [ 'https://', !GetAtt PublicApplicationLoadBalancer.DNSName ] ] ] 206 | SslCertificate: 207 | Value: 208 | !If [ SslCertificate, True, False ] 209 | -------------------------------------------------------------------------------- /deployment/03-rds.yaml: -------------------------------------------------------------------------------- 1 | # VPC on AWS 2 | # 3 | # author: aws-gcr-solution-center@ 4 | 5 | AWSTemplateFormatVersion: 2010-09-09 6 | 7 | Description: Reference Architecture to host Moodle on AWS - Creates RDS Aurora MySQL database cluster 8 | 9 | Metadata: 10 | AWS::CloudFormation::Interface: 11 | - Label: 12 | default: Database Parameters 13 | Parameters: 14 | - DatabaseInstanceType 15 | - DatabaseMasterUsername 16 | - DatabaseMasterPassword 17 | - DatabaseName 18 | - DatabaseSecurityGroup 19 | - NumberOfSubnets 20 | - Subnet 21 | ParameterLabel: 22 | DatabaseInstanceType: 23 | default: DB Instance Class 24 | DatabaseMasterUsername: 25 | default: DB Master Username 26 | DatabaseMasterPassword: 27 | default: DB Master Password 28 | DatabaseName: 29 | default: DB Name 30 | DatabaseSecurityGroup: 31 | default: DB Security Group 32 | NumberOfSubnets: 33 | default: Number of subnets 34 | Subnet: 35 | default: Subnet IDs 36 | 37 | Parameters: 38 | DatabaseInstanceType: 39 | AllowedValues: 40 | - db.r5.large 41 | - db.r5.xlarge 42 | - db.r5.2xlarge 43 | - db.r5.4xlarge 44 | - db.r5.12xlarge 45 | ConstraintDescription: Must be a valid RDS instance class. 46 | Default: db.r5.large 47 | Description: The Amazon RDS database instance class. 48 | Type: String 49 | DatabaseMasterUsername: 50 | AllowedPattern: ^([a-zA-Z0-9]*)$ 51 | Description: The Amazon RDS master username. 52 | ConstraintDescription: Must contain only alphanumeric characters and be at least 8 characters. 53 | MaxLength: 16 54 | MinLength: 1 55 | Type: String 56 | DatabaseMasterPassword: 57 | AllowedPattern: ^([a-z0-9A-Z`~!#$%^&*()_+,\\-])*$ 58 | ConstraintDescription: Must be letters (upper or lower), numbers, and these special characters '_'`~!#$%^&*()_+,- 59 | Description: The Amazon RDS master password. 60 | MaxLength: 41 61 | MinLength: 8 62 | NoEcho: true 63 | Type: String 64 | DatabaseName: 65 | AllowedPattern: ^([a-zA-Z0-9]*)$ 66 | Description: The Amazon RDS master database name. 67 | Type: String 68 | DatabaseSecurityGroup: 69 | Description: Select the database security group. 70 | Type: AWS::EC2::SecurityGroup::Id 71 | NumberOfSubnets: 72 | AllowedValues: 73 | - 2 74 | - 3 75 | Default: 3 76 | Type: String 77 | Description: Number of subnets. This must match your selections in the list of subnets below. 78 | Subnet: 79 | Description: Select existing subnets. 80 | Type: List 81 | 82 | Conditions: 83 | HasThreeAZs: 84 | !Not [!Equals [2, !Ref NumberOfSubnets]] 85 | 86 | Resources: 87 | DatabaseSubnetGroup: 88 | Type: "AWS::RDS::DBSubnetGroup" 89 | Properties: 90 | DBSubnetGroupDescription: RDS Database Subnet Group for Moodle 91 | SubnetIds: 92 | !If 93 | - HasThreeAZs 94 | - [!Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ]] 95 | - [!Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ]] 96 | Tags: 97 | - Key: Name 98 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 99 | 100 | DatabaseParameterGroup: 101 | Type: "AWS::RDS::DBParameterGroup" 102 | Properties: 103 | Description: Moodle Database Parameter Group 104 | Family: aurora5.6 105 | Parameters: 106 | innodb_file_format: Barracuda 107 | innodb_large_prefix: 1 108 | Tags: 109 | - Key: Name 110 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 111 | 112 | DatabaseClusterParameterGroup: 113 | Type: "AWS::RDS::DBClusterParameterGroup" 114 | Properties: 115 | Description: Moodle Database Cluster Parameter Group 116 | Family: aurora5.6 117 | Parameters: 118 | character_set_database: utf8mb4 119 | character_set_server: utf8mb4 120 | collation_server: utf8mb4_unicode_ci 121 | Tags: 122 | - Key: Name 123 | Value: moodle 124 | 125 | DatabaseCluster: 126 | Type: "AWS::RDS::DBCluster" 127 | Properties: 128 | BackupRetentionPeriod: 7 129 | DBClusterParameterGroupName: !Ref DatabaseClusterParameterGroup 130 | DBClusterIdentifier: !Join [ '-', [!Ref 'AWS::StackName', 'db' ] ] 131 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 132 | DatabaseName: !Ref DatabaseName 133 | Engine: aurora 134 | MasterUserPassword: !Ref DatabaseMasterPassword 135 | MasterUsername: !Ref DatabaseMasterUsername 136 | Port: 3306 137 | StorageEncrypted: true 138 | Tags: 139 | - Key: Name 140 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 141 | VpcSecurityGroupIds: 142 | - !Ref DatabaseSecurityGroup 143 | 144 | DatabaseInstance0: 145 | Type: "AWS::RDS::DBInstance" 146 | DeletionPolicy: Delete 147 | Properties: 148 | AllowMajorVersionUpgrade: false 149 | AutoMinorVersionUpgrade: true 150 | DBClusterIdentifier: !Ref DatabaseCluster 151 | DBInstanceClass: !Ref DatabaseInstanceType 152 | DBParameterGroupName: !Ref DatabaseParameterGroup 153 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 154 | Engine: aurora 155 | PubliclyAccessible: false 156 | Tags: 157 | - Key: Name 158 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 159 | 160 | DatabaseInstance1: 161 | Type: AWS::RDS::DBInstance 162 | DeletionPolicy: Delete 163 | Properties: 164 | AllowMajorVersionUpgrade: false 165 | AutoMinorVersionUpgrade: true 166 | DBClusterIdentifier: !Ref DatabaseCluster 167 | DBInstanceClass: !Ref DatabaseInstanceType 168 | DBParameterGroupName: !Ref DatabaseParameterGroup 169 | DBSubnetGroupName: !Ref DatabaseSubnetGroup 170 | Engine: aurora 171 | PubliclyAccessible: false 172 | Tags: 173 | - Key: Name 174 | Value: !Join [ '', [ 'Moodle / ', !Ref 'AWS::StackName' ] ] 175 | 176 | Outputs: 177 | DatabaseCluster: 178 | Description: Database Cluster ID 179 | Value: !Ref DatabaseCluster 180 | DatabaseName: 181 | Description: Database Name 182 | Value: !Ref DatabaseName 183 | DatabaseClusterEndpointAddress: 184 | Description: Database Cluster Endpoint 185 | Value: !GetAtt DatabaseCluster.Endpoint.Address 186 | DatabaseClusterReadEndpointAddress: 187 | Description: Database Read-only Cluster Endpoint 188 | Value: !GetAtt DatabaseCluster.ReadEndpoint.Address -------------------------------------------------------------------------------- /deployment/04-cloudfront.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates CloudFront distribution (if selected) 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: AWS Parameters 12 | Parameters: 13 | - DomainName 14 | - CloudFrontIamCertificateId 15 | - PublicAlbDomainName 16 | - PublicAlbCertificate 17 | ParameterLabels: 18 | CloudFrontIamCertificateId: 19 | default: CloudFront Certificate ID in AWS IAM 20 | PublicAlbDomainName: 21 | default: Public ALB DNS Name 22 | DomainName: 23 | default: Domain name of the Moodle site 24 | 25 | Parameters: 26 | 27 | CloudFrontIamCertificateId: 28 | Description: '[ Optional ] The ID of SSL Certificate which has been uploaded to AWS IAM' 29 | Type: String 30 | DomainName: 31 | AllowedPattern: ^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ 32 | Description: 'The main domain name of the Moodle site (e.g. moodle.example.edu).' 33 | Type: String 34 | PublicAlbDomainName: 35 | Description: The public application load balancer dns name. 36 | Type: String 37 | PublicAlbCertificate: 38 | Description: If the public alb origin has SSL certificate 39 | AllowedValues: 40 | - True 41 | - False 42 | Default: False 43 | Type: String 44 | 45 | 46 | Conditions: 47 | 48 | SslCertificate: 49 | !Not [ !Equals [ '', !Ref CloudFrontIamCertificateId ] ] 50 | NoSslCertificate: 51 | !Equals [ '', !Ref CloudFrontIamCertificateId ] 52 | DomainName: 53 | !Not [ !Equals [ '', !Ref DomainName ] ] 54 | NoDomainName: 55 | !Equals [ '', !Ref DomainName ] 56 | PublicAlbCertificate: 57 | !Equals [ True, !Ref PublicAlbCertificate ] 58 | 59 | 60 | Resources: 61 | 62 | CloudFrontDistributionNoSslCertificate: 63 | Metadata: 64 | cfn_nag: 65 | rules_to_suppress: 66 | - id: W10 67 | reason: Will to included in feature version 68 | Type: AWS::CloudFront::Distribution 69 | Condition: NoSslCertificate 70 | Properties: 71 | DistributionConfig: 72 | Aliases: 73 | - !If [ DomainName, !Ref DomainName, !Ref 'AWS::NoValue' ] 74 | Comment: !Ref 'AWS::StackName' 75 | DefaultCacheBehavior: 76 | AllowedMethods: 77 | - DELETE 78 | - GET 79 | - HEAD 80 | - OPTIONS 81 | - PATCH 82 | - POST 83 | - PUT 84 | DefaultTTL: 0 85 | MaxTTL: 0 86 | MinTTL: 0 87 | ForwardedValues: 88 | QueryString: true 89 | Headers: 90 | - '*' 91 | Cookies: 92 | Forward: all 93 | TargetOriginId: elb 94 | ViewerProtocolPolicy: allow-all 95 | Compress: true 96 | Enabled: true 97 | Origins: 98 | - DomainName: !Ref PublicAlbDomainName 99 | Id: elb 100 | CustomOriginConfig: 101 | OriginProtocolPolicy: !If [ PublicAlbCertificate, 'https-only', 'http-only' ] 102 | PriceClass: PriceClass_All 103 | CloudFrontDistributionSslCertificate: 104 | Metadata: 105 | cfn_nag: 106 | rules_to_suppress: 107 | - id: W10 108 | reason: Will to included in feature version 109 | Type: AWS::CloudFront::Distribution 110 | Condition: SslCertificate 111 | Properties: 112 | DistributionConfig: 113 | Aliases: 114 | - !If [ DomainName, !Ref DomainName, !Ref 'AWS::NoValue' ] 115 | Comment: !Ref 'AWS::StackName' 116 | DefaultCacheBehavior: 117 | AllowedMethods: 118 | - DELETE 119 | - GET 120 | - HEAD 121 | - OPTIONS 122 | - PATCH 123 | - POST 124 | - PUT 125 | DefaultTTL: 0 126 | MaxTTL: 0 127 | MinTTL: 0 128 | ForwardedValues: 129 | QueryString: true 130 | Headers: 131 | - '*' 132 | Cookies: 133 | Forward: all 134 | TargetOriginId: elb 135 | ViewerProtocolPolicy: redirect-to-https 136 | Compress: true 137 | Enabled: true 138 | Origins: 139 | - DomainName: !Ref PublicAlbDomainName 140 | Id: elb 141 | CustomOriginConfig: 142 | OriginProtocolPolicy: !If [ PublicAlbCertificate, 'https-only', 'http-only' ] 143 | PriceClass: PriceClass_All 144 | ViewerCertificate: 145 | IamCertificateId: !Ref CloudFrontIamCertificateId 146 | SslSupportMethod: sni-only 147 | MinimumProtocolVersion: TLSv1 148 | 149 | Outputs: 150 | 151 | DnsEndpoint: 152 | Value: !If [ NoSslCertificate, !GetAtt CloudFrontDistributionNoSslCertificate.DomainName, !GetAtt CloudFrontDistributionSslCertificate.DomainName ] 153 | DnsHostname: 154 | Value: !If [ NoSslCertificate, !Join [ '', [ 'http://', !GetAtt CloudFrontDistributionNoSslCertificate.DomainName ] ], !Join [ '', [ 'https://', !GetAtt CloudFrontDistributionSslCertificate.DomainName ] ] ] 155 | -------------------------------------------------------------------------------- /deployment/04-web.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: Reference Architecture to host Moodle on AWS - Creates Moodle web Auto Scaling group 5 | 6 | Metadata: 7 | 8 | AWS::CloudFormation::Interface: 9 | ParameterGroups: 10 | - Label: 11 | default: Web Parameters 12 | Parameters: 13 | - EC2KeyName 14 | - WebInstanceType 15 | - WebAsgMax 16 | - WebAsgMin 17 | - WebSecurityGroup 18 | - NumberOfSubnets 19 | - Subnet 20 | - PublicAlbTargetGroupArn 21 | - PublicAlbHostname 22 | - SslCertificate 23 | - Label: 24 | default: Moodle Parameters 25 | Parameters: 26 | - DomainName 27 | - MoodleLocale 28 | - Label: 29 | default: Database Parameters 30 | Parameters: 31 | - DatabaseClusterEndpointAddress 32 | - DatabaseName 33 | - DatabaseMasterUsername 34 | - DatabaseMasterPassword 35 | - DatabaseClusterEndpointAddress 36 | - ElastiCacheClusterEndpointAddress 37 | - Label: 38 | default: File System Parameters 39 | Parameters: 40 | - ElasticFileSystem 41 | 42 | ParameterLabels: 43 | DatabaseClusterEndpointAddress: 44 | default: DB Cluster Endpoint Address 45 | DatabaseMasterUsername: 46 | default: DB Master Username 47 | DatabaseMasterPassword: 48 | default: DB Master Password 49 | DatabaseName: 50 | default: DB Name 51 | ElastiCacheClusterEndpointAddress: 52 | default: ElastiCache Endpoint Address 53 | ElasticFileSystem: 54 | default: EFS File System 55 | EC2KeyName: 56 | default: Existing Key Pair 57 | NumberOfSubnets: 58 | default: Number of subnets 59 | PublicAlbTargetGroupArn: 60 | default: Public ALB Target Group Arn 61 | PublicAlbHostname: 62 | default: Public ALB Hostname 63 | SslCertificate: 64 | default: ACM Cert attached to Public ALB 65 | Subnet: 66 | default: Subnets 67 | WebAsgMax: 68 | default: Web ASG Max 69 | WebAsgMin: 70 | default: Web ASG Min 71 | WebInstanceType: 72 | default: Web Instance Type 73 | WebSecurityGroup: 74 | default: Web Security Group 75 | DomainName: 76 | default: Site Domain 77 | DomainHttps: 78 | default: Domain Https 79 | MoodleLocale: 80 | default: Language Code 81 | 82 | Parameters: 83 | 84 | ElastiCacheClusterEndpointAddress: 85 | Description: The ElastiCacheCluster cluster endpoint address. 86 | Type: String 87 | DatabaseClusterEndpointAddress: 88 | Description: The RDS cluster endpoint address. 89 | Type: String 90 | DatabaseMasterUsername: 91 | AllowedPattern: ^([a-zA-Z0-9]*)$ 92 | Description: The Amazon RDS master username. 93 | ConstraintDescription: Must contain only alphanumeric characters and be at least 8 characters. 94 | MaxLength: 16 95 | MinLength: 1 96 | Type: String 97 | DatabaseMasterPassword: 98 | AllowedPattern: ^([a-z0-9A-Z`~!#$%^&*()_+,\\-])*$ 99 | ConstraintDescription: Must be letters (upper or lower), numbers, and these special characters '_'`~!#$%^&*()_+,- 100 | Description: The Amazon RDS master password. 101 | MaxLength: 41 102 | MinLength: 8 103 | NoEcho: true 104 | Type: String 105 | DatabaseName: 106 | AllowedPattern: ^([a-zA-Z0-9]*)$ 107 | Description: The Amazon RDS master database name. 108 | Type: String 109 | Default: moodle 110 | ElasticFileSystem: 111 | AllowedPattern: ^(fs-)([a-z0-9]{8}|[a-z0-9]{17})$ 112 | Description: The Amazon EFS file system id. 113 | Type: String 114 | EC2KeyName: 115 | AllowedPattern: ^([a-zA-Z0-9 @.`~!#$%^&*()_+,\\-])*$ 116 | ConstraintDescription: Must be letters (upper or lower), numbers, and special characters. 117 | Description: Name of an EC2 KeyPair. Your bastion & Web instances will launch with this KeyPair. 118 | Type: AWS::EC2::KeyPair::KeyName 119 | NumberOfSubnets: 120 | AllowedValues: 121 | - 2 122 | - 3 123 | - 4 124 | - 5 125 | - 6 126 | Default: 3 127 | Description: Number of subnets. This must match your selections in the list of subnets below. 128 | Type: String 129 | PublicAlbTargetGroupArn: 130 | Description: The public application load balancer target group arn. 131 | Type: String 132 | PublicAlbHostname: 133 | Description: The hostname of the public ALB http form (e.g. http://abdc-12345-xyz..elb.amazonaws.com.cn) 134 | Type: String 135 | SslCertificate: 136 | AllowedValues: 137 | - True 138 | - False 139 | Default: True 140 | Description: Is there an ACM SSL Certificate attached to the Public ALB? 141 | Type: String 142 | Subnet: 143 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 144 | Type: List 145 | WebAsgMax: 146 | AllowedPattern: ^((?!0$)[1-2]?[0-9]|30)$ 147 | ConstraintDescription: Must be a number between 1 and 30. 148 | Default: 4 149 | Description: Specifies the maximum number of EC2 instances in the Web Autoscaling Group. 150 | Type: String 151 | WebAsgMin: 152 | AllowedPattern: ^([0-0]?[0-9]|10)$ 153 | ConstraintDescription: Must be a number between 0 and 10. 154 | Default: 2 155 | Description: Specifies the minimum number of EC2 instances in the Web Autoscaling Group. 156 | Type: String 157 | WebInstanceType: 158 | AllowedValues: 159 | - t2.medium 160 | - t2.large 161 | - t2.xlarge 162 | - t2.2xlarge 163 | - m4.large 164 | - m4.xlarge 165 | - m4.2xlarge 166 | - m4.4xlarge 167 | - m4.10xlarge 168 | - m4.16xlarge 169 | - m5.large 170 | - m5.xlarge 171 | - m5.2xlarge 172 | - m5.4xlarge 173 | - m5.12xlarge 174 | - m5.24xlarge 175 | - c4.large 176 | - c4.xlarge 177 | - c4.2xlarge 178 | - c4.4xlarge 179 | - c4.8xlarge 180 | - c5.large 181 | - c5.xlarge 182 | - c5.2xlarge 183 | - c5.4xlarge 184 | - c5.9xlarge 185 | - c5.18xlarge 186 | - r4.large 187 | - r4.xlarge 188 | - r4.2xlarge 189 | - r4.4xlarge 190 | - r4.8xlarge 191 | - r4.16xlarge 192 | - r5.large 193 | - r5.2xlarge 194 | - r5.4xlarge 195 | - r5.8xlarge 196 | - r5.12xlarge 197 | - r5.16xlarge 198 | - r5.24xlarge 199 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 200 | Default: t2.large 201 | Description: The Amazon EC2 instance type for your web instances. 202 | Type: String 203 | WebSecurityGroup: 204 | Description: Select the web security group. 205 | Type: AWS::EC2::SecurityGroup::Id 206 | DomainName: 207 | AllowedPattern: ^$|(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ 208 | Description: '[ Optional ] The main domain name of the Moodle site (e.g. moodle.example.edu). Leave empty to use the ALB DNS name for the Moodle site.' 209 | Type: String 210 | Default: '' 211 | DomainHttps: 212 | AllowedValues: 213 | - True 214 | - False 215 | Default: False 216 | Description: If the domain is protected by HTTPS? 217 | Type: String 218 | MoodleLocale: 219 | Description: The main language used in the Moodle configuration wizard. 220 | Type: String 221 | Default: en 222 | LatestAmiId : 223 | Type : AWS::SSM::Parameter::Value 224 | Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 225 | 226 | 227 | Conditions: 228 | 229 | NoSslCertificate: 230 | !Equals [ False, !Ref SslCertificate ] 231 | NumberOfSubnets1: 232 | !Equals [ 1, !Ref NumberOfSubnets ] 233 | NumberOfSubnets2: 234 | !Equals [ 2, !Ref NumberOfSubnets ] 235 | NumberOfSubnets3: 236 | !Equals [ 3, !Ref NumberOfSubnets ] 237 | NumberOfSubnets4: 238 | !Equals [ 4, !Ref NumberOfSubnets ] 239 | NumberOfSubnets5: 240 | !Equals [ 5, !Ref NumberOfSubnets ] 241 | NumberOfSubnets6: 242 | !Equals [ 6, !Ref NumberOfSubnets ] 243 | Subnet0: !Or 244 | - !Condition NumberOfSubnets1 245 | - !Condition NumberOfSubnets2 246 | - !Condition NumberOfSubnets3 247 | - !Condition NumberOfSubnets4 248 | - !Condition NumberOfSubnets5 249 | - !Condition NumberOfSubnets6 250 | Subnet1: !Or 251 | - !Condition NumberOfSubnets2 252 | - !Condition NumberOfSubnets3 253 | - !Condition NumberOfSubnets4 254 | - !Condition NumberOfSubnets5 255 | - !Condition NumberOfSubnets6 256 | Subnet2: !Or 257 | - !Condition NumberOfSubnets3 258 | - !Condition NumberOfSubnets4 259 | - !Condition NumberOfSubnets5 260 | - !Condition NumberOfSubnets6 261 | Subnet3: !Or 262 | - !Condition NumberOfSubnets4 263 | - !Condition NumberOfSubnets5 264 | - !Condition NumberOfSubnets6 265 | Subnet4: !Or 266 | - !Condition NumberOfSubnets5 267 | - !Condition NumberOfSubnets6 268 | Subnet5: !Condition NumberOfSubnets6 269 | NoDomainName: 270 | !Equals [ '', !Ref DomainName ] 271 | 272 | 273 | 274 | Resources: 275 | WebInstanceProfile: 276 | Type: AWS::IAM::InstanceProfile 277 | Properties: 278 | Path: / 279 | Roles: 280 | - !Ref WebInstanceRole 281 | WebInstanceRole: 282 | Type: AWS::IAM::Role 283 | Properties: 284 | AssumeRolePolicyDocument: 285 | Version: 2012-10-17 286 | Statement: 287 | - Effect: Allow 288 | Principal: 289 | Service: 290 | - ec2.amazonaws.com.cn 291 | Action: 292 | - sts:AssumeRole 293 | Path: / 294 | Policies: 295 | - PolicyName: logs 296 | PolicyDocument: 297 | Version: 2012-10-17 298 | Statement: 299 | - Effect: Allow 300 | Action: 301 | - logs:CreateLogGroup 302 | - logs:CreateLogStream 303 | - logs:PutLogEvents 304 | - logs:DescribeLogStreams 305 | Resource: 306 | - arn:aws-cn:logs:*:*:* 307 | - PolicyName: CompleteLifecycleAction 308 | PolicyDocument: 309 | Version: 2012-10-17 310 | Statement: 311 | - Effect: Allow 312 | Action: 313 | - autoscaling:CompleteLifecycleAction 314 | - autoscaling:DescribeAutoScalingInstances 315 | - autoscaling:DescribeLifecycleHooks 316 | Resource: 317 | - arn:aws-cn:autoscaling:*:*:* 318 | Logs: 319 | Type: AWS::Logs::LogGroup 320 | DeletionPolicy: Retain 321 | Properties: 322 | RetentionInDays: 7 323 | WebAutoScalingGroup: 324 | Type: AWS::AutoScaling::AutoScalingGroup 325 | Properties: 326 | Cooldown: 60 327 | HealthCheckGracePeriod: 300 328 | HealthCheckType: ELB 329 | LaunchConfigurationName: !Ref WebLaunchConfiguration 330 | MaxSize: !Ref WebAsgMax 331 | MinSize: !Ref WebAsgMin 332 | Tags: 333 | - Key: Name 334 | Value: !Join [ '', [ 'Web ASG / ', !Ref 'AWS::StackName' ] ] 335 | PropagateAtLaunch: true 336 | TargetGroupARNs: 337 | - !Ref PublicAlbTargetGroupArn 338 | VPCZoneIdentifier: 339 | !If 340 | [ NumberOfSubnets1, 341 | [ !Select [ 0, !Ref Subnet ] ], 342 | !If 343 | [ NumberOfSubnets2, 344 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 345 | !If 346 | [ NumberOfSubnets3, 347 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ], 348 | !If 349 | [ NumberOfSubnets4, 350 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ] ], 351 | !If 352 | [ NumberOfSubnets5, 353 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ] ], 354 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ], !Select [ 3, !Ref Subnet ], !Select [ 4, !Ref Subnet ], !Select [ 5, !Ref Subnet ] ] 355 | ] 356 | ] 357 | ] 358 | ] 359 | ] 360 | CreationPolicy: 361 | ResourceSignal: 362 | Count: !Ref WebAsgMin 363 | Timeout: PT15M 364 | 365 | ScaleUpPolicy: 366 | Type: AWS::AutoScaling::ScalingPolicy 367 | Properties: 368 | AdjustmentType: ChangeInCapacity 369 | AutoScalingGroupName: 370 | Ref: WebAutoScalingGroup 371 | Cooldown: 300 372 | ScalingAdjustment: 1 373 | CPUAlarmHigh: 374 | Type: AWS::CloudWatch::Alarm 375 | Properties: 376 | EvaluationPeriods: 3 377 | Statistic: Average 378 | Threshold: 75 379 | AlarmDescription: Alarm if CPU too high 380 | Period: 60 381 | AlarmActions: 382 | - Ref: ScaleUpPolicy 383 | Namespace: AWS/EC2 384 | Dimensions: 385 | - Name: AutoScalingGroupName 386 | Value: !Ref WebAutoScalingGroup 387 | ComparisonOperator: GreaterThanThreshold 388 | MetricName: CPUUtilization 389 | ScaleDownPolicy: 390 | Type: AWS::AutoScaling::ScalingPolicy 391 | Properties: 392 | AdjustmentType: ChangeInCapacity 393 | AutoScalingGroupName: 394 | Ref: WebAutoScalingGroup 395 | Cooldown: 300 396 | ScalingAdjustment: -1 397 | CPUAlarmLow: 398 | Type: AWS::CloudWatch::Alarm 399 | Properties: 400 | EvaluationPeriods: 3 401 | Statistic: Average 402 | Threshold: 25 403 | AlarmDescription: Alarm if CPU too low 404 | Period: 60 405 | AlarmActions: 406 | - Ref: ScaleDownPolicy 407 | Namespace: AWS/EC2 408 | Dimensions: 409 | - Name: AutoScalingGroupName 410 | Value: !Ref WebAutoScalingGroup 411 | ComparisonOperator: LessThanThreshold 412 | MetricName: CPUUtilization 413 | 414 | WebLaunchConfiguration: 415 | Type: AWS::AutoScaling::LaunchConfiguration 416 | Metadata: 417 | AWS::CloudFormation::Init: 418 | configSets: 419 | deploy_webserver: 420 | - install_logs 421 | - install_efs_utils 422 | - install_webserver 423 | - install_moodle 424 | # - install_cacheclient 425 | - install_opcache 426 | - start_webserver 427 | install_webserver: 428 | packages: 429 | yum: 430 | httpd: [] 431 | php: [] 432 | php-fpm: [] 433 | php-gd: [] 434 | php-json: [] 435 | php-mbstring: [] 436 | php-mysqlnd: [] 437 | php-xml: [] 438 | php-xmlrpc: [] 439 | php-pecl-zip: [] 440 | php-intl: [] 441 | php-soap: [] 442 | php-pecl-redis: [] 443 | php-cli: [] 444 | files: 445 | /tmp/create_site_conf.sh: 446 | content: !Sub | 447 | #!/bin/bash -xe 448 | if [ ! -f /etc/httpd/conf.d/moodle.conf ]; then 449 | touch /etc/httpd/conf.d/moodle.conf 450 | echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/moodle.conf 451 | echo 'DocumentRoot /var/www/moodle/html' >> /etc/httpd/conf.d/moodle.conf 452 | echo '' >> /etc/httpd/conf.d/moodle.conf 453 | echo ' Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/moodle.conf 454 | echo ' AllowOverride All' >> /etc/httpd/conf.d/moodle.conf 455 | echo ' Require all granted' >> /etc/httpd/conf.d/moodle.conf 456 | echo '' >> /etc/httpd/conf.d/moodle.conf 457 | fi 458 | mode: 000500 459 | owner: root 460 | group: root 461 | /tmp/modify_php_setting.sh: 462 | content: !Sub | 463 | #!/bin/bash -xe 464 | if [ -f /etc/php.ini ]; then 465 | sed -i 's/post_max_size = 8M/post_max_size = 128M/' /etc/php.ini 466 | sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 64M/' /etc/php.ini 467 | sed -i 's/memory_limit = 128M/memory_limit = 256M/' /etc/php.ini 468 | fi 469 | mode: 000500 470 | owner: root 471 | group: root 472 | commands: 473 | create_site_conf: 474 | command: ./create_site_conf.sh 475 | cwd: /tmp 476 | ignoreErrors: false 477 | create_test_php_file: 478 | command: "echo \"\" > /var/www/html/phpinfo.php" 479 | cwd: /var/www/html 480 | ignoreErrors: false 481 | modify_php_setting: 482 | command: ./modify_php_setting.sh 483 | cwd: /tmp 484 | ignoreErrors: false 485 | 486 | install_logs: 487 | packages: 488 | yum: 489 | awslogs: [] 490 | files: 491 | /etc/awslogs/awslogs.conf: 492 | content: !Sub | 493 | [general] 494 | state_file= /var/awslogs/state/agent-state 495 | 496 | [/var/log/cloud-init.log] 497 | file = /var/log/cloud-init.log 498 | log_group_name = ${Logs} 499 | log_stream_name = {instance_id}/cloud-init.log 500 | datetime_format = 501 | 502 | [/var/log/cloud-init-output.log] 503 | file = /var/log/cloud-init-output.log 504 | log_group_name = ${Logs} 505 | log_stream_name = {instance_id}/cloud-init-output.log 506 | datetime_format = 507 | 508 | [/var/log/cfn-init.log] 509 | file = /var/log/cfn-init.log 510 | log_group_name = ${Logs} 511 | log_stream_name = {instance_id}/cfn-init.log 512 | datetime_format = 513 | 514 | [/var/log/cfn-hup.log] 515 | file = /var/log/cfn-hup.log 516 | log_group_name = ${Logs} 517 | log_stream_name = {instance_id}/cfn-hup.log 518 | datetime_format = 519 | 520 | [/var/log/cfn-wire.log] 521 | file = /var/log/cfn-wire.log 522 | log_group_name = ${Logs} 523 | log_stream_name = {instance_id}/cfn-wire.log 524 | datetime_format = 525 | 526 | [/var/log/httpd] 527 | file = /var/log/httpd/* 528 | log_group_name = ${Logs} 529 | log_stream_name = {instance_id}/httpd 530 | datetime_format = %d/%b/%Y:%H:%M:%S 531 | 532 | mode: '000444' 533 | owner: root 534 | group: root 535 | /etc/awslogs/awscli.conf: 536 | content: !Sub | 537 | [plugins] 538 | cwlogs = cwlogs 539 | [default] 540 | region = ${AWS::Region} 541 | mode: '000444' 542 | owner: root 543 | group: root 544 | commands: 545 | 01_create_state_directory: 546 | command: mkdir -p /var/awslogs/state 547 | services: 548 | sysvinit: 549 | awslogsd: 550 | enabled: 'true' 551 | ensureRunning: 'true' 552 | files: /etc/awslogs/awslogs.conf 553 | 554 | install_moodle: 555 | files: 556 | /tmp/config.php: 557 | content: !Sub | 558 | dbtype = 'mysqli'; 563 | $CFG->dblibrary = 'native'; 564 | $CFG->dbhost = '${DatabaseClusterEndpointAddress}'; 565 | $CFG->dbname = '${DatabaseName}'; 566 | $CFG->dbuser = '${DatabaseMasterUsername}'; 567 | $CFG->dbpass = '${DatabaseMasterPassword}'; 568 | $CFG->prefix = 'mdl_'; 569 | $CFG->lang = '${MoodleLocale}'; 570 | $CFG->dboptions = array( 571 | 'dbpersist' => false, 572 | 'dbsocket' => false, 573 | 'dbport' => '', 574 | 'dbhandlesoptions' => false, 575 | 'dbcollation' => 'utf8mb4_unicode_ci', 576 | ); 577 | // Hostname definition // 578 | $hostname = '${DomainName}'; 579 | $domainHttps = '${DomainHttps}'; 580 | if ($hostname == '') { 581 | $hostwithprotocol = '${PublicAlbHostname}'; 582 | } else if ($domainHttps == 'true') { 583 | $hostwithprotocol = 'https://' . strtolower($hostname); 584 | } else { 585 | $hostwithprotocol = 'http://' . strtolower($hostname); 586 | }; 587 | $CFG->wwwroot = strtolower($hostwithprotocol); 588 | $CFG->sslproxy = (substr($hostwithprotocol,0,5)=='https' ? true : false); 589 | // Moodledata location // 590 | $CFG->dataroot = '/var/www/moodle/data'; 591 | $CFG->tempdir = '/var/www/moodle/temp'; 592 | $CFG->cachedir = '/var/www/moodle/cache'; 593 | $CFG->localcachedir = '/var/www/moodle/local'; 594 | // Others // 595 | $CFG->directorypermissions = 02777; 596 | $CFG->admin = 'admin'; 597 | // Configure Session Cache 598 | $SessionEndpoint = '${ElastiCacheClusterEndpointAddress}'; 599 | if ($SessionEndpoint != '') { 600 | $CFG->session_handler_class = '\core\session\redis'; 601 | $CFG->session_redis_host = $SessionEndpoint; 602 | $CFG->session_redis_prefix = 'redis.sess.'; 603 | $CFG->session_redis_acquire_lock_timeout = 120; 604 | $CFG->session_redis_lock_expire = 7200; 605 | } 606 | require_once(__DIR__ . '/lib/setup.php'); 607 | // END OF CONFIG // 608 | ?> 609 | mode: '000755' 610 | owner: root 611 | group: root 612 | /tmp/install_moodle.sh: 613 | content: !Sub | 614 | #!/bin/bash -xe 615 | wget -O /tmp/moodle.tgz https://aws-solutions-assets.s3.cn-north-1.amazonaws.com.cn/moodle/moodle-3.8.1.tgz 616 | tar -xvzf /tmp/moodle.tgz --strip-components=1 -C /var/www/moodle/html/ 617 | mv /tmp/config.php /var/www/moodle/html/config.php 618 | chown -R root:apache /var/www/moodle/html 619 | chown -R apache:apache /var/www/moodle/local 620 | # Aurora does not support Compressed Parameter 621 | sed -i 's/$rowformat = "ROW_FORMAT=Compressed"/$rowformat = "ROW_FORMAT=Dynamic"/' /var/www/moodle/html/lib/dml/mysqli_native_moodle_database.php 622 | mode: 000500 623 | owner: root 624 | group: root 625 | commands: 626 | install_moodle: 627 | command: ./install_moodle.sh 628 | cwd: /tmp 629 | ignoreErrors: false 630 | 631 | install_efs_utils: 632 | packages: 633 | yum: 634 | git: [] 635 | rpm-build: [] 636 | files: 637 | /tmp/install_efs_utils.sh: 638 | content: !Sub | 639 | #!/bin/bash -xe 640 | git clone https://github.com/aws/efs-utils.git 641 | cd efs-utils 642 | make rpm 643 | yum -y install build/amazon-efs-utils*rpm 644 | mount -t efs ${ElasticFileSystem}:/data /var/www/moodle/data 645 | mount -t efs ${ElasticFileSystem}:/cache /var/www/moodle/cache 646 | mount -t efs ${ElasticFileSystem}:/temp /var/www/moodle/temp 647 | echo '${ElasticFileSystem}:/data /var/www/moodle/data efs defaults,_netdev 0 0' >> /etc/fstab 648 | echo '${ElasticFileSystem}:/cache /var/www/moodle/cache efs defaults,_netdev 0 0' >> /etc/fstab 649 | echo '${ElasticFileSystem}:/temp /var/www/moodle/temp efs defaults,_netdev 0 0' >> /etc/fstab 650 | 651 | mode: 000500 652 | owner: root 653 | group: root 654 | commands: 655 | install_efs_utils: 656 | command: ./install_efs_utils.sh 657 | cwd: /tmp 658 | ignoreErrors: false 659 | 660 | install_cacheclient: 661 | packages: 662 | yum: 663 | gcc-c++: [] 664 | files: 665 | /tmp/install_cacheclient.sh: 666 | content: 667 | !Sub | 668 | #!/bin/bash -xe 669 | 670 | #Install memcached and then remove it. Memcached is not actually needed. We install amazon-elasticache-cluster-client.so instead. However Moodle does not detect memcached is installed. Therefore, this tricks Moodle into thinking it is installed. 671 | sudo yum install -y php70-pecl-memcached 672 | sudo yum remove -y php70-pecl-memcached 673 | 674 | wget -P /tmp/ https://elasticache-downloads.s3.amazonaws.com/ClusterClient/PHP-7.0/latest-64bit 675 | tar -xf '/tmp/latest-64bit' 676 | cp '/tmp/artifact/amazon-elasticache-cluster-client.so' /usr/lib64/php/7.0/modules/ 677 | if [ ! -f /etc/php-7.0.d/50-memcached.ini ]; then 678 | touch /etc/php-7.0.d/50-memcached.ini 679 | fi 680 | echo 'extension=/usr/lib64/php/7.0/modules/amazon-elasticache-cluster-client.so;' >> /etc/php-7.0.d/50-memcached.ini 681 | echo 'extension=igbinary.so;' >> /etc/php-7.0.d/50-memcached.ini 682 | 683 | #update Moodle source to use DYNAMIC_CLIENT_MODE so Moodle can detect changes to the elasticache cluster membership 684 | sed -i '/\$this->options\[Memcached::OPT_BUFFER_WRITES\] = \$bufferwrites;/a \ \ \ \ \ \ \ \ $this->options[Memcached::OPT_CLIENT_MODE] = Memcached::DYNAMIC_CLIENT_MODE;' /var/www/moodle/html/cache/stores/memcached/lib.php 685 | 686 | mode: 000500 687 | owner: root 688 | group: root 689 | commands: 690 | install_cacheclient: 691 | command: ./install_cacheclient.sh 692 | cwd: /tmp 693 | ignoreErrors: false 694 | 695 | install_opcache: 696 | packages: 697 | yum: 698 | php-opcache: [] 699 | files: 700 | /tmp/install_opcache.sh: 701 | content: 702 | !Sub | 703 | #!/bin/bash -xe 704 | # create hidden opcache directory locally & change owner to apache 705 | if [ ! -d /var/www/.opcache ]; then 706 | mkdir -p /var/www/.opcache 707 | chown -R apache:apache /var/www/.opcache 708 | fi 709 | #Ensure opcache is enabled and add settings recomended by moodle at https://docs.moodle.org/38/en/OPcache 710 | sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php.d/10-opcache.ini 711 | sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=128/' /etc/php.d/10-opcache.ini 712 | sed -i 's/opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/' /etc/php.d/10-opcache.ini 713 | sed -i 's/;opcache.revalidate_freq=.*/opcache.revalidate_freq=60/' /etc/php.d/10-opcache.ini 714 | sed -i 's/;opcache.use_cwd=.*/opcache.use_cwd=1/' /etc/php.d/10-opcache.ini 715 | sed -i 's/;opcache.validate_timestamps=.*/opcache.validate_timestamps=1/' /etc/php.d/10-opcache.ini 716 | sed -i 's/;opcache.save_comments=.*/opcache.save_comments=1/' /etc/php.d/10-opcache.ini 717 | sed -i 's/;opcache.enable_file_override=.*/opcache.enable_file_override=0/' /etc/php.d/10-opcache.ini 718 | mode: 000500 719 | owner: root 720 | group: root 721 | commands: 722 | install_opcache: 723 | command: ./install_opcache.sh 724 | cwd: /tmp 725 | ignoreErrors: false 726 | 727 | start_webserver: 728 | services: 729 | sysvinit: 730 | php-fpm: 731 | enable: true 732 | ensureRunning: true 733 | httpd: 734 | enabled: true 735 | ensureRunning: true 736 | Properties: 737 | IamInstanceProfile: !Ref WebInstanceProfile 738 | ImageId: !Ref LatestAmiId 739 | InstanceMonitoring: true 740 | InstanceType: !Ref WebInstanceType 741 | KeyName: !Ref EC2KeyName 742 | SecurityGroups: 743 | - !Ref WebSecurityGroup 744 | UserData: 745 | "Fn::Base64": 746 | !Sub | 747 | #!/bin/bash -xe 748 | yum update -y 749 | amazon-linux-extras enable php7.2 750 | 751 | # Create directory structure 752 | mkdir -p /var/www/moodle/html 753 | mkdir -p /var/www/moodle/data 754 | mkdir -p /var/www/moodle/cache 755 | mkdir -p /var/www/moodle/temp 756 | mkdir -p /var/www/moodle/local 757 | 758 | #Mount shared storage 759 | # mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${ElasticFileSystem}.efs.${AWS::Region}.amazonaws.com:/data /var/www/moodle/data 760 | # mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${ElasticFileSystem}.efs.${AWS::Region}.amazonaws.com:/cache /var/www/moodle/cache 761 | # mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${ElasticFileSystem}.efs.${AWS::Region}.amazonaws.com:/temp /var/www/moodle/temp 762 | 763 | #Run CloudFormation Init Scripts 764 | /opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchConfiguration --region ${AWS::Region} 765 | /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region} 766 | 767 | 768 | Outputs: 769 | 770 | Opcachestatus: 771 | Value: !Join [ '', [ !Ref PublicAlbHostname, '/opcache-instanceid.php' ] ] 772 | -------------------------------------------------------------------------------- /deployment/build-s3-dist.sh: -------------------------------------------------------------------------------- 1 | # 2 | # This script should be run from the repo's deployment directory 3 | # cd deployment 4 | # ./build-s3-dist.sh source-bucket-base-name solution-name version-code template-bucket-name 5 | # 6 | # Parameters: 7 | # - source-bucket-base-name: Name for the S3 bucket location where the template will source the Lambda 8 | # code from. The template will append '-[region_name]' to this bucket name. 9 | # For example: ./build-s3-dist.sh solutions my-solution v1.0.0 10 | # The template will then expect the source code to be located in the solutions-[region_name] bucket 11 | # 12 | # - solution-name: name of the solution for consistency 13 | # 14 | # - version-code: version of the package 15 | # 16 | # - template-bucket-name: Name for S3 bucket location where the template will be located in. 17 | 18 | # Check to see if input has been provided: 19 | if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ]; then 20 | echo "Please provide the base source bucket name, trademark approved solution name and version where the lambda code will eventually reside." 21 | echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0 solutions-reference" 22 | exit 1 23 | fi 24 | 25 | # Get reference for all important folders 26 | template_dir="$PWD" 27 | template_dist_dir="$template_dir/global-s3-assets" 28 | build_dist_dir="$template_dir/regional-s3-assets" 29 | source_dir="$template_dir/../source" 30 | 31 | echo "------------------------------------------------------------------------------" 32 | echo "[Init] Clean old dist, node_modules and bower_components folders" 33 | echo "------------------------------------------------------------------------------" 34 | echo "rm -rf $template_dist_dir" 35 | rm -rf $template_dist_dir 36 | echo "mkdir -p $template_dist_dir" 37 | mkdir -p $template_dist_dir 38 | echo "rm -rf $build_dist_dir" 39 | rm -rf $build_dist_dir 40 | echo "mkdir -p $build_dist_dir" 41 | mkdir -p $build_dist_dir 42 | 43 | echo "------------------------------------------------------------------------------" 44 | echo "[Packing] Templates" 45 | echo "------------------------------------------------------------------------------" 46 | echo "cp $template_dir/*.template $template_dist_dir/" 47 | cp $template_dir/*.template $template_dist_dir/ 48 | echo "copy yaml templates and rename" 49 | cp $template_dir/*.yaml $template_dist_dir/ 50 | cd $template_dist_dir 51 | # Rename all *.yaml to *.template 52 | for f in *.yaml; do 53 | mv -- "$f" "${f%.yaml}.template" 54 | done 55 | 56 | cd .. 57 | if [[ "$OSTYPE" == "darwin"* ]]; then 58 | echo "Updating code source bucket in template with $1" 59 | 60 | replace="s/%%BUCKET_NAME%%/$1/g" 61 | echo "sed -i '' -e $replace $template_dist_dir/*.template" 62 | sed -i '' -e $replace $template_dist_dir/*.template 63 | 64 | replace="s/%%SOLUTION_NAME%%/$2/g" 65 | echo "sed -i '' -e $replace $template_dist_dir/*.template" 66 | sed -i '' -e $replace $template_dist_dir/*.template 67 | 68 | replace="s/%%VERSION%%/$3/g" 69 | echo "sed -i '' -e $replace $template_dist_dir/*.template" 70 | sed -i '' -e $replace $template_dist_dir/*.template 71 | 72 | replace="s/%%TEMPLATE_BUCKET_NAME%%/$4/g" 73 | echo "sed -i '' -e $replace $template_dist_dir/*.template" 74 | sed -i '' -e $replace $template_dist_dir/*.template 75 | else 76 | echo "Updating code source bucket in template with $1" 77 | 78 | replace="s/%%BUCKET_NAME%%/$1/g" 79 | echo "sed -i -e $replace $template_dist_dir/*.template" 80 | sed -i -e $replace $template_dist_dir/*.template 81 | 82 | replace="s/%%SOLUTION_NAME%%/$2/g" 83 | echo "sed -i -e $replace $template_dist_dir/*.template" 84 | sed -i -e $replace $template_dist_dir/*.template 85 | 86 | replace="s/%%VERSION%%/$3/g" 87 | echo "sed -i -e $replace $template_dist_dir/*.template" 88 | sed -i -e $replace $template_dist_dir/*.template 89 | 90 | replace="s/%%TEMPLATE_BUCKET_NAME%%/$4/g" 91 | echo "sed -i -e $replace $template_dist_dir/*.template" 92 | sed -i -e $replace $template_dist_dir/*.template 93 | fi -------------------------------------------------------------------------------- /deployment/regional-s3-assets/tmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/moodle-on-aws-cn/edf9c8bacaaa1f50754d472e7c7480e39fa81a0d/deployment/regional-s3-assets/tmp -------------------------------------------------------------------------------- /deployment/run-unit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This assumes all of the OS-level configuration has been completed and git repo has already been cloned 4 | # 5 | # This script should be run from the repo's deployment directory 6 | # cd deployment 7 | # ./run-unit-tests.sh 8 | # 9 | 10 | # Get reference for all important folders 11 | template_dir="$PWD" 12 | source_dir="$template_dir/../source" 13 | 14 | echo "------------------------------------------------------------------------------" 15 | echo "[Init] Clean old dist and node_modules folders" 16 | echo "------------------------------------------------------------------------------" 17 | echo "find $source_dir/services -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null" 18 | find $source_dir/services -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null 19 | echo "find $source_dir/services -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null" 20 | find $source_dir/services -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null 21 | echo "find ../ -type f -name 'package-lock.json' -delete" 22 | find $source_dir/services -type f -name 'package-lock.json' -delete 23 | echo "find $source_dir/resources -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null" 24 | find $source_dir/resources -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null 25 | echo "find $source_dir/resources -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null" 26 | find $source_dir/resources -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null 27 | echo "find ../ -type f -name 'package-lock.json' -delete" 28 | find $source_dir/resources -type f -name 'package-lock.json' -delete 29 | echo "find $source_dir/simulator -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null" 30 | find $source_dir/simulator -iname "node_modules" -type d -exec rm -r "{}" \; 2> /dev/null 31 | echo "find $source_dir/simulator -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null" 32 | find $source_dir/simulator -iname "dist" -type d -exec rm -r "{}" \; 2> /dev/null 33 | echo "find ../ -type f -name 'package-lock.json' -delete" 34 | find $source_dir/simulator -type f -name 'package-lock.json' -delete 35 | 36 | echo "------------------------------------------------------------------------------" 37 | echo "[Test] Services - Example Function" 38 | echo "------------------------------------------------------------------------------" 39 | cd $source_dir/example-function-js 40 | npm install 41 | npm test 42 | -------------------------------------------------------------------------------- /launch-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/moodle-on-aws-cn/edf9c8bacaaa1f50754d472e7c7480e39fa81a0d/launch-stack.png -------------------------------------------------------------------------------- /launch-stack.svg: -------------------------------------------------------------------------------- 1 | Launch Stack --------------------------------------------------------------------------------