├── .gitignore ├── NOTICE ├── images ├── aws-refarch-moodle-caching.png ├── cloudformation-launch-stack.png └── aws-refarch-moodle-architecture.png ├── .github └── PULL_REQUEST_TEMPLATE.md ├── CODE_OF_CONDUCT.md ├── templates ├── 05-route53.yaml ├── 03-efsfilesystem.yaml ├── 03-elasticacheserverless.yaml ├── 04-cloudfront.yaml ├── 02-securitygroups.yaml ├── 03-rdsserverless.yaml ├── 03-elasticache.yaml ├── 03-rds.yaml ├── 05-codepipeline.yaml ├── 03-publicalb.yaml ├── 01-newvpc.yaml ├── 04-web.yaml ├── 03-pipelinehelper.yaml └── 00-main.yaml ├── CONTRIBUTING.md ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /images/aws-refarch-moodle-caching.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-refarch-moodle/HEAD/images/aws-refarch-moodle-caching.png -------------------------------------------------------------------------------- /images/cloudformation-launch-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-refarch-moodle/HEAD/images/cloudformation-launch-stack.png -------------------------------------------------------------------------------- /images/aws-refarch-moodle-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-refarch-moodle/HEAD/images/aws-refarch-moodle-architecture.png -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /templates/05-route53.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle DNS using Route 53 4 | 5 | Parameters: 6 | DnsEndpoint: 7 | AllowedPattern: ^(?!http)(([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])$ 8 | Description: The DNS endpoint - CloudFront DNS if using CloudFront else Public ALB DNS name. 9 | Type: String 10 | DnsHostId: 11 | AllowedPattern: ^[A-Z0-9]+$ 12 | Description: The DNS host zone id - 'Z2FDTNDATAQYW2' if using CloudFront else Public ALB host zone id. 13 | Type: String 14 | HostedZoneName: 15 | AllowedPattern: ^(?!http)(([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])$ 16 | Description: The main Route 53 hosted zone (e.g. example.edu). 17 | Type: String 18 | DomainName: 19 | AllowedPattern: ^(?!http)(([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])$ 20 | Description: The main domain name of the Moodle site (e.g. moodle.example.edu). 21 | Type: String 22 | 23 | Resources: 24 | RecordSet: 25 | Type: AWS::Route53::RecordSetGroup 26 | Properties: 27 | HostedZoneName: !Join ['', [!Ref HostedZoneName, '.']] 28 | RecordSets: 29 | - Name: !Join ['', [!Ref DomainName, '.']] 30 | Type: A 31 | AliasTarget: 32 | DNSName: !Ref DnsEndpoint 33 | EvaluateTargetHealth: True 34 | HostedZoneId: !Ref DnsHostId 35 | -------------------------------------------------------------------------------- /templates/03-efsfilesystem.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle EFS shared file system 4 | 5 | Parameters: 6 | Cmk: 7 | Description: An existing AWS KMS Customer Master Key (CMK) to encrypt file system 8 | Type: String 9 | NumberOfSubnets: 10 | AllowedValues: 11 | - 1 12 | - 2 13 | - 3 14 | Default: 2 15 | Description: Number of subnets. This must match your selections in the list of Subnets below. 16 | Type: String 17 | SecurityGroup: 18 | Description: Select the Amazon EFS security group. 19 | Type: AWS::EC2::SecurityGroup::Id 20 | Subnet: 21 | Description: Select existing subnets. 22 | Type: List 23 | ProjectName: 24 | AllowedPattern: ^([a-zA-Z0-9]*)$ 25 | Default: App 26 | Description: The Moodle Project Name 27 | Type: String 28 | 29 | Conditions: 30 | NumberOfSubnets1: 31 | !Equals [ 1, !Ref NumberOfSubnets ] 32 | NumberOfSubnets2: 33 | !Equals [ 2, !Ref NumberOfSubnets ] 34 | NumberOfSubnets3: 35 | !Equals [ 3, !Ref NumberOfSubnets ] 36 | Subnet0: !Or 37 | - !Condition NumberOfSubnets1 38 | - !Condition NumberOfSubnets2 39 | - !Condition NumberOfSubnets3 40 | Subnet1: !Or 41 | - !Condition NumberOfSubnets2 42 | - !Condition NumberOfSubnets3 43 | Subnet2: !Condition NumberOfSubnets3 44 | UseAWS-ManagedCMK: 45 | !Equals ['', !Ref Cmk] 46 | 47 | Resources: 48 | ElasticFileSystem: 49 | Type: AWS::EFS::FileSystem 50 | Properties: 51 | Encrypted: true 52 | KmsKeyId: !If [ UseAWS-ManagedCMK, !Ref 'AWS::NoValue', !Ref Cmk ] 53 | PerformanceMode: generalPurpose 54 | ThroughputMode: elastic 55 | 56 | ElasticFileSystemMountTarget0: 57 | Condition: Subnet0 58 | Type: AWS::EFS::MountTarget 59 | Properties: 60 | FileSystemId: !Ref ElasticFileSystem 61 | SecurityGroups: 62 | - !Ref SecurityGroup 63 | SubnetId: !Select [ 0, !Ref Subnet ] 64 | 65 | ElasticFileSystemMountTarget1: 66 | Condition: Subnet1 67 | Type: AWS::EFS::MountTarget 68 | Properties: 69 | FileSystemId: !Ref ElasticFileSystem 70 | SecurityGroups: 71 | - !Ref SecurityGroup 72 | SubnetId: !Select [ 1, !Ref Subnet ] 73 | 74 | ElasticFileSystemMountTarget2: 75 | Condition: Subnet2 76 | Type: AWS::EFS::MountTarget 77 | Properties: 78 | FileSystemId: !Ref ElasticFileSystem 79 | SecurityGroups: 80 | - !Ref SecurityGroup 81 | SubnetId: !Select [ 2, !Ref Subnet ] 82 | 83 | ElasticFileSystemParam: 84 | Type: AWS::SSM::Parameter 85 | Properties: 86 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/SharedFile/ElasticFileSystem' ] ] 87 | Type: String 88 | Value: !Ref ElasticFileSystem 89 | Description: Moodle EFS File System Id 90 | 91 | Outputs: 92 | ElasticFileSystem: 93 | Value: !Ref ElasticFileSystem 94 | ElasticFileSystemDnsName: 95 | Description: DNS name for the Amazon EFS file system. 96 | Value: !Join [ '.', [ !Ref ElasticFileSystem, 'efs', !Ref 'AWS::Region', 'amazonaws', 'com' ] ] 97 | -------------------------------------------------------------------------------- /templates/03-elasticacheserverless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle serverless cache cluster 4 | 5 | Parameters: 6 | CacheEngineType: 7 | AllowedValues: 8 | - Redis 9 | - Memcached 10 | Default: Memcached 11 | Description: Cache engine to use. 12 | Type: String 13 | CacheUsageType: 14 | AllowedValues: 15 | - application 16 | - session 17 | Default: session 18 | Description: Session or application caching. 19 | Type: String 20 | ElastiCacheClusterName: 21 | AllowedPattern: ^([a-zA-Z0-9]*)$ 22 | Description: ElastiCache cluster name. 23 | Type: String 24 | ElastiCacheNodeType: 25 | Description: Unused, only kept for compatibility with the non-serverless template. 26 | Type: String 27 | Default: cache.t2.micro 28 | ElastiCacheSecurityGroup: 29 | Description: ElastiCache Security Group. 30 | Type: AWS::EC2::SecurityGroup::Id 31 | NumberOfSubnets: 32 | AllowedValues: 33 | - 1 34 | - 2 35 | - 3 36 | Default: 2 37 | Description: Number of subnets. This must match your selections in the list of subnets below. 38 | Type: String 39 | Subnet: 40 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 41 | Type: List 42 | ProjectName: 43 | AllowedPattern: ^([a-zA-Z0-9]*)$ 44 | Default: App 45 | Description: Moodle Project Name 46 | Type: String 47 | 48 | Conditions: 49 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 50 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 51 | 52 | Resources: 53 | ElastiCacheServerless: 54 | Type: AWS::ElastiCache::ServerlessCache 55 | Properties: 56 | Engine: !Ref CacheEngineType 57 | ServerlessCacheName: !Ref ElastiCacheClusterName 58 | SecurityGroupIds: 59 | - !Ref ElastiCacheSecurityGroup 60 | SubnetIds: !If [ NumberOfSubnets1, [ !Select [ 0, !Ref Subnet ] ], 61 | !If [ NumberOfSubnets2, [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 62 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] ] ] 63 | 64 | ElastiCacheClusterEndpoint: 65 | Type: AWS::SSM::Parameter 66 | Properties: 67 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/Cache/', !Ref CacheUsageType, '/ElastiCacheClusterEndpoint' ] ] 68 | Description: ElastiCache Cluster Endpoint 69 | Type: String 70 | Value: !Join [ ':', [!GetAtt ElastiCacheServerless.Endpoint.Address, !GetAtt ElastiCacheServerless.Endpoint.Port]] 71 | 72 | ElastiCacheEngine: 73 | Type: AWS::SSM::Parameter 74 | Properties: 75 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/Cache/', !Ref CacheUsageType, '/Engine' ] ] 76 | Description: !Sub ElastiCache Engine Type (${CacheUsageType}) 77 | Type: String 78 | Value: !Ref CacheEngineType 79 | 80 | Outputs: 81 | ElastiCacheClusterEndpointAddress: 82 | Value: !Join [ ':', [!GetAtt ElastiCacheServerless.Endpoint.Address, !GetAtt ElastiCacheServerless.Endpoint.Port]] 83 | -------------------------------------------------------------------------------- /templates/04-cloudfront.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle CloudFront distribution 4 | 5 | Parameters: 6 | CloudFrontAcmCertificate: 7 | AllowedPattern: ^$|(arn:aws:acm:)([a-z0-9/:-])*([a-z0-9])$ 8 | 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.' 9 | Type: String 10 | PublicAlbDnsName: 11 | Description: The public application load balancer dns name. 12 | Type: String 13 | DomainName: 14 | 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])$ 15 | Description: '[ Optional ] The main domain name of the Moodle site (e.g. moodle.example.edu).' 16 | Type: String 17 | 18 | Conditions: 19 | SslCertificate: !Not [ !Equals [ '', !Ref CloudFrontAcmCertificate ] ] 20 | NoSslCertificate: !Equals [ '', !Ref CloudFrontAcmCertificate ] 21 | DomainName: !Not [ !Equals [ '', !Ref DomainName ] ] 22 | NoDomainName: !Equals [ '', !Ref DomainName ] 23 | 24 | Resources: 25 | CloudFrontDistributionNoSslCertificate: 26 | Type: AWS::CloudFront::Distribution 27 | Condition: NoSslCertificate 28 | Properties: 29 | DistributionConfig: 30 | Comment: !Ref 'AWS::StackName' 31 | DefaultCacheBehavior: 32 | AllowedMethods: 33 | - DELETE 34 | - GET 35 | - HEAD 36 | - OPTIONS 37 | - PATCH 38 | - POST 39 | - PUT 40 | DefaultTTL: 0 41 | MaxTTL: 0 42 | MinTTL: 0 43 | ForwardedValues: 44 | QueryString: true 45 | Headers: 46 | - '*' 47 | Cookies: 48 | Forward: all 49 | TargetOriginId: alb 50 | ViewerProtocolPolicy: redirect-to-https 51 | Compress: true 52 | Enabled: true 53 | Origins: 54 | - DomainName: !Ref PublicAlbDnsName 55 | Id: alb 56 | CustomOriginConfig: 57 | OriginProtocolPolicy: http-only 58 | PriceClass: PriceClass_All 59 | HttpVersion: http2and3 60 | ViewerCertificate: 61 | CloudFrontDefaultCertificate: true 62 | MinimumProtocolVersion: TLSv1.2_2021 63 | 64 | CloudFrontDistributionSslCertificate: 65 | Type: AWS::CloudFront::Distribution 66 | Condition: SslCertificate 67 | Properties: 68 | DistributionConfig: 69 | Aliases: 70 | - !If [ DomainName, !Ref DomainName, !Ref 'AWS::NoValue' ] 71 | Comment: !Ref 'AWS::StackName' 72 | DefaultCacheBehavior: 73 | AllowedMethods: 74 | - DELETE 75 | - GET 76 | - HEAD 77 | - OPTIONS 78 | - PATCH 79 | - POST 80 | - PUT 81 | DefaultTTL: 0 82 | MaxTTL: 0 83 | MinTTL: 0 84 | ForwardedValues: 85 | QueryString: true 86 | Headers: 87 | - '*' 88 | Cookies: 89 | Forward: all 90 | TargetOriginId: alb 91 | ViewerProtocolPolicy: redirect-to-https 92 | Compress: true 93 | Enabled: true 94 | Origins: 95 | - DomainName: !Ref PublicAlbDnsName 96 | Id: alb 97 | CustomOriginConfig: 98 | OriginProtocolPolicy: http-only 99 | PriceClass: PriceClass_All 100 | HttpVersion: http2and3 101 | ViewerCertificate: 102 | AcmCertificateArn: !Ref CloudFrontAcmCertificate 103 | SslSupportMethod: sni-only 104 | MinimumProtocolVersion: TLSv1.2_2021 105 | 106 | Outputs: 107 | DnsName: 108 | Value: !If [ NoSslCertificate, !GetAtt CloudFrontDistributionNoSslCertificate.DomainName, !GetAtt CloudFrontDistributionSslCertificate.DomainName ] 109 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/aws-refarch-moodle/issues), or [recently closed](https://github.com/aws-samples/aws-refarch-moodle/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/aws-refarch-moodle/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/aws-refarch-moodle/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | 63 | ## Authors 64 | 65 | Names in alphabetical orders 66 | 67 | v1.0 68 | - Diego Magalhães 69 | - gamerf 70 | - Henri Yandell 71 | - jtrollin 72 | - MathieuJeandron 73 | - StephaniePar 74 | - Thiago Pádua 75 | 76 | v2.0 77 | - Irshad Chohan 78 | - Norman Owens 79 | - Preenesh Nayanasudhan 80 | - Stephen Smith 81 | - Vincent Rioux 82 | 83 | -------------------------------------------------------------------------------- /templates/02-securitygroups.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle security groups 4 | 5 | Parameters: 6 | Vpc: 7 | AllowedPattern: ^(vpc-)([a-z0-9]{8}|[a-z0-9]{17})$ 8 | Description: VPC Id of an existing VPC. 9 | Type: AWS::EC2::VPC::Id 10 | DatabaseType: 11 | AllowedValues: 12 | - MySQL 13 | - PostgreSQL 14 | Default: PostgreSQL 15 | Description: Database engine to use. 16 | Type: String 17 | ElastiCacheType: 18 | AllowedValues: 19 | - Redis 20 | - Memcached 21 | Default: Memcached 22 | Description: Cache engine to use. 23 | Type: String 24 | 25 | Conditions: 26 | UsePostgreSQL: 27 | !Equals [!Ref DatabaseType, PostgreSQL] 28 | UseRedis: 29 | !Equals [!Ref ElastiCacheType, Redis] 30 | 31 | Resources: 32 | DatabaseSecurityGroup: 33 | Type: AWS::EC2::SecurityGroup 34 | Properties: 35 | GroupDescription: Database Security Group 36 | SecurityGroupIngress: 37 | - IpProtocol: tcp 38 | Description: "Allow database access from instances in the Web Security Group" 39 | FromPort: !If [ UsePostgreSQL, 5432, 3306 ] 40 | ToPort: !If [ UsePostgreSQL, 5432, 3306 ] 41 | SourceSecurityGroupId: !Ref WebSecurityGroup 42 | SecurityGroupEgress: 43 | - IpProtocol: -1 44 | Description: "Block all egress" 45 | CidrIp: 127.0.0.1/32 46 | VpcId: !Ref Vpc 47 | 48 | ElastiCacheSecurityGroup: 49 | Type: AWS::EC2::SecurityGroup 50 | Properties: 51 | GroupDescription: Cache Security Group 52 | SecurityGroupIngress: 53 | - IpProtocol: tcp 54 | Description: "Allow cache engine access from instances in the Web Security Group" 55 | FromPort: !If [ UseRedis, 6379, 11211 ] 56 | ToPort: !If [ UseRedis, 6379, 11211 ] 57 | SourceSecurityGroupId: !Ref WebSecurityGroup 58 | SecurityGroupEgress: 59 | - IpProtocol: -1 60 | Description: "Block all egress" 61 | CidrIp: 127.0.0.1/32 62 | VpcId: !Ref Vpc 63 | 64 | EfsSecurityGroup: 65 | Type: AWS::EC2::SecurityGroup 66 | Properties: 67 | GroupDescription: EFS Security Group 68 | SecurityGroupIngress: 69 | - IpProtocol: tcp 70 | Description: "Allow NFS access from instances in the Web Security Group" 71 | FromPort: 2049 72 | ToPort: 2049 73 | SourceSecurityGroupId: !Ref WebSecurityGroup 74 | SecurityGroupEgress: 75 | - IpProtocol: -1 76 | Description: "Block all egress" 77 | CidrIp: 127.0.0.1/32 78 | VpcId: !Ref Vpc 79 | 80 | EfsSecurityGroupIngress: 81 | Type: AWS::EC2::SecurityGroupIngress 82 | Properties: 83 | IpProtocol: tcp 84 | Description: "Allow NFS access from instances in EFS Security Group" 85 | FromPort: 2049 86 | ToPort: 2049 87 | SourceSecurityGroupId: !GetAtt EfsSecurityGroup.GroupId 88 | GroupId: !GetAtt EfsSecurityGroup.GroupId 89 | 90 | PublicAlbSecurityGroup: 91 | Type: AWS::EC2::SecurityGroup 92 | Properties: 93 | GroupDescription: Load balancer Security Group 94 | SecurityGroupIngress: 95 | - IpProtocol: tcp 96 | Description: "Allow https access from everywhere" 97 | FromPort: 443 98 | ToPort: 443 99 | CidrIp: 0.0.0.0/0 100 | - IpProtocol: tcp 101 | Description: "Allow http access from everywhere" 102 | FromPort: 80 103 | ToPort: 80 104 | CidrIp: 0.0.0.0/0 105 | SecurityGroupEgress: 106 | - IpProtocol: -1 107 | Description: "Block all egress" 108 | CidrIp: 127.0.0.1/32 109 | VpcId: !Ref Vpc 110 | 111 | PublicAlbSecurityGroupEgressHttp: 112 | Type: AWS::EC2::SecurityGroupEgress 113 | Properties: 114 | IpProtocol: tcp 115 | Description: "Allow http access to Web security group" 116 | FromPort: 80 117 | ToPort: 80 118 | DestinationSecurityGroupId: !GetAtt WebSecurityGroup.GroupId 119 | GroupId: !GetAtt PublicAlbSecurityGroup.GroupId 120 | 121 | PublicAlbSecurityGroupEgressHttps: 122 | Type: AWS::EC2::SecurityGroupEgress 123 | Properties: 124 | IpProtocol: tcp 125 | Description: "Allow http access to Web security group" 126 | FromPort: 443 127 | ToPort: 443 128 | DestinationSecurityGroupId: !GetAtt WebSecurityGroup.GroupId 129 | GroupId: !GetAtt PublicAlbSecurityGroup.GroupId 130 | 131 | WebSecurityGroup: 132 | Type: AWS::EC2::SecurityGroup 133 | Properties: 134 | GroupDescription: Web instances Security Group 135 | SecurityGroupIngress: 136 | - IpProtocol: tcp 137 | Description: "Allow http access from load balancer Security Group" 138 | FromPort: 80 139 | ToPort: 80 140 | SourceSecurityGroupId: !Ref PublicAlbSecurityGroup 141 | - IpProtocol: tcp 142 | Description: "Allow https access from load balancer Security Group" 143 | FromPort: 443 144 | ToPort: 443 145 | SourceSecurityGroupId: !Ref PublicAlbSecurityGroup 146 | SecurityGroupEgress: 147 | - IpProtocol: "-1" 148 | Description: "Allow outgoing access from web instances to Internet (download packages, updates)" 149 | CidrIp: 0.0.0.0/0 150 | VpcId: !Ref Vpc 151 | 152 | Outputs: 153 | DatabaseSecurityGroup: 154 | Value: !Ref DatabaseSecurityGroup 155 | EfsSecurityGroup: 156 | Value: !Ref EfsSecurityGroup 157 | ElastiCacheSecurityGroup: 158 | Value: !Ref ElastiCacheSecurityGroup 159 | PublicAlbSecurityGroup: 160 | Value: !Ref PublicAlbSecurityGroup 161 | WebSecurityGroup: 162 | Value: !Ref WebSecurityGroup 163 | -------------------------------------------------------------------------------- /templates/03-rdsserverless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle serverless database 4 | 5 | Parameters: 6 | DatabaseType: 7 | AllowedValues: 8 | - MySQL 9 | - PostgreSQL 10 | Default: PostgreSQL 11 | Description: Database engine to use. 12 | Type: String 13 | DatabaseCmk: 14 | Description: AWS KMS Customer Master Key (CMK) to encrypt database cluster 15 | Type: String 16 | RDSInstanceSecretArn: 17 | Type: String 18 | DatabaseName: 19 | AllowedPattern: ^([a-zA-Z0-9]*)$ 20 | Description: Amazon Aurora RDS master database name 21 | Type: String 22 | DatabaseSecurityGroup: 23 | Description: Database security group 24 | Type: AWS::EC2::SecurityGroup::Id 25 | NumberOfSubnets: 26 | AllowedValues: 27 | - 1 28 | - 2 29 | - 3 30 | Default: 2 31 | Description: Number of subnets. This must match your selections in the list of subnets below. 32 | Type: String 33 | Subnet: 34 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 35 | Type: List 36 | ProjectName: 37 | AllowedPattern: ^([a-zA-Z0-9]*)$ 38 | Default: App 39 | Description: Moodle Project Name 40 | Type: String 41 | DatabaseMinCapacity: 42 | AllowedValues: 43 | - 0.5 44 | - 1 45 | - 2 46 | - 4 47 | - 5 48 | - 16 49 | - 32 50 | - 64 51 | - 128 52 | Default: 0.5 53 | Description: The minimum capacity for an Aurora DB cluster, starts with 0.5 and maximum up to 1 54 | Type: String 55 | DatabaseMaxCapacity: 56 | AllowedValues: 57 | - 0.5 58 | - 1 59 | - 2 60 | - 4 61 | - 5 62 | - 16 63 | - 32 64 | - 64 65 | - 128 66 | Default: 64 67 | Description: The maximum capacity for an Aurora DB cluster, starts with 0.5 and maximum up to 1 68 | Type: String 69 | 70 | Conditions: 71 | UseMySQL: !Equals [!Ref DatabaseType, MySQL] 72 | UsePostgreSQL: !Equals [!Ref DatabaseType, PostgreSQL] 73 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 74 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 75 | UseAWS-ManagedCMK: !Equals ['', !Ref DatabaseCmk] 76 | 77 | Resources: 78 | DatabaseCluster: 79 | Type: AWS::RDS::DBCluster 80 | Properties: 81 | BackupRetentionPeriod: 30 82 | DatabaseName: !Ref DatabaseName 83 | DBSubnetGroupName: !Ref DataSubnetGroup 84 | Engine: !If [ UsePostgreSQL, aurora-postgresql, aurora-mysql ] 85 | Port: !If [ UsePostgreSQL, 5432, 3306 ] 86 | KmsKeyId: !If [ UseAWS-ManagedCMK, !Ref 'AWS::NoValue', !Ref DatabaseCmk ] 87 | MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecretArn, ':SecretString:username}}' ]] 88 | MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecretArn, ':SecretString:password}}' ]] 89 | StorageEncrypted: true 90 | VpcSecurityGroupIds: 91 | - !Ref DatabaseSecurityGroup 92 | ServerlessV2ScalingConfiguration: 93 | MinCapacity: !Ref DatabaseMinCapacity 94 | MaxCapacity: !Ref DatabaseMaxCapacity 95 | 96 | DatabaseInstance0: 97 | Type: AWS::RDS::DBInstance 98 | DeletionPolicy: Delete 99 | Properties: 100 | AllowMajorVersionUpgrade: false 101 | AutoMinorVersionUpgrade: true 102 | DBClusterIdentifier: !Ref DatabaseCluster 103 | DBInstanceClass: 'db.serverless' 104 | DBSubnetGroupName: !Ref DataSubnetGroup 105 | Engine: !If [ UsePostgreSQL, aurora-postgresql, aurora-mysql ] 106 | PubliclyAccessible: false 107 | 108 | DataSubnetGroup: 109 | Type: AWS::RDS::DBSubnetGroup 110 | Properties: 111 | DBSubnetGroupDescription: RDS Database Subnet Group for Moodle 112 | SubnetIds: !If [ NumberOfSubnets1, [ !Select [ 0, !Ref Subnet ] ], 113 | !If [ NumberOfSubnets2, [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 114 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] ] ] 115 | 116 | DatabaseClusterParam: 117 | Type: AWS::SSM::Parameter 118 | Properties: 119 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/ClusterEndpoint' ] ] 120 | Type: String 121 | Value: !GetAtt DatabaseCluster.Endpoint.Address 122 | Description: Moodle DB Cluster Endpoint 123 | 124 | DatabaseClusterReadOnlyParam: 125 | Type: AWS::SSM::Parameter 126 | Properties: 127 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/ClusterReadOnlyEndpoint' ] ] 128 | Type: String 129 | Value: !GetAtt DatabaseCluster.ReadEndpoint.Address 130 | Description: Moodle DB Cluster Read only Endpoint 131 | 132 | DatabaseNameParam: 133 | Type: AWS::SSM::Parameter 134 | Properties: 135 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/Name' ] ] 136 | Type: String 137 | Value: !Ref DatabaseName 138 | Description: Moodle DB Name 139 | 140 | DatabaseTypeParam: 141 | Type: AWS::SSM::Parameter 142 | Properties: 143 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/Type' ] ] 144 | Type: String 145 | Value: !Ref DatabaseType 146 | Description: for Moodle DB Type 147 | 148 | Outputs: 149 | Database: 150 | Value: !Ref DatabaseCluster 151 | DatabaseName: 152 | Value: !Ref DatabaseName 153 | DataSubnetGroup: 154 | Value: !Ref DataSubnetGroup 155 | DatabaseClusterEndpointAddress: 156 | Value: !GetAtt DatabaseCluster.Endpoint.Address 157 | DatabaseClusterEndpointPort: 158 | Value: !GetAtt DatabaseCluster.Endpoint.Port 159 | DatabaseClusterReadEndpointAddress: 160 | Value: !GetAtt DatabaseCluster.ReadEndpoint.Address 161 | -------------------------------------------------------------------------------- /templates/03-elasticache.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle cache cluster 4 | 5 | Parameters: 6 | CacheEngineType: 7 | AllowedValues: 8 | - Redis 9 | - Memcached 10 | Default: Memcached 11 | Description: Cache engine to use. 12 | Type: String 13 | CacheUsageType: 14 | AllowedValues: 15 | - application 16 | - session 17 | Default: session 18 | Description: Session or application caching. 19 | Type: String 20 | ElastiCacheClusterName: 21 | AllowedPattern: ^([a-zA-Z0-9]*)$ 22 | Description: ElastiCache cluster name. 23 | Type: String 24 | ElastiCacheNodeType: 25 | AllowedValues: 26 | - cache.t2.micro 27 | - cache.t2.small 28 | - cache.t2.medium 29 | - cache.t3.micro 30 | - cache.t3.small 31 | - cache.t3.medium 32 | - cache.t4g.micro 33 | - cache.t4g.small 34 | - cache.t4g.medium 35 | - cache.m4.large 36 | - cache.m4.xlarge 37 | - cache.m4.2xlarge 38 | - cache.m4.4xlarge 39 | - cache.m4.10xlarge 40 | - cache.m5.large 41 | - cache.m5.xlarge 42 | - cache.m5.2xlarge 43 | - cache.m5.4xlarge 44 | - cache.m5.12xlarge 45 | - cache.m5.24xlarge 46 | - cache.m6g.large 47 | - cache.m6g.xlarge 48 | - cache.m6g.2xlarge 49 | - cache.m6g.4xlarge 50 | - cache.m6g.8xlarge 51 | - cache.m6g.12xlarge 52 | - cache.m6g.16xlarge 53 | - cache.r4.large 54 | - cache.r4.xlarge 55 | - cache.r4.2xlarge 56 | - cache.r4.4xlarge 57 | - cache.r4.8xlarge 58 | - cache.r4.16xlarge 59 | - cache.r5.large 60 | - cache.r5.xlarge 61 | - cache.r5.2xlarge 62 | - cache.r5.4xlarge 63 | - cache.r5.12xlarge 64 | - cache.r5.24xlarge 65 | - cache.r6g.large 66 | - cache.r6g.xlarge 67 | - cache.r6g.2xlarge 68 | - cache.r6g.4xlarge 69 | - cache.r6g.8xlarge 70 | - cache.r6g.12xlarge 71 | - cache.r6g.16xlarge 72 | - cache.r6gd.xlarge 73 | - cache.r6gd.2xlarge 74 | - cache.r6gd.4xlarge 75 | - cache.r6gd.8xlarge 76 | - cache.r6gd.12xlarge 77 | - cache.r6gd.16xlarge 78 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 79 | Default: cache.r6g.large 80 | Description: ElastiCache cluster node type. 81 | Type: String 82 | ElastiCacheSecurityGroup: 83 | Description: ElastiCache Security Group. 84 | Type: AWS::EC2::SecurityGroup::Id 85 | NumberOfSubnets: 86 | AllowedValues: 87 | - 1 88 | - 2 89 | - 3 90 | Default: 2 91 | Description: Number of subnets. This must match your selections in the list of subnets below. 92 | Type: String 93 | Subnet: 94 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 95 | Type: List 96 | ProjectName: 97 | AllowedPattern: ^([a-zA-Z0-9]*)$ 98 | Default: App 99 | Description: Moodle Project Name 100 | Type: String 101 | 102 | Conditions: 103 | UseRedis: !Equals [!Ref CacheEngineType, Redis] 104 | UseMemcached: !Equals [!Ref CacheEngineType, Memcached] 105 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 106 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 107 | 108 | Resources: 109 | ElastiCacheClusterRedis: 110 | Condition: UseRedis 111 | Type: AWS::ElastiCache::ReplicationGroup 112 | Properties: 113 | ReplicationGroupId: !Ref ElastiCacheClusterName 114 | ReplicationGroupDescription: !Ref ElastiCacheClusterName 115 | AtRestEncryptionEnabled: true 116 | TransitEncryptionEnabled: true 117 | AutoMinorVersionUpgrade: true 118 | MultiAZEnabled: true 119 | CacheNodeType: !Ref ElastiCacheNodeType 120 | CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup 121 | SecurityGroupIds: 122 | - !Ref ElastiCacheSecurityGroup 123 | Engine: redis 124 | NumCacheClusters: !Ref NumberOfSubnets 125 | 126 | ElastiCacheClusterMemcached: 127 | Condition: UseMemcached 128 | Type: AWS::ElastiCache::CacheCluster 129 | Properties: 130 | AZMode: cross-az 131 | CacheNodeType: !Ref ElastiCacheNodeType 132 | CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup 133 | ClusterName: !Ref ElastiCacheClusterName 134 | Engine: memcached 135 | NumCacheNodes: !Ref NumberOfSubnets 136 | VpcSecurityGroupIds: 137 | - !Ref ElastiCacheSecurityGroup 138 | 139 | ElastiCacheSubnetGroup: 140 | Type: AWS::ElastiCache::SubnetGroup 141 | Properties: 142 | CacheSubnetGroupName: !Join [ '', [ !Ref ElastiCacheClusterName, SubnetGroup ] ] 143 | Description: ElastiCache Subnet Group for Moodle 144 | SubnetIds: !If [ NumberOfSubnets1, [ !Select [ 0, !Ref Subnet ] ], 145 | !If [ NumberOfSubnets2, [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 146 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] ] ] 147 | 148 | ElastiCacheClusterEndpoint: 149 | Type: AWS::SSM::Parameter 150 | Properties: 151 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/Cache/', !Ref CacheUsageType, '/ElastiCacheClusterEndpoint' ] ] 152 | Description: ElastiCache Cluster Endpoint 153 | Type: String 154 | Value: !If [UseRedis, !Join [ ':', [!GetAtt ElastiCacheClusterRedis.PrimaryEndPoint.Address, !GetAtt ElastiCacheClusterRedis.PrimaryEndPoint.Port]], 155 | !Join [ ':', [!GetAtt ElastiCacheClusterMemcached.ConfigurationEndpoint.Address, !GetAtt ElastiCacheClusterMemcached.ConfigurationEndpoint.Port]] ] 156 | 157 | ElastiCacheEngine: 158 | Type: AWS::SSM::Parameter 159 | Properties: 160 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/Cache/', !Ref CacheUsageType, '/Engine' ] ] 161 | Description: !Sub ElastiCache Engine Type (${CacheUsageType}) 162 | Type: String 163 | Value: !Ref CacheEngineType 164 | 165 | Outputs: 166 | ElastiCacheClusterEndpointAddress: 167 | Value: !If [UseRedis, !Join [ ':', [!GetAtt ElastiCacheClusterRedis.PrimaryEndPoint.Address, !GetAtt ElastiCacheClusterRedis.PrimaryEndPoint.Port]], 168 | !Join [ ':', [!GetAtt ElastiCacheClusterMemcached.ConfigurationEndpoint.Address, !GetAtt ElastiCacheClusterMemcached.ConfigurationEndpoint.Port]] ] 169 | -------------------------------------------------------------------------------- /templates/03-rds.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle database 4 | 5 | Parameters: 6 | DatabaseType: 7 | AllowedValues: 8 | - MySQL 9 | - PostgreSQL 10 | Default: PostgreSQL 11 | Description: Database engine to use. 12 | Type: String 13 | DatabaseCmk: 14 | Description: AWS KMS Customer Master Key (CMK) to encrypt database cluster 15 | Type: String 16 | DatabaseInstanceType: 17 | AllowedValues: 18 | - db.t3.medium 19 | - db.t3.large 20 | - db.t4g.medium 21 | - db.t4g.large 22 | - db.r5.large 23 | - db.r5.xlarge 24 | - db.r5.2xlarge 25 | - db.r5.4xlarge 26 | - db.r5.8xlarge 27 | - db.r5.12xlarge 28 | - db.r5.16xlarge 29 | - db.r6g.large 30 | - db.r6g.xlarge 31 | - db.r6g.2xlarge 32 | - db.r6g.4xlarge 33 | - db.r6g.8xlarge 34 | - db.r6g.12xlarge 35 | - db.r6g.16xlarge 36 | - db.r6i.large 37 | - db.r6i.xlarge 38 | - db.r6i.2xlarge 39 | - db.r6i.4xlarge 40 | - db.r6i.8xlarge 41 | - db.r6i.12xlarge 42 | - db.r6i.16xlarge 43 | - db.r6i.24xlarge 44 | - db.r6i.32xlarge 45 | - db.r6gd.xlarge 46 | - db.r6gd.2xlarge 47 | - db.r6gd.4xlarge 48 | - db.r6gd.8xlarge 49 | - db.r6gd.12xlarge 50 | - db.r6gd.16xlarge 51 | ConstraintDescription: Must be a valid Aurora RDS instance type. 52 | Default: db.r6g.large 53 | Description: Amazon Aurora RDS database instance type 54 | Type: String 55 | RDSInstanceSecretArn: 56 | Type: String 57 | DatabaseName: 58 | AllowedPattern: ^([a-zA-Z0-9]*)$ 59 | Description: Amazon Aurora RDS master database name 60 | Type: String 61 | DatabaseSecurityGroup: 62 | Description: Database security group 63 | Type: AWS::EC2::SecurityGroup::Id 64 | NumberOfSubnets: 65 | AllowedValues: 66 | - 1 67 | - 2 68 | - 3 69 | Default: 2 70 | Description: Number of subnets. This must match your selections in the list of subnets below. 71 | Type: String 72 | Subnet: 73 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 74 | Type: List 75 | ProjectName: 76 | AllowedPattern: ^([a-zA-Z0-9]*)$ 77 | Default: App 78 | Description: Moodle Project Name 79 | Type: String 80 | 81 | Conditions: 82 | UseMySQL: !Equals [!Ref DatabaseType, MySQL] 83 | UsePostgreSQL: !Equals [!Ref DatabaseType, PostgreSQL] 84 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 85 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 86 | UseAWS-ManagedCMK: !Equals ['', !Ref DatabaseCmk] 87 | 88 | Resources: 89 | DatabaseCluster: 90 | Type: AWS::RDS::DBCluster 91 | Properties: 92 | BackupRetentionPeriod: 30 93 | DatabaseName: !Ref DatabaseName 94 | DBSubnetGroupName: !Ref DataSubnetGroup 95 | PubliclyAccessible: false 96 | Engine: !If [ UsePostgreSQL, aurora-postgresql, aurora-mysql ] 97 | KmsKeyId: !If [ UseAWS-ManagedCMK, !Ref 'AWS::NoValue', !Ref DatabaseCmk ] 98 | MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecretArn, ':SecretString:username}}' ]] 99 | MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref RDSInstanceSecretArn, ':SecretString:password}}' ]] 100 | Port: !If [ UsePostgreSQL, 5432, 3306 ] 101 | StorageEncrypted: true 102 | VpcSecurityGroupIds: 103 | - !Ref DatabaseSecurityGroup 104 | 105 | DatabaseInstance0: 106 | Type: AWS::RDS::DBInstance 107 | DeletionPolicy: Delete 108 | Properties: 109 | AllowMajorVersionUpgrade: false 110 | AutoMinorVersionUpgrade: true 111 | PubliclyAccessible: false 112 | DBClusterIdentifier: !Ref DatabaseCluster 113 | DBInstanceClass: !Ref DatabaseInstanceType 114 | DBSubnetGroupName: !Ref DataSubnetGroup 115 | Engine: !If [ UsePostgreSQL, aurora-postgresql, aurora-mysql ] 116 | 117 | DatabaseInstance1: 118 | Type: AWS::RDS::DBInstance 119 | DeletionPolicy: Delete 120 | Properties: 121 | AllowMajorVersionUpgrade: false 122 | AutoMinorVersionUpgrade: true 123 | PubliclyAccessible: false 124 | DBClusterIdentifier: !Ref DatabaseCluster 125 | DBInstanceClass: !Ref DatabaseInstanceType 126 | DBSubnetGroupName: !Ref DataSubnetGroup 127 | Engine: !If [ UsePostgreSQL, aurora-postgresql, aurora-mysql ] 128 | 129 | DataSubnetGroup: 130 | Type: AWS::RDS::DBSubnetGroup 131 | Properties: 132 | DBSubnetGroupDescription: RDS Database Subnet Group for Moodle 133 | SubnetIds: !If [ NumberOfSubnets1, [ !Select [ 0, !Ref Subnet ] ], 134 | !If [ NumberOfSubnets2, [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 135 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] ] ] 136 | 137 | DatabaseClusterParam: 138 | Type: AWS::SSM::Parameter 139 | Properties: 140 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/ClusterEndpoint' ] ] 141 | Type: String 142 | Value: !GetAtt DatabaseCluster.Endpoint.Address 143 | Description: Moodle DB Cluster 144 | 145 | DatabaseClusterReadOnlyParam: 146 | Type: AWS::SSM::Parameter 147 | Properties: 148 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/ClusterReadOnlyEndpoint' ] ] 149 | Type: String 150 | Value: !GetAtt DatabaseCluster.ReadEndpoint.Address 151 | Description: Moodle DB Cluster Read only 152 | 153 | DatabaseNameParam: 154 | Type: AWS::SSM::Parameter 155 | Properties: 156 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/Name' ] ] 157 | Type: String 158 | Value: !Ref DatabaseName 159 | Description: Moodle DB Name 160 | 161 | DatabaseTypeParam: 162 | Type: AWS::SSM::Parameter 163 | Properties: 164 | Name: !Join [ '', [ '/Moodle/',!Ref ProjectName, '/DB/Type' ] ] 165 | Type: String 166 | Value: !Ref DatabaseType 167 | Description: Moodle DB Type 168 | 169 | Outputs: 170 | Database: 171 | Value: !Ref DatabaseCluster 172 | DatabaseName: 173 | Value: !Ref DatabaseName 174 | DatabaseInstance0: 175 | Value: !Ref DatabaseInstance0 176 | DatabaseInstance1: 177 | Value: !Ref DatabaseInstance1 178 | DataSubnetGroup: 179 | Value: !Ref DataSubnetGroup 180 | DatabaseClusterEndpointAddress: 181 | Value: !GetAtt DatabaseCluster.Endpoint.Address 182 | DatabaseClusterEndpointPort: 183 | Value: !GetAtt DatabaseCluster.Endpoint.Port 184 | DatabaseClusterReadEndpointAddress: 185 | Value: !GetAtt DatabaseCluster.ReadEndpoint.Address 186 | -------------------------------------------------------------------------------- /templates/05-codepipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle deployment pipeline 4 | 5 | Parameters: 6 | GitS3RemoteRepoName: 7 | Type: "String" 8 | Default: "Moodle Repo" 9 | Description: Moodle git-remote-s3 repo name 10 | GitS3RemoteRepoBucketArn: 11 | Type: "String" 12 | Description: Moodle git-remote-s3 repo Arn 13 | BranchName: 14 | Type: "String" 15 | Default: "main" 16 | Description: Repository branch name 17 | AppAutoScalingGroupName: 18 | Type: "String" 19 | Description: Name of Autoscaling group for Moodle Web app. 20 | MoodleAppTargetGroupName: 21 | Type: "String" 22 | Description: Name of Target group attached to load balancer for web application servers. 23 | CodeArtifactS3BucketName: 24 | Type: "String" 25 | Description: Code Artifact S3 Bucket Name 26 | CodeArtifactS3BucketArn: 27 | Type: "String" 28 | Description: Code Artifact S3 Bucket ARN 29 | ProjectName: 30 | AllowedPattern: ^([a-zA-Z0-9]*)$ 31 | Default: App 32 | Description: The Moodle Project Name 33 | Type: String 34 | 35 | Resources: 36 | # This role is assumed by the CodePipeline service itself. 37 | MoodleCodeDeployServiceRole: 38 | Type: AWS::IAM::Role 39 | Properties: 40 | AssumeRolePolicyDocument: 41 | Version: 2012-10-17 42 | Statement: 43 | - Effect: Allow 44 | Principal: 45 | Service: 46 | - codedeploy.amazonaws.com 47 | Action: 48 | - sts:AssumeRole 49 | ManagedPolicyArns: 50 | - 'arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole' 51 | Path: / 52 | Policies: 53 | - PolicyName: MoodleCodeDeployCustomPolicy 54 | PolicyDocument: 55 | Version: 2012-10-17 56 | Statement: 57 | - Effect: Allow 58 | Action: 59 | - ec2:CreateTags 60 | - ec2:RunInstances 61 | Resource: 62 | - '*' 63 | 64 | MoodleDeployApp: 65 | Type: 'AWS::CodeDeploy::Application' 66 | Properties: 67 | ApplicationName: !Sub '${ProjectName}-DeployApp' 68 | 69 | MoodleDeploymentGroup: 70 | Type: AWS::CodeDeploy::DeploymentGroup 71 | DependsOn: MoodleDeployApp 72 | Properties: 73 | ApplicationName: !Ref MoodleDeployApp 74 | ServiceRoleArn: !GetAtt MoodleCodeDeployServiceRole.Arn 75 | DeploymentStyle: 76 | DeploymentOption: BLUE_GREEN #IN_PLACE 77 | DeploymentOption: WITH_TRAFFIC_CONTROL #WITHOUT_TRAFFIC_CONTROL 78 | AutoScalingGroups: 79 | - !Ref AppAutoScalingGroupName 80 | LoadBalancerInfo: 81 | TargetGroupInfoList: 82 | - Name: !Ref MoodleAppTargetGroupName 83 | 84 | MoodleWebAppASGNameParam: 85 | Type: AWS::SSM::Parameter 86 | Properties: 87 | Name: !Sub '/Moodle/${ProjectName}/WebAppASGName' 88 | Type: String 89 | Value: !Ref AppAutoScalingGroupName 90 | Description: SSM Parameter for Moodle WebApp Auto scaling group 91 | 92 | #This role is for Moodle pipeline to perform ci-cd tasks. 93 | MoodlePipelineRole: 94 | Type: AWS::IAM::Role 95 | Properties: 96 | AssumeRolePolicyDocument: 97 | Version: '2012-10-17' 98 | Statement: 99 | - Effect: Allow 100 | Principal: 101 | Service: 102 | - codepipeline.amazonaws.com 103 | Action: sts:AssumeRole 104 | ManagedPolicyArns: 105 | - 'arn:aws:iam::aws:policy/AWSCodePipeline_FullAccess' 106 | - 'arn:aws:iam::aws:policy/AWSCodeDeployDeployerAccess' 107 | Path: / 108 | Policies: 109 | - PolicyName: MoodlePipelineCustomPolicy 110 | PolicyDocument: 111 | Version: 2012-10-17 112 | Statement: 113 | - Effect: Allow 114 | Action: 115 | - codedeploy:CreateDeployment 116 | - codedeploy:CreateDeployment* 117 | - codedeploy:StopDeployment 118 | - codedeploy:Update* 119 | - codedeploy:RegisterApplicationRevision 120 | - codedeploy:Get* 121 | - codedeploy:List* 122 | - codedeploy:PutLifecycleEventHookExecutionStatus 123 | Resource: 124 | - arn:aws:codedeploy:*:*:application:* 125 | - arn:aws:codedeploy:*:*:deploymentgroup:*/* 126 | - arn:aws:codedeploy:*:*:instance:* 127 | - arn:aws:codedeploy:*:*:deploymentconfig:* 128 | - Effect: Allow 129 | Action: 130 | - s3:GetObject 131 | - s3:ListBucket 132 | - s3:GetBucketLocation 133 | - s3:PutObject 134 | Resource: 135 | - !Ref CodeArtifactS3BucketArn 136 | - !Join [ '', [ !Ref CodeArtifactS3BucketArn,'/*' ] ] 137 | - Effect: Allow 138 | Action: 139 | - s3:GetObject 140 | - s3:GetObjectVersion 141 | - s3:GetBucketVersioning 142 | - s3:ListBucket 143 | Resource: 144 | - !Ref GitS3RemoteRepoBucketArn 145 | - !Sub "${GitS3RemoteRepoBucketArn}/*" 146 | 147 | # The CI/CD pipeline stitching the full mechanism together 148 | MoodleAppPipeline: 149 | Type: AWS::CodePipeline::Pipeline 150 | Properties: 151 | Name: !Sub '${ProjectName}-Pipeline' 152 | RoleArn: !GetAtt MoodlePipelineRole.Arn 153 | Stages: 154 | - Actions: 155 | # Initiate Pipeline from git-remote-s3 156 | - ActionTypeId: 157 | Version: '1' 158 | Provider: S3 159 | Category: Source 160 | Owner: AWS 161 | OutputArtifacts: 162 | - Name: source 163 | InputArtifacts: [] 164 | Name: source 165 | Configuration: 166 | S3Bucket: !Ref GitS3RemoteRepoName 167 | S3ObjectKey: !Sub ${ProjectName}/refs/heads/main/repo.zip 168 | PollForSourceChanges: 'false' 169 | RunOrder: 1 170 | Namespace: SourceVars 171 | Name: Initiate 172 | - Actions: 173 | # Creating CodeDeploy for Deploying Moodle codebase from git-remote-s3 repo 174 | - ActionTypeId: 175 | Category: Deploy 176 | Owner: AWS 177 | Provider: CodeDeploy 178 | Version: "1" 179 | InputArtifacts: 180 | - Name: source 181 | Name: DeployOnASG 182 | Configuration: 183 | ApplicationName: !Ref MoodleDeployApp 184 | DeploymentGroupName: !Ref MoodleDeploymentGroup 185 | RunOrder: 1 186 | Name: Deploy 187 | ArtifactStore: 188 | Location: !Ref CodeArtifactS3BucketName 189 | Type: S3 190 | 191 | -------------------------------------------------------------------------------- /templates/03-publicalb.yaml: -------------------------------------------------------------------------------- 1 | 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle Application Load Balancer 4 | 5 | Parameters: 6 | NumberOfSubnets: 7 | AllowedValues: 8 | - 1 9 | - 2 10 | - 3 11 | Default: 2 12 | Description: Number of subnets. This must match your selections in the list of subnets below. 13 | Type: String 14 | PublicAlbAcmCertificate: 15 | AllowedPattern: ^$|(arn:aws:acm:)([a-z0-9/:-])*([a-z0-9])$ 16 | Description: '[ Optional ] The AWS Certification Manager 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.' 17 | Type: String 18 | PublicAlbSecurityGroup: 19 | Description: Select the ALB security group. 20 | Type: AWS::EC2::SecurityGroup::Id 21 | Subnet: 22 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 23 | Type: List 24 | Vpc: 25 | Description: Select an existing Vpc 26 | Type: AWS::EC2::VPC::Id 27 | ProjectName: 28 | AllowedPattern: ^([a-zA-Z0-9]*)$ 29 | Default: App 30 | Description: The Moodle Project Name 31 | Type: String 32 | 33 | Conditions: 34 | SslCertificate: !Not [!Equals [ '', !Ref PublicAlbAcmCertificate ] ] 35 | NoSslCertificate: !Equals [ '', !Ref PublicAlbAcmCertificate ] 36 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 37 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 38 | 39 | Mappings: 40 | # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-access-logging.html 41 | RegionMap: 42 | us-east-1: 43 | "ELBAccountID": "127311923021" 44 | us-east-2: 45 | "ELBAccountID": "033677994240" 46 | us-west-1: 47 | "ELBAccountID": "027434742980" 48 | us-west-2: 49 | "ELBAccountID": "797873946194" 50 | af-south-1: 51 | "ELBAccountID": "098369216593" 52 | ap-east-1: 53 | "ELBAccountID": "754344448648" 54 | ap-southeast-3: 55 | "ELBAccountID": "589379963580" 56 | ap-south-1: 57 | "ELBAccountID": "718504428378" 58 | ap-northeast-3: 59 | "ELBAccountID": "383597477331" 60 | ap-northeast-2: 61 | "ELBAccountID": "600734575887" 62 | ap-southeast-1: 63 | "ELBAccountID": "114774131450" 64 | ap-southeast-2: 65 | "ELBAccountID": "783225319266" 66 | ap-northeast-1: 67 | "ELBAccountID": "582318560864" 68 | ca-central-1: 69 | "ELBAccountID": "985666609251" 70 | eu-central-1: 71 | "ELBAccountID": "054676820928" 72 | eu-west-1: 73 | "ELBAccountID": "156460612806" 74 | eu-west-2: 75 | "ELBAccountID": "652711504416" 76 | eu-south-1: 77 | "ELBAccountID": "635631232127" 78 | eu-west-3: 79 | "ELBAccountID": "009996457667" 80 | eu-north-1: 81 | "ELBAccountID": "897822967062" 82 | me-south-1: 83 | "ELBAccountID": "076674570225" 84 | sa-east-1: 85 | "ELBAccountID": "507241528517" 86 | 87 | Resources: 88 | PublicAlbListenerNoSslCertificate: 89 | Type : AWS::ElasticLoadBalancingV2::Listener 90 | DependsOn: LoadBalancerAccessLogsBucketPolicy 91 | Properties: 92 | DefaultActions: 93 | - Type: forward 94 | TargetGroupArn: !Ref PublicAlbTargetGroup 95 | LoadBalancerArn: !Ref PublicApplicationLoadBalancer 96 | Port: 80 97 | Protocol: HTTP 98 | 99 | PublicAlbListenerSslCertificate: 100 | Condition: SslCertificate 101 | Type : AWS::ElasticLoadBalancingV2::Listener 102 | DependsOn: LoadBalancerAccessLogsBucketPolicy 103 | Properties: 104 | Certificates: 105 | - CertificateArn: !Ref PublicAlbAcmCertificate 106 | DefaultActions: 107 | - Type: forward 108 | TargetGroupArn: !Ref PublicAlbTargetGroup 109 | LoadBalancerArn: !Ref PublicApplicationLoadBalancer 110 | Port: 443 111 | Protocol: HTTPS 112 | SslPolicy: "ELBSecurityPolicy-TLS13-1-2-2021-06" 113 | 114 | LoadBalancerAccessLogsBucket: 115 | Type: AWS::S3::Bucket 116 | DeletionPolicy: RetainExceptOnCreate 117 | Properties: 118 | PublicAccessBlockConfiguration: 119 | BlockPublicAcls: true 120 | BlockPublicPolicy: true 121 | IgnorePublicAcls: true 122 | RestrictPublicBuckets: true 123 | BucketEncryption: 124 | ServerSideEncryptionConfiguration: 125 | - ServerSideEncryptionByDefault: 126 | SSEAlgorithm: AES256 127 | LifecycleConfiguration: 128 | Rules: 129 | - Id: DeleteOldLogs 130 | Status: Enabled 131 | ExpirationInDays: 7 132 | 133 | LoadBalancerAccessLogsBucketPolicy: 134 | Type: AWS::S3::BucketPolicy 135 | Properties: 136 | Bucket: !Ref LoadBalancerAccessLogsBucket 137 | PolicyDocument: 138 | Statement: 139 | - Action: 140 | - s3:PutObject 141 | Effect: Allow 142 | Resource: !Join [ "", [ !GetAtt LoadBalancerAccessLogsBucket.Arn, "/*" ] ] 143 | Principal: 144 | Service: logdelivery.elasticloadbalancing.amazonaws.com 145 | # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/enable-access-logging.html 146 | - Action: 147 | - s3:PutObject 148 | Effect: Allow 149 | Resource: !Join [ "", [ !GetAtt LoadBalancerAccessLogsBucket.Arn, "/*" ] ] 150 | Principal: 151 | AWS: !Join [ "", [ "arn:aws:iam::", !FindInMap [RegionMap, !Ref "AWS::Region", ELBAccountID], ":root" ] ] 152 | 153 | PublicApplicationLoadBalancer: 154 | Type: AWS::ElasticLoadBalancingV2::LoadBalancer 155 | Properties: 156 | Scheme: internet-facing 157 | Subnets: !If [ NumberOfSubnets1, [ !Select [ 0, !Ref Subnet ] ], 158 | !If [ NumberOfSubnets2, [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 159 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] ] ] 160 | LoadBalancerAttributes: 161 | - Key: idle_timeout.timeout_seconds 162 | Value: '60' 163 | - Key: access_logs.s3.enabled 164 | Value: true 165 | - Key: access_logs.s3.bucket 166 | Value: !Ref LoadBalancerAccessLogsBucket 167 | - Key: access_logs.s3.prefix 168 | Value: !Join [ '', [ !Ref ProjectName, '-publicalb' ] ] 169 | SecurityGroups: 170 | - !Ref PublicAlbSecurityGroup 171 | 172 | PublicAlbTargetGroup: 173 | Type: AWS::ElasticLoadBalancingV2::TargetGroup 174 | Properties: 175 | HealthCheckIntervalSeconds: 30 176 | HealthCheckPath: /status.txt 177 | HealthCheckTimeoutSeconds: 5 178 | Name: !Join [ '', [ 'PublicALB-', !Ref Vpc ] ] 179 | Port: 80 180 | Protocol: HTTP 181 | UnhealthyThresholdCount: 5 182 | VpcId: !Ref Vpc 183 | 184 | Outputs: 185 | PublicAlbTargetGroupArn: 186 | Value: !Ref PublicAlbTargetGroup 187 | PublicAlbTargetGroupName: 188 | Value: !GetAtt PublicAlbTargetGroup.TargetGroupName 189 | PublicAlbCanonicalHostedZoneId: 190 | Value: !GetAtt PublicApplicationLoadBalancer.CanonicalHostedZoneID 191 | PublicAlbDnsName: 192 | Value: !GetAtt PublicApplicationLoadBalancer.DNSName 193 | PublicAlbFullName: 194 | Value: !GetAtt PublicApplicationLoadBalancer.LoadBalancerFullName 195 | PublicAlbName: 196 | Value: !GetAtt PublicApplicationLoadBalancer.LoadBalancerName 197 | PublicAlbHostname: 198 | Value: !If [ NoSslCertificate, !Join [ '', [ 'http://', !GetAtt PublicApplicationLoadBalancer.DNSName ] ], !Join [ '', [ 'https://', !GetAtt PublicApplicationLoadBalancer.DNSName ] ] ] 199 | SslCertificate: 200 | Value: !If [ SslCertificate, True, False ] 201 | -------------------------------------------------------------------------------- /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 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /templates/01-newvpc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle network (VPC) 4 | 5 | Parameters: 6 | AvailabilityZones: 7 | Description: 'List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved.' 8 | Type: List 9 | NumberOfAZs: 10 | AllowedValues: 11 | - 1 12 | - 2 13 | - 3 14 | Default: 2 15 | Description: Number of Availability Zones to use in the VPC. This must match your selections in the list of Availability Zones parameter. 16 | Type: Number 17 | VpcCidr: 18 | 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]))$" 19 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 20 | Default: 10.0.0.0/16 21 | Description: CIDR block for the VPC 22 | Type: String 23 | DataSubnet0Cidr: 24 | 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]))$" 25 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 26 | Default: 10.0.100.0/24 27 | Description: CIDR block for data subnet 0 located in Availability Zone 0 28 | Type: String 29 | DataSubnet1Cidr: 30 | 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]))$" 31 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 32 | Default: 10.0.101.0/24 33 | Description: CIDR block for data subnet 1 located in Availability Zone 1 34 | Type: String 35 | DataSubnet2Cidr: 36 | 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]))$" 37 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 38 | Default: 10.0.102.0/24 39 | Description: CIDR block for data subnet 2 located in Availability Zone 2 40 | Type: String 41 | PublicSubnet0Cidr: 42 | 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]))$" 43 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 44 | Default: 10.0.200.0/24 45 | Description: CIDR block for Public subnet 0 located in Availability Zone 0 46 | Type: String 47 | PublicSubnet1Cidr: 48 | 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]))$" 49 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 50 | Default: 10.0.201.0/24 51 | Description: CIDR block for Public subnet 1 located in Availability Zone 1 52 | Type: String 53 | PublicSubnet2Cidr: 54 | 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]))$" 55 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 56 | Default: 10.0.202.0/24 57 | Description: CIDR block for Public subnet 2 located in Availability Zone 2 58 | Type: String 59 | AppSubnet0Cidr: 60 | 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]))$" 61 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 62 | Default: 10.0.0.0/22 63 | Description: CIDR block for App Subnet 0 located in Availability Zone 0 64 | Type: String 65 | AppSubnet1Cidr: 66 | 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]))$" 67 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 68 | Default: 10.0.4.0/22 69 | Description: CIDR block for App Subnet 1 located in Availability Zone 1 70 | Type: String 71 | AppSubnet2Cidr: 72 | 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]))$" 73 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 74 | Default: 10.0.8.0/22 75 | Description: CIDR block for App Subnet 2 located in Availability Zone 2 76 | Type: String 77 | 78 | Conditions: 79 | NumberOfAZs1: !Equals [ '1', !Ref NumberOfAZs ] 80 | NumberOfAZs2: !Equals [ '2', !Ref NumberOfAZs ] 81 | NumberOfAZs3: !Equals [ '3', !Ref NumberOfAZs ] 82 | AZ0: !Or 83 | - !Condition NumberOfAZs1 84 | - !Condition NumberOfAZs2 85 | - !Condition NumberOfAZs3 86 | AZ1: !Or 87 | - !Condition NumberOfAZs2 88 | - !Condition NumberOfAZs3 89 | AZ2: !Condition NumberOfAZs3 90 | 91 | Resources: 92 | AppSubnet0: 93 | Condition: AZ0 94 | Type: AWS::EC2::Subnet 95 | Properties: 96 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 97 | CidrBlock: !Ref AppSubnet0Cidr 98 | MapPublicIpOnLaunch: false 99 | Tags: 100 | - Key: Name 101 | Value: !Join [ '', [ 'AppSubnet0 / ', !Ref 'AWS::StackName' ] ] 102 | - Key: SubnetType 103 | Value: Private 104 | VpcId: !Ref Vpc 105 | 106 | AppSubnet1: 107 | Condition: AZ1 108 | Type: AWS::EC2::Subnet 109 | Properties: 110 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 111 | CidrBlock: !Ref AppSubnet1Cidr 112 | MapPublicIpOnLaunch: false 113 | Tags: 114 | - Key: Name 115 | Value: !Join [ '', [ 'AppSubnet1 / ', !Ref 'AWS::StackName' ] ] 116 | - Key: SubnetType 117 | Value: Private 118 | VpcId: !Ref Vpc 119 | 120 | AppSubnet2: 121 | Condition: AZ2 122 | Type: AWS::EC2::Subnet 123 | Properties: 124 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 125 | CidrBlock: !Ref AppSubnet2Cidr 126 | MapPublicIpOnLaunch: false 127 | Tags: 128 | - Key: Name 129 | Value: !Join [ '', [ 'AppSubnet2 / ', !Ref 'AWS::StackName' ] ] 130 | - Key: SubnetType 131 | Value: Private 132 | VpcId: !Ref Vpc 133 | 134 | AppSubnetRouteTableAssociation0: 135 | Condition: AZ0 136 | Type: AWS::EC2::SubnetRouteTableAssociation 137 | Properties: 138 | RouteTableId: !Ref NatRouteTable0 139 | SubnetId: !Ref AppSubnet0 140 | 141 | AppSubnetRouteTableAssociation1: 142 | Condition: AZ1 143 | Type: AWS::EC2::SubnetRouteTableAssociation 144 | Properties: 145 | RouteTableId: !Ref NatRouteTable1 146 | SubnetId: !Ref AppSubnet1 147 | 148 | AppSubnetRouteTableAssociation2: 149 | Condition: AZ2 150 | Type: AWS::EC2::SubnetRouteTableAssociation 151 | Properties: 152 | RouteTableId: !Ref NatRouteTable2 153 | SubnetId: !Ref AppSubnet2 154 | 155 | DataSubnet0: 156 | Condition: AZ0 157 | Type: AWS::EC2::Subnet 158 | Properties: 159 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 160 | CidrBlock: !Ref DataSubnet0Cidr 161 | MapPublicIpOnLaunch: false 162 | Tags: 163 | - Key: Name 164 | Value: !Join [ '', [ 'DataSubnet0 / ', !Ref 'AWS::StackName' ] ] 165 | - Key: SubnetType 166 | Value: Private 167 | VpcId: !Ref Vpc 168 | 169 | DataSubnet1: 170 | Condition: AZ1 171 | Type: AWS::EC2::Subnet 172 | Properties: 173 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 174 | CidrBlock: !Ref DataSubnet1Cidr 175 | MapPublicIpOnLaunch: false 176 | Tags: 177 | - Key: Name 178 | Value: !Join [ '', [ 'DataSubnet1 / ', !Ref 'AWS::StackName' ] ] 179 | - Key: SubnetType 180 | Value: Private 181 | VpcId: !Ref Vpc 182 | 183 | DataSubnet2: 184 | Condition: AZ2 185 | Type: AWS::EC2::Subnet 186 | Properties: 187 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 188 | CidrBlock: !Ref DataSubnet2Cidr 189 | MapPublicIpOnLaunch: false 190 | Tags: 191 | - Key: Name 192 | Value: !Join [ '', [ 'DataSubnet2 / ', !Ref 'AWS::StackName' ] ] 193 | - Key: SubnetType 194 | Value: Private 195 | VpcId: !Ref Vpc 196 | 197 | DataSubnetRouteTableAssociation0: 198 | Condition: AZ0 199 | Type: AWS::EC2::SubnetRouteTableAssociation 200 | Properties: 201 | RouteTableId: !Ref NatRouteTable0 202 | SubnetId: !Ref DataSubnet0 203 | 204 | DataSubnetRouteTableAssociation1: 205 | Condition: AZ1 206 | Type: AWS::EC2::SubnetRouteTableAssociation 207 | Properties: 208 | RouteTableId: !Ref NatRouteTable1 209 | SubnetId: !Ref DataSubnet1 210 | 211 | DataSubnetRouteTableAssociation2: 212 | Condition: AZ2 213 | Type: AWS::EC2::SubnetRouteTableAssociation 214 | Properties: 215 | RouteTableId: !Ref NatRouteTable2 216 | SubnetId: !Ref DataSubnet2 217 | 218 | InternetGateway: 219 | Type: AWS::EC2::InternetGateway 220 | Properties: 221 | Tags: 222 | - Key: Name 223 | Value: !Join [ '', [ 'InternetGateway / ', !Ref 'AWS::StackName' ] ] 224 | 225 | AttachInternetGateway: 226 | Type: AWS::EC2::VPCGatewayAttachment 227 | Properties: 228 | InternetGatewayId: !Ref InternetGateway 229 | VpcId: !Ref Vpc 230 | 231 | NatEIP0: 232 | Condition: AZ0 233 | Type: AWS::EC2::EIP 234 | Properties: 235 | Domain: vpc 236 | 237 | NatGateway0: 238 | Condition: AZ0 239 | Type: AWS::EC2::NatGateway 240 | DependsOn: AttachInternetGateway 241 | Properties: 242 | AllocationId: !GetAtt NatEIP0.AllocationId 243 | SubnetId: !Ref PublicSubnet0 244 | 245 | NatRoute0: 246 | Condition: AZ0 247 | Type: AWS::EC2::Route 248 | Properties: 249 | RouteTableId: !Ref NatRouteTable0 250 | DestinationCidrBlock: 0.0.0.0/0 251 | NatGatewayId: !Ref NatGateway0 252 | 253 | NatRouteTable0: 254 | Condition: AZ0 255 | Type: AWS::EC2::RouteTable 256 | Properties: 257 | Tags: 258 | - Key: Name 259 | Value: !Join [ '', ['NatRouteTable0 / ', !Ref 'AWS::StackName' ] ] 260 | - Key: Network 261 | Value: Public 262 | VpcId: !Ref Vpc 263 | 264 | NatEIP1: 265 | Condition: AZ1 266 | Type: AWS::EC2::EIP 267 | Properties: 268 | Domain: vpc 269 | 270 | NatGateway1: 271 | Condition: AZ1 272 | Type: AWS::EC2::NatGateway 273 | DependsOn: AttachInternetGateway 274 | Properties: 275 | AllocationId: !GetAtt NatEIP1.AllocationId 276 | SubnetId: !Ref PublicSubnet1 277 | 278 | NatRoute1: 279 | Condition: AZ1 280 | Type: AWS::EC2::Route 281 | Properties: 282 | RouteTableId: !Ref NatRouteTable1 283 | DestinationCidrBlock: 0.0.0.0/0 284 | NatGatewayId: !Ref NatGateway1 285 | 286 | NatRouteTable1: 287 | Condition: AZ1 288 | Type: AWS::EC2::RouteTable 289 | Properties: 290 | Tags: 291 | - Key: Name 292 | Value: !Join [ '', [ 'NatRouteTable1 / ', !Ref 'AWS::StackName' ] ] 293 | - Key: Network 294 | Value: Public 295 | VpcId: !Ref Vpc 296 | 297 | NatEIP2: 298 | Condition: AZ2 299 | Type: AWS::EC2::EIP 300 | Properties: 301 | Domain: vpc 302 | 303 | NatGateway2: 304 | Condition: AZ2 305 | Type: AWS::EC2::NatGateway 306 | DependsOn: AttachInternetGateway 307 | Properties: 308 | AllocationId: !GetAtt NatEIP2.AllocationId 309 | SubnetId: !Ref PublicSubnet2 310 | 311 | NatRoute2: 312 | Condition: AZ2 313 | Type: AWS::EC2::Route 314 | Properties: 315 | RouteTableId: !Ref NatRouteTable2 316 | DestinationCidrBlock: 0.0.0.0/0 317 | NatGatewayId: !Ref NatGateway2 318 | 319 | NatRouteTable2: 320 | Condition: AZ2 321 | Type: AWS::EC2::RouteTable 322 | Properties: 323 | Tags: 324 | - Key: Name 325 | Value: !Join [ '', [ 'NatRouteTable2 / ', !Ref 'AWS::StackName' ] ] 326 | - Key: Network 327 | Value: Public 328 | VpcId: !Ref Vpc 329 | 330 | PublicRoute: 331 | Type: AWS::EC2::Route 332 | DependsOn: AttachInternetGateway 333 | Properties: 334 | RouteTableId: !Ref PublicRouteTable 335 | DestinationCidrBlock: 0.0.0.0/0 336 | GatewayId: !Ref InternetGateway 337 | 338 | PublicRouteTable: 339 | Type: AWS::EC2::RouteTable 340 | Properties: 341 | Tags: 342 | - Key: Name 343 | Value: !Join [ '', [ 'PublicRouteTable / ', !Ref 'AWS::StackName' ] ] 344 | - Key: Network 345 | Value: Public 346 | VpcId: !Ref Vpc 347 | 348 | PublicRouteTableAssociation0: 349 | Condition: AZ0 350 | Type: AWS::EC2::SubnetRouteTableAssociation 351 | Properties: 352 | SubnetId: !Ref PublicSubnet0 353 | RouteTableId: !Ref PublicRouteTable 354 | 355 | PublicRouteTableAssociation1: 356 | Condition: AZ1 357 | Type: AWS::EC2::SubnetRouteTableAssociation 358 | Properties: 359 | SubnetId: !Ref PublicSubnet1 360 | RouteTableId: !Ref PublicRouteTable 361 | 362 | PublicRouteTableAssociation2: 363 | Condition: AZ2 364 | Type: AWS::EC2::SubnetRouteTableAssociation 365 | Properties: 366 | SubnetId: !Ref PublicSubnet2 367 | RouteTableId: !Ref PublicRouteTable 368 | 369 | PublicSubnet0: 370 | Condition: AZ0 371 | Type: AWS::EC2::Subnet 372 | Properties: 373 | AvailabilityZone: !Select [ 0, !Ref AvailabilityZones ] 374 | CidrBlock: !Ref PublicSubnet0Cidr 375 | MapPublicIpOnLaunch: false 376 | Tags: 377 | - Key: Name 378 | Value: !Join [ '', [ 'PublicSubnet0 / ', !Ref 'AWS::StackName' ] ] 379 | - Key: SubnetType 380 | Value: Public 381 | VpcId: !Ref Vpc 382 | 383 | PublicSubnet1: 384 | Condition: AZ1 385 | Type: AWS::EC2::Subnet 386 | Properties: 387 | AvailabilityZone: !Select [ 1, !Ref AvailabilityZones ] 388 | CidrBlock: !Ref PublicSubnet1Cidr 389 | MapPublicIpOnLaunch: false 390 | Tags: 391 | - Key: Name 392 | Value: !Join [ '', [ 'PublicSubnet1 / ', !Ref 'AWS::StackName' ] ] 393 | - Key: SubnetType 394 | Value: Public 395 | VpcId: !Ref Vpc 396 | 397 | PublicSubnet2: 398 | Condition: AZ2 399 | Type: AWS::EC2::Subnet 400 | Properties: 401 | AvailabilityZone: !Select [ 2, !Ref AvailabilityZones ] 402 | CidrBlock: !Ref PublicSubnet2Cidr 403 | MapPublicIpOnLaunch: false 404 | Tags: 405 | - Key: Name 406 | Value: !Join [ '', [ 'PublicSubnet2 / ', !Ref 'AWS::StackName' ] ] 407 | - Key: SubnetType 408 | Value: Public 409 | VpcId: !Ref Vpc 410 | 411 | Vpc: 412 | Type: AWS::EC2::VPC 413 | Properties: 414 | CidrBlock: !Ref VpcCidr 415 | EnableDnsHostnames: true 416 | EnableDnsSupport: true 417 | Tags: 418 | - Key: Name 419 | Value: !Join [ '', [ 'Vpc / ', !Ref 'AWS::StackName' ] ] 420 | 421 | VpcFlowLog: 422 | Type: AWS::EC2::FlowLog 423 | Properties: 424 | DeliverLogsPermissionArn: !GetAtt VpcFlowLogsRole.Arn 425 | LogGroupName: !Join [ '', [ !Ref 'AWS::StackName', '-FlowLog' ] ] 426 | ResourceId: !Ref Vpc 427 | ResourceType: VPC 428 | TrafficType: ALL 429 | 430 | VpcFlowLogsRole: 431 | Type: AWS::IAM::Role 432 | Properties: 433 | AssumeRolePolicyDocument: 434 | Version: 2012-10-17 435 | Statement: 436 | - Action: 437 | - sts:AssumeRole 438 | Effect: Allow 439 | Principal: 440 | Service: 441 | - vpc-flow-logs.amazonaws.com 442 | Path: '/' 443 | Policies: 444 | - PolicyName: root 445 | PolicyDocument: 446 | Version: 2012-10-17 447 | Statement: 448 | - Action: 449 | - logs:CreateLogStream 450 | - logs:DescribeLogGroups 451 | - logs:DescribeLogStreams 452 | - logs:PutLogEvents 453 | Effect: Allow 454 | Resource: !GetAtt VpcFlowLogsGroup.Arn 455 | 456 | VpcFlowLogsGroup: 457 | Type: AWS::Logs::LogGroup 458 | DeletionPolicy: Delete 459 | Properties: 460 | RetentionInDays: 7 461 | 462 | Outputs: 463 | Vpc: 464 | Value: !Ref Vpc 465 | VpcCidr: 466 | Value: !Ref VpcCidr 467 | PublicSubnet0: 468 | Condition: AZ0 469 | Value: !Ref PublicSubnet0 470 | PublicSubnet1: 471 | Condition: AZ1 472 | Value: !Ref PublicSubnet1 473 | PublicSubnet2: 474 | Condition: AZ2 475 | Value: !Ref PublicSubnet2 476 | AppSubnet0: 477 | Condition: AZ0 478 | Value: !Ref AppSubnet0 479 | AppSubnet1: 480 | Condition: AZ1 481 | Value: !Ref AppSubnet1 482 | AppSubnet2: 483 | Condition: AZ2 484 | Value: !Ref AppSubnet2 485 | DataSubnet0: 486 | Condition: AZ0 487 | Value: !Ref DataSubnet0 488 | DataSubnet1: 489 | Condition: AZ1 490 | Value: !Ref DataSubnet1 491 | DataSubnet2: 492 | Condition: AZ2 493 | Value: !Ref DataSubnet2 494 | DataSubnet: 495 | Value: 496 | !If 497 | [ NumberOfAZs1, 498 | !Ref DataSubnet0, 499 | !If 500 | [ NumberOfAZs2, 501 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1 ] ], 502 | !Join [ ',', [ !Ref DataSubnet0, !Ref DataSubnet1, !Ref DataSubnet2 ] ] 503 | ] 504 | ] 505 | AppSubnet: 506 | Value: 507 | !If 508 | [ NumberOfAZs1, 509 | !Ref AppSubnet0, 510 | !If 511 | [ NumberOfAZs2, 512 | !Join [ ',', [ !Ref AppSubnet0, !Ref AppSubnet1 ] ], 513 | !Join [ ',', [ !Ref AppSubnet0, !Ref AppSubnet1, !Ref AppSubnet2 ] ] 514 | ] 515 | ] 516 | PublicSubnet: 517 | Value: 518 | !If 519 | [ NumberOfAZs1, 520 | !Ref PublicSubnet0, 521 | !If 522 | [ NumberOfAZs2, 523 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1 ] ], 524 | !Join [ ',', [ !Ref PublicSubnet0, !Ref PublicSubnet1, !Ref PublicSubnet2 ] ] 525 | ] 526 | ] 527 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hosting Moodle™ on AWS 2 | 3 | ### Version 2.0.2 4 | 5 | ## Overview 6 | 7 | This repository provides set of CloudFormation nested templates that deploy a highly available, elastic, and scalable [Moodle™ 4.4](https://docs.moodle.org) environment on AWS. Moodle™ offers a learning platform that provides educators, administrators and learners a single robust, secure and integrated system for personalized learning environment. 8 | 9 | These nested templates can be used to deploy Moodle™ on AWS using [Amazon Virtual Private Cloud (Amazon VPC)](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Introduction.html), [Amazon Elastic Compute Cloud (Amazon EC2)](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html), [Auto Scaling](http://docs.aws.amazon.com/autoscaling/latest/userguide/WhatIsAutoScaling.html), [Elastic Load Balancing (Application Load Balancer)](http://docs.aws.amazon.com/elasticbalancing/latest/application/introduction.html), [Amazon Aurora](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/CHAP_AuroraOverview.html), [Amazon ElastiCache](http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/WhatIs.html), [Amazon Elastic File System (Amazon EFS)](http://docs.aws.amazon.com/efs/latest/ug/whatisefs.html), [Amazon CloudFront](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html), [Amazon Route 53](http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html), [Amazon Certificate Manager (Amazon ACM)](http://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html) with [AWS CloudFormation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html) in YAML format. 10 | 11 | This architecture is expansive enough to meet the needs of large institutions / organizations. Smaller organizations can choose to run a subset of the template to meet their needs. These templates can also be run individually and may be modified. 12 | 13 | This template currently uses [Moodle™ 4.4](https://download.moodle.org/download.php/stable404/moodle-4.4.tgz) stable version downloaded directly from [download.moodle.org](https://download.moodle.org/releases/latest/). Details for downloading are available in the [templates/03-pipelinehelper.yaml](templates/03-pipelinehelper.yaml) template file. 14 | 15 | ## Deployment guide 16 | 17 | Read the [reference architecture](#architecture) and the steps below to understand the deployment scope and options. While following the steps and guidelines to deploy Moodle™, pay careful attention to the parameters and their descriptions. 18 | 19 | ### Pre-requisites 20 | 1) Select an [AWS Region](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions) (for example: us-east-1) for your deployment. 21 | 2) Give a meaningful `Stack Name` that does `not have` any `special characters` including hyphen(-) Eg: `MoodleDevDeploy` or `MoodleProd` 22 | 2) If you plan to use HTTPS, you must [create](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) or [import](https://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html) your certificate into Amazon Certificate Manager (ACM) and provide its [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) when deploying the CloudFormation stack. 23 | 3) Alternatively, if you plan to use an SSL Certificate with [Amazon CloudFront](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html), you must create or import your certificate into Amazon Certificate Manager in the us-east-1 region before launching Moodle™ and provide it's ARN when deploying the CloudFormation stack. 24 | 25 | ### Steps 26 | 1) Deploy the 00-main.yaml stack. You can also click the `Launch Stack` button below to launch the stack in your logged-in AWS Account. 27 | 2) After the stack deployment completes, you will see a `DNS Name` entry under the `Outputs` tab under the main CloudFormation template. This DNS Name value will be your Moodle™ app URL. You can configure aliases or CNAMEs to point to this DNS Name if you want to customize this. 28 | 3) Navigate to the Moodle™ application URL to complete the installation. 29 | 1) *NOTE: You may encounter a 504 Gateway Timeout or CloudFront error on the final step of the Moodle™ application installation wizard (after configuring the administrator password). You can safely ignore this error and refresh the page to complete the installation.* 30 | 2) You may also see "Installation must be finished from the original IP address, sorry." If this is the case, update your database and set the `lastip` field of the `mdl_user` table to the internal IP address of your Application Load Balancer which can be found under the `Network Interfaces` section of the `EC2` section of the AWS Console. To update the value in the database, run these commands on the EC2 web server: 31 | 32 | psql -h -U 33 | update mdl_user set lastip=''; 34 | 35 | 4) Once the Moodle™ installation wizard completes successfully, you need to update the value of the SSM Parameter `IsMoodleSetupCompleted` to 'Yes'. 36 | 1) In your main `CloudFormation` template, check `Outputs` tab to see parameter `IsMoodleSetupCompleted`. Click the link under `Value` to get details of the parameter. 37 | 3) Edit the parameter and change the value to `Yes`. 38 | 4) Go back to `Outputs` tab to see link for `MoodleCodePipeline`. Click on the link to open Code Pipeline. Click on the `Release Change` button. This will re-run the deployment pipeline and update the Moodle™ configurations post-installation, in order to adjust the auto-scaling configuration and the session cache configuration. 39 | 40 | 5) This template can optionally deploy [Amazon ElastiCache](https://aws.amazon.com/elasticache/) as the Moodle™ Session and/or Application cache(s). When this feature is activated, you still need to configure the Application Cache within Moodle™ after deployment (see [how-to](#application-caching) guide). The cache endpoint is listed under the CloudFormation `Output` tab as `ApplicationCacheServerEndpoint`. 41 | 42 | *`NOTE:` To connect to your EC2 web servers, select an EC2 Instance and click on the `Connect` button in the AWS Console. Open the `Session Manager` tab and click on the `Connect` button. Note that this feature uses the [AWS SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-getting-started-instance-profile.html) that is installed on the instances, allowing you to connect to EC2 Instances without opening the SSH port to Internet traffic. An alternative approach to connect to your instances would be to enable the bastion host through the CloudFormation stack parameters. 43 | 44 | ### Launch the CloudFormation Template 45 | 46 | You can launch this CloudFormation template in different AWS Regions. Below are links to help you get started quickly, but note that you can always change the region yourself once you are in the AWS Console. 47 | 48 | | AWS Region Code | Name | Launch | 49 | | --- | --- | --- 50 | | us-east-1 |US East (N. Virginia)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 51 | | us-east-2 |US East (Ohio)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-2#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 52 | | us-west-2 |US West (Oregon)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 53 | | eu-west-1 |EU (Ireland)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=eu-west-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 54 | | eu-central-1 |EU (Frankfurt)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=eu-central-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 55 | | ap-southeast-1 |AP (Singapore)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-southeast-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 56 | | ap-southeast-2 |AP (Sydney)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-southeast-2#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 57 | | ap-south-1 |India (Mumbai)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-south-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 58 | | ca-central-1 |Canada (Central)| [![cloudformation-launch-stack](images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ca-central-1#/stacks/new?stackName=Moodle&templateURL=https://s3.amazonaws.com/aws-refarch/moodle/al2023/templates/00-main.yaml) | 59 | 60 | ## Architecture 61 | 62 | The following sections describe the architecture and its components. This architecture uses a similar approach to the one used in the [WordPress Reference Architecture](https://github.com/awslabs/aws-refarch-wordpress). 63 | 64 | ![](images/aws-refarch-moodle-architecture.png) 65 | 66 | ### AWS Certificate Manager 67 | 68 | AWS Certificate Manager lets you easily provision, manage, and deploy Secure Sockets Layer/Transport Layer Security (SSL/TLS) certificates for use with AWS services. You should use SSL/TLS to protect data in transit, including sessions and passwords. If you plan to use SSL/TLS, you must create or import a certificate using AWS Certificate Manager before you deploy the template. In addition, if wish to use CloudFront and host Moodle™ in a region other than us-east-1, you must create or import the certificate in both us-east-1 and the region you are hosting Moodle™ in. CloudFront requires certificates in the us-east-1 region. 69 | 70 | ### Application Load Balancer 71 | 72 | The Application Load Balancer distributes incoming application traffic across multiple EC2 instances in multiple Availability Zones. You achieve high availability by clustering multiple Moodle™ servers behind this load balancer. You can review Moodle™'s overview of [Server Clustering](https://docs.moodle.org/en/Server_cluster) before proceeding. 73 | 74 | ### Amazon Autoscaling 75 | 76 | Amazon EC2 Auto Scaling helps ensure that the appropriate number of Amazon EC2 instances are available to handle the load of the application. The template configures autoscaling based on CPU utilization. An additional instance is added when the average CPU utilization exceeds 75% for three minutes and removed when the average CPU utilization is less than 25% for three minutes. Based on the instance type, cache configuration, and other factors, you may find that other metrics are better predictors of load. You can change the metrics to better meet your operational needs. 77 | 78 | *`Note:` that the installation wizard causes spikes in CPU that could cause the cluster to scale unexpectedly. To avoid an issue with this during installation, initial deployment starts with minimum and maximum autoscaling values of 1. Once you complete the Moodle™ installation wizard, update the SSM parameter `IsMoodleSetupCompleted` and run the Moodle™ pipeline, the minimum and maximum autoscaling values will be updated according to your parameters.* 79 | 80 | ### Amazon Elastic File System (EFS) 81 | 82 | Amazon Elastic File System (Amazon EFS) provides simple, scalable file storage in the AWS Cloud. Using EFS makes Moodle™ operations and management (shared files, updates, patches, etc.) easier. However, Moodle™ performance may suffer when the application code itself is run from mounted volumes like EFS. Moodle™ recommends `dirroot` to be on local or high-performance storage. This template follows that recommendation, and uses a combination of Elastic Block Storage (EBS) and EFS for storage. Each web server in the Moodle™ Cluster employs the following directory structure: 83 | ``` 84 | $CFG->dirroot = '/var/www/moodle/html' #Stored on root EBS volume 85 | $CFG->localcachedir = '/var/www/moodle/local' #Stored on root EBS volume 86 | 87 | $CFG->dataroot = '/var/www/moodle/data' #Stored on shared EFS filesystem 88 | $CFG->cachedir = '/var/www/moodle/cache' #Stored on shared EFS filesystem 89 | $CFG->tempdir = '/var/www/moodle/temp' #Stored on shared EFS filesystem 90 | ``` 91 | 92 | With [elastic](https://docs.aws.amazon.com/efs/latest/ug/performance.html#elastic) throughput type, Amazon EFS automatically scales throughput performance up or down to meet the needs of your workload activity. You don't need to specify or provision the throughput capacity to meet your application needs. 93 | 94 | *Moodle™ recommends the `dirroot` be set as read only for the apache process in a clustered environment [[Reference]](https://docs.moodle.org/400/en/Server_cluster#.24CFG-.3Edirroot). You should not install plugins to a server cluster from the admin page. `Moodle™ recommends manually installing plugins on each server during planned maintenance`. To follow the infrastructure-as-code methodology, installation/upgrade of plugins can be managed using AWS CodePipeline scripts. See the `.pipeline` folder inside your git-remote-s3 Moodle™ repository. 95 | 96 | ### AWS CodePipeline 97 | This CloudFormation templates use AWS Services to create a CI/CD pipeline to help manage your Moodle™ environment. AWS S3 will host a git repository for your Moodle™ environment using [git-remote-s3](https://github.com/awslabs/git-remote-s3). It initially pulls the source from [download.moodle.org.](https://download.moodle.org/download.php/stable404/moodle-4.4.tgz). It also adds files required to automate the deployment pipeline. You can explore these files under the `.pipeline` folder. 98 | This template also creates an AWS CodePipeline configuration that build artifacts to deploy on EC2 with autoscaling groups using AWS CodeBuild and AWS CodeDeploy. It can optionally support a BLUE_GREEN deployment. 99 | 100 | *You can customize the overall pipeline for your Moodle™ setup.* 101 | 102 | ### AWS Systems Manager - Parameter Store 103 | This template also uses the Parameter Store to host Moodle™ environment configurations parameters like the database endpoint, the database credentials, the application and session cache endpoints, etc. This allows easy management of these configuration parameters. You can change these parameters and refresh your deployment to quickly implement them. 104 | 105 | ### `Caching` 106 | --- 107 | Caching can have a dramatic impact on Moodle™'s performance. This template configures various forms of caching including OPcache, CloudFront and ElastiCache. 108 | 109 | #### OPcache 110 | 111 | [PHP OPcache](https://www.php.net/manual/book.opcache.php) speeds up PHP execution by caching precompiled scripts in memory. This template configures OPcache as described [here](https://docs.moodle.org/400/en/OPcache). 112 | 113 | #### Amazon ElastiCache 114 | 115 | Amazon ElastiCache for `Memcached` is a Memcached-compatible in-memory key-value store service that can be used as a cache or a data store. Moodle™ recommends that you `don't use the same memcached server for both sessions and MUC` [Refer](https://docs.moodle.org/26/en/Session_handling). Events triggering MUC caches to be purged leads to MUC purging the memcached server]. This template configures two ElastiCache clusters, one for session caching and one for application caching. 116 | 117 | This template also allows you to create `Amazon ElastiCache for Redis` as Redis compatible in-memory key-value store service that can be used as a cache or a data store. 118 | 119 | #### Session Caching 120 | 121 | Moodle™ recommends that you [store user sessions in one shared memcached server](https://docs.moodle.org/en/Server_cluster#Performance_recommendations). The template configures session caching as described [here](https://docs.moodle.org/en/Session_handling#Memcached_session_driver). 122 | 123 | *`Note:` This template doesn't configure the Session Cache during initial deployment. It waits for you to finish the initial Moodle™ installation wizard and update the Parameter `IsMoodleSetupCompleted` value to `Yes` in the SSM Parameter Store. Once the installation is completed, you need to run the Moodle™ pipeline to enable session caching and finalize the other remaining configuration.* 124 | 125 | #### Application Caching 126 | 127 | The template deploys an ElastiCache cluster for application caching, `but the application caching must be configured after the Moodle installation is completed`. You can configure [memcached](https://docs.moodle.org/en/Caching#Memcached) or [Redis](https://docs.moodle.org/en/Redis_cache_store) by filling in the auto-discovery endpoint to the list of Servers under both Store Configuration and Enable Clustered Servers (see image below). You can find the `ApplicationCacheServerEndpoint` address in the `Outputs` of the CloudFormation stack. Finally, scroll to the bottom of the caching administration page in Moodle™ and set ElastiCache as the default store for application caching. 128 | 129 | ![](images/aws-refarch-moodle-caching.png) 130 | ![](images/aws-refarch-moodle-caching-redis.png) 131 | 132 | #### Amazon CloudFront 133 | 134 | Amazon CloudFront is a global content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to your viewers with low latency and high transfer speeds. It also helps in caching content closer to user's geography and reduces loads on the web servers. 135 | 136 | ### Amazon Route 53 137 | 138 | Amazon Route 53 is a highly available and scalable cloud Domain Name System (DNS) service. The template will optionally configure a Route53 alias that points to either the Application Load Balancer or CloudFront. If you are using another DNS system, you should create a CNAME record in your DNS system to reference either the Application Load Balancer or CloudFront (if deployed). If you don't have access to DNS you can leave Domain Name blank and the template will configure Moodle™ to use the auto-generated Application Load Balancer domain name. 139 | 140 | --- 141 | 142 | ## License 143 | 144 | This library is licensed under the Apache 2.0 License. 145 | 146 | Portions copyright. 147 | 148 | - Moodle™ is licensed under the General Public License (GPLv3 or later) from the Free Software Foundation. 149 | - OPcache is licensed under PHP License, version 3.01. 150 | 151 | Please see [LICENSE](LICENSE) for applicable license terms and [NOTICE](NOTICE) for applicable notices. 152 | 153 | The word Moodle and associated Moodle logos are trademarks or registered trademarks of Moodle Pty Ltd or its related affiliates. 154 | -------------------------------------------------------------------------------- /templates/04-web.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle web instances 4 | 5 | Parameters: 6 | RDSInstanceSecretArn: 7 | Type: String 8 | NumberOfSubnets: 9 | AllowedValues: 10 | - 1 11 | - 2 12 | - 3 13 | Default: 2 14 | Description: Number of subnets. This must match your selections in the list of subnets below. 15 | Type: String 16 | PublicAlbTargetGroupArn: 17 | Description: The public application load balancer target group arn. 18 | Type: String 19 | Subnet: 20 | Description: Select existing subnets. The number selected must match the number of subnets above. Subnets selected must be in separate AZs. 21 | Type: List 22 | WebAsgMax: 23 | AllowedPattern: ^((?!0$)[1-2]?[0-9]|30)$ 24 | ConstraintDescription: Must be a number between 1 and 30. 25 | Default: 1 26 | Description: Specifies the maximum number of EC2 instances in the Web Autoscaling Group. 27 | Type: String 28 | WebAsgMin: 29 | AllowedPattern: ^([0-0]?[0-9]|10)$ 30 | ConstraintDescription: Must be a number between 0 and 10. 31 | Default: 2 32 | Description: Specifies the minimum number of EC2 instances in the Web Autoscaling Group. 33 | Type: String 34 | WebInstanceType: 35 | AllowedValues: 36 | - t3.nano 37 | - t3.micro 38 | - t3.small 39 | - t3.medium 40 | - t3.large 41 | - t3.xlarge 42 | - t3.2xlarge 43 | - m5.large 44 | - m5.xlarge 45 | - m5.2xlarge 46 | - m5.4xlarge 47 | - m5.8xlarge 48 | - m5.12xlarge 49 | - m5.16xlarge 50 | - m5.24xlarge 51 | - c5.large 52 | - c5.xlarge 53 | - c5.2xlarge 54 | - c5.4xlarge 55 | - c5.9xlarge 56 | - c5.12xlarge 57 | - c5.18xlarge 58 | - c5.24xlarge 59 | - r5.large 60 | - r5.xlarge 61 | - r5.2xlarge 62 | - r5.4xlarge 63 | - r5.8xlarge 64 | - r5.12xlarge 65 | - r5.16xlarge 66 | - r5.24xlarge 67 | - t3a.nano 68 | - t3a.micro 69 | - t3a.small 70 | - t3a.medium 71 | - t3a.large 72 | - t3a.xlarge 73 | - t3a.2xlarge 74 | - m5a.large 75 | - m5a.xlarge 76 | - m5a.2xlarge 77 | - m5a.4xlarge 78 | - m5a.8xlarge 79 | - m5a.12xlarge 80 | - m5a.16xlarge 81 | - m5a.24xlarge 82 | - c5a.large 83 | - c5a.xlarge 84 | - c5a.2xlarge 85 | - c5a.4xlarge 86 | - c5a.9xlarge 87 | - c5a.12xlarge 88 | - c5a.18xlarge 89 | - c5a.24xlarge 90 | - r5a.large 91 | - r5a.xlarge 92 | - r5a.2xlarge 93 | - r5a.4xlarge 94 | - r5a.8xlarge 95 | - r5a.12xlarge 96 | - r5a.16xlarge 97 | - r5a.24xlarge 98 | - t4g.nano 99 | - t4g.micro 100 | - t4g.small 101 | - t4g.medium 102 | - t4g.large 103 | - t4g.xlarge 104 | - t4g.2xlarge 105 | - m6g.large 106 | - m6g.xlarge 107 | - m6g.2xlarge 108 | - m6g.4xlarge 109 | - m6g.8xlarge 110 | - m6g.12xlarge 111 | - m6g.16xlarge 112 | - m6g.24xlarge 113 | - m7g.medium 114 | - m7g.large 115 | - m7g.xlarge 116 | - m7g.2xlarge 117 | - m7g.4xlarge 118 | - m7g.8xlarge 119 | - m7g.12xlarge 120 | - m7g.16xlarge 121 | - c6g.large 122 | - c6g.xlarge 123 | - c6g.2xlarge 124 | - c6g.4xlarge 125 | - c6g.9xlarge 126 | - c6g.12xlarge 127 | - c6g.18xlarge 128 | - c6g.24xlarge 129 | - r6g.large 130 | - r6g.xlarge 131 | - r6g.2xlarge 132 | - r6g.4xlarge 133 | - r6g.8xlarge 134 | - r6g.12xlarge 135 | - r6g.16xlarge 136 | - r6g.24xlarge 137 | - c7g.medium 138 | - c7g.large 139 | - c7g.xlarge 140 | - c7g.2xlarge 141 | - c7g.4xlarge 142 | - c7g.8xlarge 143 | - c7g.12xlarge 144 | - c7g.16xlarge 145 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 146 | Default: c7g.xlarge 147 | Description: The Amazon EC2 instance type for your web instances. 148 | Type: String 149 | WebSecurityGroup: 150 | Description: Select the web security group. 151 | Type: AWS::EC2::SecurityGroup::Id 152 | LatestAmiId : 153 | Type : AWS::SSM::Parameter::Value 154 | Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 155 | LatestArmAmiId : 156 | Type : AWS::SSM::Parameter::Value 157 | Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 158 | CodeArtifactS3BucketArn: 159 | Type: "String" 160 | Description: Code Artifact S3 Bucket Arn 161 | ProjectName: 162 | AllowedPattern: ^([a-zA-Z0-9]*)$ 163 | Default: App 164 | Description: Main stack name 165 | Type: String 166 | 167 | Conditions: 168 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 169 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 170 | NumberOfSubnets3: !Equals [ 3, !Ref NumberOfSubnets ] 171 | UsingGraviton2Ami: !Or 172 | - !Equals ["t4",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 173 | - !Equals ["c6",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 174 | - !Equals ["c7",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 175 | - !Equals ["m6",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 176 | - !Equals ["m7",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 177 | - !Equals ["r6",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 178 | - !Equals ["r7",!Select [0, !Split [ "g.", !Ref WebInstanceType]]] 179 | 180 | Resources: 181 | WebInstanceProfile: 182 | Type: AWS::IAM::InstanceProfile 183 | Properties: 184 | Path: / 185 | Roles: 186 | - !Ref WebInstanceRole 187 | 188 | WebInstanceRole: 189 | Type: AWS::IAM::Role 190 | Properties: 191 | AssumeRolePolicyDocument: 192 | Version: 2012-10-17 193 | Statement: 194 | - Effect: Allow 195 | Principal: 196 | Service: 197 | - ec2.amazonaws.com 198 | Action: 199 | - sts:AssumeRole 200 | ManagedPolicyArns: 201 | - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore' 202 | - 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforAWSCodeDeployLimited' 203 | Path: / 204 | Policies: 205 | - PolicyName: MoodleCustomWebInstancePolicy 206 | PolicyDocument: 207 | Version: 2012-10-17 208 | Statement: 209 | - Effect: Allow 210 | Action: 211 | - logs:CreateLogStream 212 | - logs:PutLogEvents 213 | - logs:DescribeLogStreams 214 | Resource: 215 | - !GetAtt Logs.Arn 216 | - Effect: Allow 217 | Action: 218 | - autoscaling:CompleteLifecycleAction 219 | - autoscaling:DescribeAutoScalingInstances 220 | - autoscaling:DescribeLifecycleHooks 221 | Resource: 222 | - "*" 223 | - Effect: Allow 224 | Action: 225 | - secretsmanager:GetSecretValue 226 | Resource: 227 | - !Ref RDSInstanceSecretArn 228 | - Effect: Allow 229 | Action: 230 | - s3:GetObject 231 | - s3:ListBucket 232 | - s3:GetBucketLocation 233 | Resource: 234 | - !Ref CodeArtifactS3BucketArn 235 | - !Join [ '', [ !Ref CodeArtifactS3BucketArn,'/*' ] ] 236 | - Effect: Allow 237 | Action: 238 | - cloudformation:DescribeStackResources 239 | - cloudformation:DescribeStackResource 240 | Resource: 241 | - '*' 242 | 243 | Logs: 244 | Type: AWS::Logs::LogGroup 245 | DeletionPolicy: Delete 246 | Properties: 247 | RetentionInDays: 7 248 | 249 | WebAutoScalingGroup: 250 | Type: AWS::AutoScaling::AutoScalingGroup 251 | Properties: 252 | Cooldown: '60' 253 | HealthCheckGracePeriod: 120 254 | HealthCheckType: ELB 255 | LaunchTemplate: 256 | LaunchTemplateId: !Ref WebLaunchTemplate 257 | Version: !GetAtt WebLaunchTemplate.LatestVersionNumber 258 | MaxSize: !Ref WebAsgMax 259 | MinSize: !Ref WebAsgMin 260 | Tags: 261 | - Key: Name 262 | Value: !Join [ '', [ 'Web ASG / ', !Ref 'AWS::StackName' ] ] 263 | PropagateAtLaunch: true 264 | TargetGroupARNs: 265 | - !Ref PublicAlbTargetGroupArn 266 | VPCZoneIdentifier: 267 | !If 268 | [ NumberOfSubnets1, 269 | [ !Select [ 0, !Ref Subnet ] ], 270 | !If 271 | [ NumberOfSubnets2, 272 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ] ], 273 | [ !Select [ 0, !Ref Subnet ], !Select [ 1, !Ref Subnet ], !Select [ 2, !Ref Subnet ] ] 274 | ] 275 | ] 276 | CreationPolicy: 277 | ResourceSignal: 278 | Count: !Ref WebAsgMin 279 | Timeout: PT15M 280 | 281 | ScaleUpPolicy: 282 | Type: AWS::AutoScaling::ScalingPolicy 283 | Properties: 284 | AdjustmentType: ChangeInCapacity 285 | AutoScalingGroupName: 286 | Ref: WebAutoScalingGroup 287 | Cooldown: '300' 288 | ScalingAdjustment: 1 289 | 290 | CPUAlarmHigh: 291 | Type: AWS::CloudWatch::Alarm 292 | Properties: 293 | EvaluationPeriods: 3 294 | Statistic: Average 295 | Threshold: 75 296 | AlarmDescription: Alarm if CPU too high 297 | Period: 60 298 | AlarmActions: 299 | - Ref: ScaleUpPolicy 300 | Namespace: AWS/EC2 301 | Dimensions: 302 | - Name: AutoScalingGroupName 303 | Value: !Ref WebAutoScalingGroup 304 | ComparisonOperator: GreaterThanThreshold 305 | MetricName: CPUUtilization 306 | 307 | ScaleDownPolicy: 308 | Type: AWS::AutoScaling::ScalingPolicy 309 | Properties: 310 | AdjustmentType: ChangeInCapacity 311 | AutoScalingGroupName: 312 | Ref: WebAutoScalingGroup 313 | Cooldown: '300' 314 | ScalingAdjustment: -1 315 | 316 | CPUAlarmLow: 317 | Type: AWS::CloudWatch::Alarm 318 | Properties: 319 | EvaluationPeriods: 3 320 | Statistic: Average 321 | Threshold: 25 322 | AlarmDescription: Alarm if CPU too low 323 | Period: 60 324 | AlarmActions: 325 | - Ref: ScaleDownPolicy 326 | Namespace: AWS/EC2 327 | Dimensions: 328 | - Name: AutoScalingGroupName 329 | Value: !Ref WebAutoScalingGroup 330 | ComparisonOperator: LessThanThreshold 331 | MetricName: CPUUtilization 332 | 333 | WebLaunchTemplate: 334 | Type: AWS::EC2::LaunchTemplate 335 | Metadata: 336 | AWS::CloudFormation::Init: 337 | configSets: 338 | deploy_webserver: 339 | - install_packages 340 | - install_cloudwatchlogs 341 | - install_codedeploy 342 | - install_webserver 343 | - start_services 344 | - add_crontab 345 | install_packages: 346 | packages: 347 | yum: 348 | amazon-cloudwatch-agent: [] 349 | ruby3.2: [] 350 | httpd: [] 351 | php8.1: [] 352 | php8.1-gd: [] 353 | php8.1-soap: [] 354 | php8.1-intl: [] 355 | php8.1-mbstring: [] 356 | php8.1-xml: [] 357 | php8.1-opcache: [] 358 | php8.1-fpm: [] 359 | php8.1-pgsql: [] 360 | php8.1-mysqlnd: [] 361 | cronie: [] 362 | php8.1-devel: [] 363 | php-pear: [] 364 | libzip: [] 365 | libzip-devel: [] 366 | libsodium-devel: [] 367 | libzstd-devel: [] 368 | redis6-devel: [] 369 | lz4-devel: [] 370 | libmemcached-awesome-tools: [] 371 | libmemcached-awesome-devel: [] 372 | zlib-devel: [] 373 | cyrus-sasl-devel: [] 374 | libevent-devel: [] 375 | install_cloudwatchlogs: 376 | files: 377 | /etc/awslogs/awslogs.conf: 378 | content: !Sub | 379 | [general] 380 | state_file= /var/awslogs/state/agent-state 381 | 382 | [/var/log/cloud-init.log] 383 | file = /var/log/cloud-init.log 384 | log_group_name = ${Logs} 385 | log_stream_name = {instance_id}/cloud-init.log 386 | datetime_format = 387 | 388 | [/var/log/cloud-init-output.log] 389 | file = /var/log/cloud-init-output.log 390 | log_group_name = ${Logs} 391 | log_stream_name = {instance_id}/cloud-init-output.log 392 | datetime_format = 393 | 394 | [/var/log/cfn-init.log] 395 | file = /var/log/cfn-init.log 396 | log_group_name = ${Logs} 397 | log_stream_name = {instance_id}/cfn-init.log 398 | datetime_format = 399 | 400 | [/var/log/cfn-hup.log] 401 | file = /var/log/cfn-hup.log 402 | log_group_name = ${Logs} 403 | log_stream_name = {instance_id}/cfn-hup.log 404 | datetime_format = 405 | 406 | [/var/log/cfn-wire.log] 407 | file = /var/log/cfn-wire.log 408 | log_group_name = ${Logs} 409 | log_stream_name = {instance_id}/cfn-wire.log 410 | datetime_format = 411 | 412 | [/var/log/httpd] 413 | file = /var/log/httpd/* 414 | log_group_name = ${Logs} 415 | log_stream_name = {instance_id}/httpd 416 | datetime_format = %d/%b/%Y:%H:%M:%S 417 | mode: '000444' 418 | owner: root 419 | group: root 420 | /etc/awslogs/awscli.conf: 421 | content: !Sub | 422 | [plugins] 423 | cwlogs = cwlogs 424 | [default] 425 | region = ${AWS::Region} 426 | mode: '000444' 427 | owner: root 428 | group: root 429 | commands: 430 | create_state_directory: 431 | command: mkdir -p /var/awslogs/state 432 | install_codedeploy: 433 | files: 434 | /home/ec2-user/install: 435 | source: !Join ["", ["https://aws-codedeploy-", !Ref "AWS::Region", ".s3.", !Ref "AWS::Region", ".amazonaws.com/latest/install" ] ] 436 | mode: "000755" 437 | commands: 438 | install_codedeploy: 439 | command: "./install auto" 440 | cwd: "/home/ec2-user/" 441 | install_webserver: 442 | files: 443 | /tmp/status.txt: 444 | content: !Sub | 445 | Health Check 446 | mode: '000644' 447 | owner: root 448 | group: root 449 | /tmp/create_site_conf.sh: 450 | content: !Sub | 451 | #!/bin/bash -xe 452 | 453 | # Below to be able to compile zip.so for the PHP Zip library that's not in the available packages... 454 | pecl install zip 455 | echo "extension=zip.so;" > /etc/php.d/50-zip.ini 456 | 457 | # Install Sodium 458 | pecl install -f libsodium 459 | echo "extension=sodium.so;" > /etc/php.d/50-sodium.ini 460 | 461 | # Install Redis client as well as related extensions - see here https://github.com/amazonlinux/amazon-linux-2023/issues/328 462 | pear update-channels 463 | pecl update-channels 464 | /usr/bin/yes 'no' | pecl install igbinary 465 | echo 'extension=igbinary.so' > /etc/php.d/30-igbinary.ini 466 | /usr/bin/yes 'no' | pecl install msgpack 467 | echo 'extension=msgpack.so' > /etc/php.d/30-msgpack.ini 468 | /usr/bin/yes 'no' | pecl install zstd 469 | echo 'extension=zstd.so' > /etc/php.d/40-zstd.ini 470 | /usr/bin/yes 'no' | pecl install --configureoptions 'enable-lzf-better-compression="no"' lzf 471 | echo 'extension=lzf.so' > /etc/php.d/40-lzf.ini 472 | /usr/bin/yes 'no' | pecl install --configureoptions 'enable-redis-igbinary="yes" enable-redis-lzf="yes" enable-redis-zstd="yes" enable-redis-msgpack="yes" enable-redis-lz4="yes" with-liblz4="yes"' redis 473 | echo 'extension=redis.so' > /etc/php.d/41-redis.ini 474 | 475 | # Adjust base php.ini 476 | sed -i 's/memory_limit =.*/memory_limit = 4096M/' /etc/php.ini 477 | sed -i 's/;max_input_vars.*/max_input_vars = 5000/' /etc/php.ini 478 | 479 | # Create Apache config 480 | if [ ! -f /etc/httpd/conf.d/moodle.conf ]; then 481 | touch /etc/httpd/conf.d/moodle.conf 482 | echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/moodle.conf 483 | echo 'DocumentRoot /var/www/moodle/html' >> /etc/httpd/conf.d/moodle.conf 484 | echo '' >> /etc/httpd/conf.d/moodle.conf 485 | echo ' Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/moodle.conf 486 | echo ' AllowOverride All' >> /etc/httpd/conf.d/moodle.conf 487 | echo ' Require all granted' >> /etc/httpd/conf.d/moodle.conf 488 | echo '' >> /etc/httpd/conf.d/moodle.conf 489 | fi 490 | 491 | # Create hidden opcache directory locally & change owner to apache 492 | if [ ! -d /var/www/.opcache ]; then 493 | mkdir -p /var/www/.opcache 494 | fi 495 | # Ensure opcache is enabled and add settings recomended by moodle at https://docs.moodle.org/34/en/OPcache 496 | sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php.d/10-opcache.ini 497 | sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=512/' /etc/php.d/10-opcache.ini 498 | sed -i 's/opcache.max_accelerated_files=.*/opcache.max_accelerated_files=8000/' /etc/php.d/10-opcache.ini 499 | sed -i 's/;opcache.revalidate_freq=.*/opcache.revalidate_freq=300/' /etc/php.d/10-opcache.ini 500 | sed -i 's/;opcache.use_cwd=.*/opcache.use_cwd=1/' /etc/php.d/10-opcache.ini 501 | sed -i 's/;opcache.validate_timestamps=.*/opcache.validate_timestamps=1/' /etc/php.d/10-opcache.ini 502 | sed -i 's/;opcache.save_comments=.*/opcache.save_comments=1/' /etc/php.d/10-opcache.ini 503 | sed -i 's/;opcache.file_cache_only=.*/opcache.file_cache_only=1/' /etc/php.d/10-opcache.ini 504 | 505 | # Install ElastiCache client 506 | #if [ $(uname -a | grep -c x86_64) == "1" ]; then 507 | # echo "downloading x86 client for ElastiCache" 508 | # wget -P /tmp/ https://elasticache-downloads.s3.amazonaws.com/ClusterClient/PHP-8.1/latest-64bit-X86-openssl3 509 | # tar -xf '/tmp/latest-64bit-X86-openssl3' 510 | #else 511 | # echo "downloading ARM-64 client for ElastiCache" 512 | # wget -P /tmp/ https://elasticache-downloads.s3.amazonaws.com/ClusterClient/PHP-8.2/latest-64bit-arm-X86-openssl3 513 | # tar -xf '/tmp/latest-64bit-arm-X86-openssl3' 514 | #fi 515 | #mv amazon-elasticache-cluster-client.so /usr/lib64/php/modules/ 516 | #echo 'extension=amazon-elasticache-cluster-client.so;' > /etc/php.d/50-elasticache.ini 517 | # Install Memcached client - note that ElastiCache client is failing on AL2023 518 | /usr/bin/yes 'no' | pecl install --configureoptions 'enable-memcached-igbinary="yes" enable-memcached-msgpack="yes" enable-memcached-json="yes" enable-memcached-protocol="yes" enable-memcached-sasl="yes" enable-memcached-session="yes"' memcached 519 | echo 'extension=memcached.so' > /etc/php.d/41-memcached.ini 520 | 521 | # Mount EFS 522 | availabilityzone=$(ec2-metadata -z | awk '{print $2}' | sed 's/(.)//') 523 | region=$(ec2-metadata -z | awk '{print $2}' | sed 's/[a-z]$//') 524 | if grep -qs '/var/www/moodle/data ' /proc/mounts; then 525 | echo "/var/www/moodle/data is mounted." 526 | else 527 | export EnvElasticFileSystem=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/SharedFile/ElasticFileSystem --query Parameters[0].Value) 528 | export EnvElasticFileSystem=`echo $EnvElasticFileSystem | sed -e 's/^"//' -e 's/"$//'` 529 | 530 | sudo mkdir -p /$EnvElasticFileSystem 531 | sudo mountpoint -q /$EnvElasticFileSystem || sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $EnvElasticFileSystem.efs.${AWS::Region}.amazonaws.com:/ /$EnvElasticFileSystem 532 | 533 | #Create directories for Moodle 534 | sudo mkdir -p /$EnvElasticFileSystem/data 535 | sudo mkdir -p /$EnvElasticFileSystem/cache 536 | sudo mkdir -p /$EnvElasticFileSystem/temp 537 | 538 | chown apache:apache /$EnvElasticFileSystem/data/ 539 | chown apache:apache /$EnvElasticFileSystem/cache/ 540 | chown apache:apache /$EnvElasticFileSystem/temp/ 541 | 542 | sudo umount -f /$EnvElasticFileSystem 543 | 544 | mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $EnvElasticFileSystem.efs.${AWS::Region}.amazonaws.com:/data /var/www/moodle/data 545 | #mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $EnvElasticFileSystem.efs.${AWS::Region}.amazonaws.com:/cache /var/www/moodle/cache 546 | #mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $EnvElasticFileSystem.efs.${AWS::Region}.amazonaws.com:/temp /var/www/moodle/temp 547 | fi 548 | 549 | cp /tmp/status.txt /var/www/moodle/html/status.txt 550 | mode: 000500 551 | owner: root 552 | group: root 553 | commands: 554 | create_site_conf: 555 | command: ./create_site_conf.sh 556 | cwd: /tmp 557 | start_services: 558 | services: 559 | sysvinit: 560 | httpd: 561 | enabled: true 562 | ensureRunning: true 563 | crond: 564 | enabled: true 565 | ensureRunning: true 566 | add_crontab: 567 | files: 568 | /tmp/add_crontab.sh: 569 | content: !Sub | 570 | #!/bin/sh -xe 571 | echo "* * * * * apache /usr/bin/php /var/www/moodle/html/admin/cli/cron.php" >> /etc/cron.d/moodle 572 | mode: 000500 573 | owner: root 574 | group: root 575 | commands: 576 | add_crontab: 577 | command: ./add_crontab.sh 578 | cwd: /tmp 579 | Properties: 580 | LaunchTemplateData: 581 | BlockDeviceMappings: 582 | - DeviceName: /dev/xvda 583 | Ebs: 584 | DeleteOnTermination: true 585 | VolumeSize: 10 586 | VolumeType: gp3 587 | IamInstanceProfile: 588 | Arn: !GetAtt WebInstanceProfile.Arn 589 | ImageId: !If [UsingGraviton2Ami, !Ref LatestArmAmiId, !Ref LatestAmiId] 590 | InstanceType: !Ref WebInstanceType 591 | Monitoring: 592 | Enabled: true 593 | SecurityGroupIds: 594 | - !Ref WebSecurityGroup 595 | UserData: 596 | Fn::Base64: 597 | !Sub | 598 | #!/bin/bash -xe 599 | sudo systemctl enable amazon-ssm-agent 600 | sudo systemctl start amazon-ssm-agent 601 | 602 | #Create directory structure 603 | mkdir -p /var/www/moodle/html 604 | mkdir -p /var/www/moodle/data 605 | mkdir -p /var/www/moodle/cache 606 | mkdir -p /var/www/moodle/temp 607 | mkdir -p /var/www/moodle/local 608 | 609 | #Run CloudFormation Init Scripts 610 | /opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchTemplate --region ${AWS::Region} 611 | /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region} 612 | 613 | Outputs: 614 | WebAutoScalingGroupName: 615 | Value: !Ref WebAutoScalingGroup 616 | -------------------------------------------------------------------------------- /templates/03-pipelinehelper.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | 4 | Description: This templates helps creating git-remote-s3 repo, S3 Bucket and update repo with Moodle code & configurations. 5 | 6 | Parameters: 7 | PipelineSecurityGroup: 8 | Description: Select the Pipeline security group Id 9 | Type: AWS::EC2::SecurityGroup::Id 10 | 11 | NumberOfSubnets: 12 | AllowedValues: 13 | - 1 14 | - 2 15 | - 3 16 | Default: 2 17 | Description: Number of subnets. This must match your selections in the list of Subnets below. 18 | Type: String 19 | 20 | PipelineSubnet: 21 | Description: Select existing subnets. 22 | Type: List 23 | 24 | InstanceType: 25 | AllowedValues: 26 | - t3.nano 27 | - t3.micro 28 | - t3.small 29 | - t3.medium 30 | - t3.large 31 | - t3.xlarge 32 | - t3.2xlarge 33 | - m5.large 34 | - m5.xlarge 35 | - m5.2xlarge 36 | - m5.4xlarge 37 | - m5.8xlarge 38 | - m5.12xlarge 39 | - m5.16xlarge 40 | - m5.24xlarge 41 | - c5.large 42 | - c5.xlarge 43 | - c5.2xlarge 44 | - c5.4xlarge 45 | - c5.9xlarge 46 | - c5.12xlarge 47 | - c5.18xlarge 48 | - c5.24xlarge 49 | - r5.large 50 | - r5.xlarge 51 | - r5.2xlarge 52 | - r5.4xlarge 53 | - r5.8xlarge 54 | - r5.12xlarge 55 | - r5.16xlarge 56 | - r5.24xlarge 57 | - t3a.nano 58 | - t3a.micro 59 | - t3a.small 60 | - t3a.medium 61 | - t3a.large 62 | - t3a.xlarge 63 | - t3a.2xlarge 64 | - m5a.large 65 | - m5a.xlarge 66 | - m5a.2xlarge 67 | - m5a.4xlarge 68 | - m5a.8xlarge 69 | - m5a.12xlarge 70 | - m5a.16xlarge 71 | - m5a.24xlarge 72 | - c5a.large 73 | - c5a.xlarge 74 | - c5a.2xlarge 75 | - c5a.4xlarge 76 | - c5a.9xlarge 77 | - c5a.12xlarge 78 | - c5a.18xlarge 79 | - c5a.24xlarge 80 | - r5a.large 81 | - r5a.xlarge 82 | - r5a.2xlarge 83 | - r5a.4xlarge 84 | - r5a.8xlarge 85 | - r5a.12xlarge 86 | - r5a.16xlarge 87 | - r5a.24xlarge 88 | - t4g.nano 89 | - t4g.micro 90 | - t4g.small 91 | - t4g.medium 92 | - t4g.large 93 | - t4g.xlarge 94 | - t4g.2xlarge 95 | - m6g.large 96 | - m6g.xlarge 97 | - m6g.2xlarge 98 | - m6g.4xlarge 99 | - m6g.8xlarge 100 | - m6g.12xlarge 101 | - m6g.16xlarge 102 | - m6g.24xlarge 103 | - m7g.medium 104 | - m7g.large 105 | - m7g.xlarge 106 | - m7g.2xlarge 107 | - m7g.4xlarge 108 | - m7g.8xlarge 109 | - m7g.12xlarge 110 | - m7g.16xlarge 111 | - c6g.large 112 | - c6g.xlarge 113 | - c6g.2xlarge 114 | - c6g.4xlarge 115 | - c6g.9xlarge 116 | - c6g.12xlarge 117 | - c6g.18xlarge 118 | - c6g.24xlarge 119 | - r6g.large 120 | - r6g.xlarge 121 | - r6g.2xlarge 122 | - r6g.4xlarge 123 | - r6g.8xlarge 124 | - r6g.12xlarge 125 | - r6g.16xlarge 126 | - r6g.24xlarge 127 | - c7g.medium 128 | - c7g.large 129 | - c7g.xlarge 130 | - c7g.2xlarge 131 | - c7g.4xlarge 132 | - c7g.8xlarge 133 | - c7g.12xlarge 134 | - c7g.16xlarge 135 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 136 | Default: c7g.xlarge 137 | Description: The Amazon EC2 instance type that dynamically adjusts thresholds based on permitted throughput changes. 138 | Type: String 139 | 140 | LatestAmiId: 141 | Type : AWS::SSM::Parameter::Value 142 | Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 143 | LatestArmAmiId : 144 | Type : AWS::SSM::Parameter::Value 145 | Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 146 | 147 | MoodleLocale: 148 | Description: "The main language of the Moodle site, during initial configuration." 149 | Type: String 150 | Default: en 151 | 152 | DomainName: 153 | Description: '[ Optional ] The main domain name of the Moodle site (e.g. moodle.example.edu).' 154 | Type: String 155 | 156 | RDSInstanceSecretArn: 157 | Description: 'Credentials for Moodle RDS instance' 158 | Type: String 159 | Default: "" 160 | 161 | ProjectName: 162 | AllowedPattern: ^([a-zA-Z0-9]*)$ 163 | Default: App 164 | Description: The Moodle Project Name 165 | Type: String 166 | 167 | WebAsgMax: 168 | AllowedPattern: ^((?!0$)[1-2]?[0-9]|30)$ 169 | ConstraintDescription: Must be a number between 1 and 30. 170 | Default: 1 171 | Description: Specifies the maximum number of EC2 instances in the Web Autoscaling Group. 172 | Type: String 173 | WebAsgMin: 174 | AllowedPattern: ^([0-0]?[0-9]|10)$ 175 | ConstraintDescription: Must be a number between 0 and 10. 176 | Default: 2 177 | Description: Specifies the minimum number of EC2 instances in the Web Autoscaling Group. 178 | Type: String 179 | MoodleDirectDownloadURL: 180 | Default: "https://download.moodle.org/download.php/direct/stable404/moodle-4.4.tgz" 181 | Description: Specifies the TGZ Moodle direct download URL 182 | Type: String 183 | 184 | Conditions: 185 | NumberOfSubnets1: !Equals [ 1, !Ref NumberOfSubnets ] 186 | NumberOfSubnets2: !Equals [ 2, !Ref NumberOfSubnets ] 187 | UsingGraviton2Ami: !Or 188 | - !Equals ["t4",!Select [0, !Split [ "g.", !Ref InstanceType]]] 189 | - !Equals ["c6",!Select [0, !Split [ "g.", !Ref InstanceType]]] 190 | - !Equals ["c7",!Select [0, !Split [ "g.", !Ref InstanceType]]] 191 | - !Equals ["m6",!Select [0, !Split [ "g.", !Ref InstanceType]]] 192 | - !Equals ["m7",!Select [0, !Split [ "g.", !Ref InstanceType]]] 193 | - !Equals ["r6",!Select [0, !Split [ "g.", !Ref InstanceType]]] 194 | - !Equals ["r7",!Select [0, !Split [ "g.", !Ref InstanceType]]] 195 | 196 | Resources: 197 | # This bucket is being used as the repo for git-s3-remote 198 | MoodleGitBucket: 199 | Type: AWS::S3::Bucket 200 | DeletionPolicy: RetainExceptOnCreate 201 | Properties: 202 | VersioningConfiguration: 203 | Status: Enabled 204 | PublicAccessBlockConfiguration: 205 | BlockPublicAcls: true 206 | BlockPublicPolicy: true 207 | IgnorePublicAcls: true 208 | RestrictPublicBuckets: true 209 | BucketEncryption: 210 | ServerSideEncryptionConfiguration: 211 | - ServerSideEncryptionByDefault: 212 | SSEAlgorithm: AES256 213 | 214 | #This bucket is being used for storing Code artifacts for deployment. 215 | CodeArtifactS3Bucket: 216 | Type: AWS::S3::Bucket 217 | DeletionPolicy: RetainExceptOnCreate 218 | Properties: 219 | PublicAccessBlockConfiguration: 220 | BlockPublicAcls: true 221 | BlockPublicPolicy: true 222 | IgnorePublicAcls: true 223 | RestrictPublicBuckets: true 224 | BucketEncryption: 225 | ServerSideEncryptionConfiguration: 226 | - ServerSideEncryptionByDefault: 227 | SSEAlgorithm: AES256 228 | 229 | InstanceProfile: 230 | Type: AWS::IAM::InstanceProfile 231 | Properties: 232 | Path: / 233 | Roles: 234 | - !Ref InstanceRole 235 | 236 | InstanceRole: 237 | Type: AWS::IAM::Role 238 | Properties: 239 | AssumeRolePolicyDocument: 240 | Version: 2012-10-17 241 | Statement: 242 | - Effect: Allow 243 | Principal: 244 | Service: 245 | - ec2.amazonaws.com 246 | Action: 247 | - sts:AssumeRole 248 | ManagedPolicyArns: 249 | - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore' 250 | Path: / 251 | Policies: 252 | - PolicyName: MoodlePipelineHelperPolicy 253 | PolicyDocument: 254 | Version: 2012-10-17 255 | Statement: 256 | - Effect: Allow 257 | Action: 258 | - autoscaling:DescribeAutoScalingGroups 259 | - autoscaling:DescribeAutoScalingInstances 260 | - autoscaling:DescribePolicies 261 | - autoscaling:UpdateAutoScalingGroup 262 | Resource: '*' 263 | - Effect: Allow 264 | Action: 265 | - codepipeline:StartPipelineExecution 266 | Resource: 267 | !Sub 'arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${ProjectName}-Pipeline' 268 | - Effect: Allow 269 | Action: 270 | - s3:PutObject 271 | - s3:GetObject 272 | - s3:DeleteObject 273 | Resource: !Sub "${MoodleGitBucket.Arn}/*" 274 | - Effect: Allow 275 | Action: s3:ListBucket 276 | Resource: !GetAtt MoodleGitBucket.Arn 277 | 278 | PipelineHelperASGroup: 279 | Type: AWS::AutoScaling::AutoScalingGroup 280 | Properties: 281 | Cooldown: 60 282 | HealthCheckGracePeriod: 120 283 | HealthCheckType: EC2 284 | LaunchTemplate: 285 | LaunchTemplateId: !Ref PipelineHelperLaunchTemplate 286 | Version: !GetAtt PipelineHelperLaunchTemplate.LatestVersionNumber 287 | MaxSize: 1 288 | MinSize: 0 289 | DesiredCapacity: 1 290 | Tags: 291 | - Key: Name 292 | Value: !Join [ '', [ 'Moodle Pipeline Helper s3+zip://' , !Ref MoodleGitBucket , '/' , !Ref ProjectName , ' ...will auto terminate' ] ] 293 | PropagateAtLaunch: true 294 | VPCZoneIdentifier: 295 | !If 296 | [ NumberOfSubnets1, 297 | [ !Select [ 0, !Ref PipelineSubnet ] ], 298 | !If 299 | [ NumberOfSubnets2, 300 | [ !Select [ 0, !Ref PipelineSubnet ], !Select [ 1, !Ref PipelineSubnet ] ], 301 | [ !Select [ 0, !Ref PipelineSubnet ], !Select [ 1, !Ref PipelineSubnet ], !Select [ 2, !Ref PipelineSubnet ] ] 302 | ] 303 | ] 304 | CreationPolicy: 305 | ResourceSignal: 306 | Count: 0 307 | Timeout: PT15M 308 | UpdatePolicy: 309 | AutoScalingReplacingUpdate: 310 | WillReplace: true 311 | 312 | PipelineHelperLaunchTemplate: 313 | Type: AWS::EC2::LaunchTemplate 314 | Metadata: 315 | AWS::CloudFormation::Init: 316 | configSets: 317 | moodle_git_config: 318 | - moodle-git-config 319 | moodle-git-config: 320 | files: 321 | /tmp/appspec.yml: 322 | content: !Sub | 323 | version: 0.0 324 | os: linux 325 | files: 326 | - source: / 327 | destination: /var/www/moodle/html/ 328 | hooks: 329 | ApplicationStop: 330 | - location: .pipeline/stop_application.sh 331 | timeout: 300 332 | AfterInstall: 333 | - location: .pipeline/after_install.sh 334 | timeout: 300 335 | ApplicationStart: 336 | - location: .pipeline/start_application.sh 337 | timeout: 300 338 | ValidateService: 339 | - location: .pipeline/basic_health_check.sh 340 | timeout: 300 341 | mode: '000755' 342 | owner: root 343 | group: root 344 | /tmp/start_application.sh: 345 | content: !Sub | 346 | #!/bin/bash 347 | sudo systemctl start php-fpm 348 | sudo systemctl start httpd 349 | mode: '000755' 350 | owner: root 351 | group: root 352 | /tmp/stop_application.sh: 353 | content: !Sub | 354 | #!/bin/bash 355 | sudo systemctl stop httpd 356 | sudo systemctl stop php-fpm 357 | mode: '000755' 358 | owner: root 359 | group: root 360 | /tmp/basic_health_check.sh: 361 | content: !Sub | 362 | #!/bin/bash 363 | for i in `seq 1 10`; 364 | do 365 | HTTP_CODE=`curl --write-out '%{http_code}' -o /dev/null -m 10 -q -s http://localhost:80/status.txt` 366 | if [ "$HTTP_CODE" == "200" ]; then 367 | echo "Successfully pulled root page." 368 | exit 0; 369 | fi 370 | echo "Attempt to curl endpoint returned HTTP Code $HTTP_CODE. Backing off and retrying." 371 | sleep 10 372 | done 373 | echo "Server did not come up after expected time. Failing." 374 | exit 1 375 | mode: '000755' 376 | owner: root 377 | group: root 378 | /tmp/config.php: 379 | content: !Sub | 380 | '2017-10-17', 388 | 'region' => '${AWS::Region}', 389 | ]); 390 | 391 | $secretName = '${RDSInstanceSecretArn}'; 392 | 393 | try { 394 | $result = $client->getSecretValue([ 395 | 'SecretId' => $secretName, 396 | ]); 397 | 398 | } catch (AwsException $e) { 399 | $error = $e->getAwsErrorCode(); 400 | } 401 | // Decrypts secret using the associated KMS CMK. 402 | // Depending on whether the secret is a string or binary, one of these fields will be populated. 403 | if (isset($result['SecretString'])) { 404 | $secret = $result['SecretString']; 405 | } 406 | $CFG = new stdClass; 407 | $CFG->getremoteaddrconf = 0; 408 | $CFG->dbtype = 'pgsql'; 409 | $CFG->dblibrary = 'native'; 410 | $CFG->dbhost = getenv('EnvDatabaseClusterEndpointAddress'); 411 | $CFG->dbname = getenv('EnvDatabaseName'); 412 | $CFG->dbuser = json_decode($secret)->{'username'}; 413 | $CFG->dbpass = json_decode($secret)->{'password'}; 414 | $CFG->prefix = 'mdl_'; 415 | $CFG->lang = '${MoodleLocale}'; 416 | $CFG->dboptions = array( 417 | 'dbpersist' => false, 418 | 'dbsocket' => false, 419 | 'dbport' => '', 420 | 'dbhandlesoptions' => false, 421 | 'dbcollation' => 'utf8mb4_unicode_ci', 422 | 'connecttimeout' => 300, 423 | 'readonly' => [ 424 | 'instance' => 'db-cluster-readonly-endpoint', 425 | 'connecttimeout' => 300, 426 | 'latency' => 2, 427 | 'exclude_tables' => [ 428 | 'config', 429 | ], 430 | ] 431 | ); 432 | 433 | // Hostname definition // 434 | $hostname = '${DomainName}'; 435 | $hostwithprotocol = strtolower($hostname); 436 | 437 | if(substr($hostwithprotocol, 0, 4) === 'http'){} else { 438 | $hostwithprotocol = 'http://'.strtolower($hostwithprotocol); 439 | } 440 | 441 | $CFG->wwwroot = strtolower($hostwithprotocol); 442 | $CFG->sslproxy = (substr($hostwithprotocol,0,5)=='https' ? true : false); 443 | // Moodledata location // 444 | $CFG->dataroot = '/var/www/moodle/data'; 445 | $CFG->tempdir = '/var/www/moodle/temp'; 446 | $CFG->cachedir = '/var/www/moodle/cache'; 447 | $CFG->localcachedir = '/var/www/moodle/local'; 448 | $CFG->directorypermissions = 02777; 449 | $CFG->admin = 'admin'; 450 | // Configure Session Cache 451 | $SessionsCacheType = 'Memcached'; 452 | $SessionEndpoint = ''; 453 | if ($SessionEndpoint != '') { 454 | 455 | $CFG->dbsessions = false; 456 | 457 | if($SessionsCacheType == 'Redis') { 458 | 459 | $CFG->session_handler_class = '\core\session\redis'; 460 | $CFG->session_redis_host = $SessionEndpoint; 461 | $CFG->session_redis_port = 6379; // Optional. 462 | $CFG->session_redis_database = 0; // Optional, default is db 0. 463 | //$CFG->session_redis_auth = ''; // Optional, default is don't set one. 464 | //$CFG->session_redis_prefix = ''; // Optional, default is don't set one. 465 | $CFG->session_redis_acquire_lock_timeout = 120; // Default is 2 minutes. 466 | $CFG->session_redis_acquire_lock_warn = 0; // If set logs early warning if a lock has not been acquried. 467 | $CFG->session_redis_lock_expire = 7200; // Optional, defaults to session timeout. 468 | $CFG->session_redis_lock_retry = 100; // Optional wait between lock attempts in ms, default is 100. 469 | 470 | $CFG->session_redis_serializer_use_igbinary = false; // Optional, default is PHP builtin serializer. 471 | $CFG->session_redis_compressor = 'none'; 472 | } else { 473 | 474 | $CFG->session_handler_class = '\core\session\memcached'; 475 | $CFG->session_memcached_save_path = $SessionEndpoint; 476 | $CFG->session_memcached_prefix = 'memc.sess.key.'; 477 | $CFG->session_memcached_acquire_lock_timeout = 120; 478 | $CFG->session_memcached_lock_expire = 7100; 479 | $CFG->session_memcached_lock_retry_sleep = 150; 480 | } 481 | } 482 | //@error_reporting(E_ALL | E_STRICT); // NOT FOR PRODUCTION SERVERS! 483 | //@ini_set('display_errors', '1'); // NOT FOR PRODUCTION SERVERS! 484 | //$CFG->debug = (E_ALL | E_STRICT); // === DEBUG_DEVELOPER - NOT FOR PRODUCTION SERVERS! 485 | //$CFG->debugdisplay = 1; 486 | require_once(__DIR__ . '/lib/setup.php'); 487 | // END OF CONFIG // 488 | ?> 489 | mode: '000755' 490 | owner: root 491 | group: root 492 | 493 | /tmp/after_install.sh: 494 | content: 495 | !Sub | 496 | #!/bin/bash -xe 497 | 498 | # Setting up access ownership to apache:apache 499 | chown -R apache:apache /var/www/moodle/html 500 | chown -R apache:apache /var/www/moodle/data 501 | chown -R apache:apache /var/www/moodle/cache 502 | chown -R apache:apache /var/www/moodle/temp 503 | chown -R apache:apache /var/www/moodle/local 504 | 505 | availabilityzone=$(ec2-metadata -z | awk '{print $2}' | sed 's/(.)//') 506 | region=$(ec2-metadata -z | awk '{print $2}' | sed 's/[a-z]$//') 507 | 508 | export EnvDatabaseType=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/DB/Type --query Parameters[0].Value) 509 | export EnvDatabaseType=`echo $EnvDatabaseType | sed -e 's/^"//' -e 's/"$//'` 510 | 511 | if [ "$EnvDatabaseType" == "MySQL" ]; then 512 | sed -i "s/\$CFG->dbtype = .*/\$CFG->dbtype = 'auroramysql';/" /var/www/moodle/html/config.php 513 | else 514 | sed -i "s/\$CFG->dbtype = .*/\$CFG->dbtype = 'pgsql';/" /var/www/moodle/html/config.php 515 | fi 516 | 517 | export EnvDatabaseName=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/DB/Name --query Parameters[0].Value) 518 | export EnvDatabaseName=`echo $EnvDatabaseName | sed -e 's/^"//' -e 's/"$//'` 519 | sed -i "s/\$CFG->dbname.*/\$CFG->dbname = '"$EnvDatabaseName"';/" /var/www/moodle/html/config.php 520 | 521 | export EnvDatabaseClusterEndpointAddress=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/DB/ClusterEndpoint --query Parameters[0].Value) 522 | export EnvDatabaseClusterEndpointAddress=`echo $EnvDatabaseClusterEndpointAddress | sed -e 's/^"//' -e 's/"$//'` 523 | sed -i "s/\$CFG->dbhost.*/\$CFG->dbhost = '"$EnvDatabaseClusterEndpointAddress"';/" /var/www/moodle/html/config.php 524 | 525 | export EnvDatabaseClusterReadOnlyEndpointAddress=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/DB/ClusterReadOnlyEndpoint --query Parameters[0].Value) 526 | export EnvDatabaseClusterReadOnlyEndpointAddress=`echo $EnvDatabaseClusterReadOnlyEndpointAddress | sed -e 's/^"//' -e 's/"$//'` 527 | sed -i "s/'instance' => '.*/'instance' => '"$EnvDatabaseClusterReadOnlyEndpointAddress"',/" /var/www/moodle/html/config.php 528 | 529 | export EnvDnsName=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/Network/DomainName --query Parameters[0].Value) 530 | export EnvDnsName=`echo $EnvDnsName | sed -e 's/^"//' -e 's/"$//'` 531 | sed -i'' -e "s,\$hostname = .*,\$hostname = '"$EnvDnsName"';," /var/www/moodle/html/config.php 532 | 533 | export EnvIsMoodleSetupCompleted=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/IsMoodleSetupCompleted --query Parameters[0].Value) 534 | export EnvIsMoodleSetupCompleted=`echo $EnvIsMoodleSetupCompleted | sed -e 's/^"//' -e 's/"$//'` 535 | 536 | export EnvElastiCacheClusterEndpointAddress=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/Cache/session/ElastiCacheClusterEndpoint --query Parameters[0].Value) 537 | export EnvElastiCacheClusterEndpointAddress=`echo $EnvElastiCacheClusterEndpointAddress | sed -e 's/^"//' -e 's/"$//' | cut -f1 -d":"` 538 | 539 | export EnvElastiCacheEngine=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/Cache/session/Engine --query Parameters[0].Value) 540 | export EnvElastiCacheEngine=`echo $EnvElastiCacheEngine | sed -e 's/^"//' -e 's/"$//'` 541 | 542 | # Setting up ElastiCache dependencies for cache 543 | if [ "$EnvIsMoodleSetupCompleted" != "No" ] && [ "$EnvElastiCacheClusterEndpointAddress" != "null" -a "$EnvElastiCacheClusterEndpointAddress" != "" ]; then 544 | sed -i "s/\$SessionEndpoint = .*/\$SessionEndpoint = '"$EnvElastiCacheClusterEndpointAddress"';/" /var/www/moodle/html/config.php 545 | 546 | if [ "$EnvElastiCacheEngine" == "Redis" ]; then 547 | sed -i "s/\$SessionsCacheType = .*/\$SessionsCacheType = '"$EnvElastiCacheEngine"';/" /var/www/moodle/html/config.php 548 | #else 549 | # Below commented out because we don't use the ElastiCache client anymore 550 | #update Moodle source to use DYNAMIC_CLIENT_MODE so Moodle can detect changes to the elasticache cluster membership 551 | #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 552 | fi 553 | else 554 | sed -i "s/\$SessionEndpoint = .*/\$SessionEndpoint = '';/" /var/www/moodle/html/config.php 555 | fi 556 | mode: 000500 557 | owner: root 558 | group: root 559 | /tmp/moodle-git-config.sh: 560 | content: !Sub | 561 | #!/bin/bash -x 562 | 563 | python3 -m pip install git-remote-s3 564 | export PATH=$PATH:~/.local/bin 565 | 566 | git config --system user.name 'AWS User' 567 | git config --system user.email noreply@amazon.com 568 | 569 | DIR="/tmp/moodle" 570 | if [ -d "$DIR" ]; then 571 | cd /tmp/moodle 572 | else 573 | mkdir /tmp/moodle 574 | mkdir /tmp/moodle/.pipeline 575 | 576 | # Get Latest Moodle stable version 577 | wget -O /tmp/moodle.tgz ${MoodleDirectDownloadURL} 578 | tar -xvzf /tmp/moodle.tgz --strip-components=1 -C /tmp/moodle/ 579 | wget -O /tmp/moodle/lib/aws.phar https://docs.aws.amazon.com/aws-sdk-php/v3/download/aws.phar 580 | cd /tmp/moodle 581 | git init 582 | git checkout -b main 583 | git add . 584 | git commit -m "Moodle original code commit." 585 | git remote add origin s3+zip://${MoodleGitBucket}/${ProjectName} 586 | fi 587 | 588 | #Added code scripts for CodePipeline 589 | 590 | cp -f /tmp/appspec.yml /tmp/moodle/ 591 | 592 | cp -f /tmp/start_application.sh /tmp/moodle/.pipeline/ 593 | cp -f /tmp/stop_application.sh /tmp/moodle/.pipeline/ 594 | cp -f /tmp/basic_health_check.sh /tmp/moodle/.pipeline/ 595 | 596 | cp -f /tmp/config.php /tmp/moodle/ 597 | 598 | cp -f /tmp/before_install.sh /tmp/moodle/.pipeline/ 599 | cp -f /tmp/setup_efs.sh /tmp/moodle/.pipeline/ 600 | cp -f /tmp/configure_opcache.sh /tmp/moodle/.pipeline/ 601 | 602 | cp -f /tmp/install_mysql_dependencies.sh /tmp/moodle/.pipeline/ 603 | cp -f /tmp/install_pgsql_dependencies.sh /tmp/moodle/.pipeline/ 604 | cp -f /tmp/install_cacheclient.sh /tmp/moodle/.pipeline/ 605 | 606 | cp -f /tmp/after_install.sh /tmp/moodle/.pipeline/ 607 | 608 | git add --all 609 | git commit -m "Moodle Code pipeline commits" 610 | 611 | git push -u origin main 612 | 613 | # get instance id 614 | instance_id=$(ec2-metadata -i | awk '{print $2}' | sed 's/(.)//') 615 | 616 | # get region from instance meta-data 617 | availabilityzone=$(ec2-metadata -z | awk '{print $2}' | sed 's/(.)//') 618 | region=$(ec2-metadata -z | awk '{print $2}' | sed 's/[a-z]$//') 619 | 620 | # wait for Moodle setup to be completed 621 | echo "Start checking whether Moodle setup completed or not" 622 | counter=0 623 | while true 624 | do 625 | sleep 60 626 | export EnvIsMoodleSetupCompleted=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/IsMoodleSetupCompleted --query Parameters[0].Value) 627 | export EnvIsMoodleSetupCompleted=`echo $EnvIsMoodleSetupCompleted | sed -e 's/^"//' -e 's/"$//'` 628 | 629 | if [ "$EnvIsMoodleSetupCompleted" == "No" ]; then 630 | ((counter++)) 631 | echo "Waiting for 1 more minute, running for the $counter time." 632 | else 633 | break 634 | fi 635 | 636 | done 637 | 638 | # script to update Moodle pipeline release 639 | 640 | aws codepipeline start-pipeline-execution --name ${ProjectName}-Pipeline --region $region 641 | 642 | # set ASG to zero which terminates instance 643 | 644 | export EnvWebAppASGName=$(aws ssm get-parameters --region $region --names /Moodle/${ProjectName}/WebAppASGName --query Parameters[0].Value) 645 | export EnvWebAppASGName=`echo $EnvWebAppASGName | sed -e 's/^"//' -e 's/"$//'` 646 | 647 | aws autoscaling update-auto-scaling-group --auto-scaling-group-name $EnvWebAppASGName --desired-capacity ${WebAsgMin} --min-size ${WebAsgMin} --max-size ${WebAsgMax} --region $region 648 | 649 | # Shutting down pipeline-helper instance 650 | # get autoscaling group name 651 | asg_name=$(aws autoscaling describe-auto-scaling-instances --instance-ids $instance_id --region $region --output text --query 'AutoScalingInstances[0].AutoScalingGroupName') 652 | 653 | # set pipeline-helper ASG to zero which terminates instance 654 | aws autoscaling update-auto-scaling-group --auto-scaling-group-name $asg_name --min-size 0 --desired-capacity 0 --region $region 655 | mode: '000755' 656 | owner: root 657 | group: root 658 | commands: 659 | update-moodle-repo: 660 | command: ./moodle-git-config.sh 661 | cwd: /tmp 662 | ignoreErrors: false 663 | Properties: 664 | LaunchTemplateData: 665 | BlockDeviceMappings: 666 | - DeviceName: /dev/xvda 667 | Ebs: 668 | DeleteOnTermination: true 669 | VolumeSize: 10 670 | VolumeType: gp3 671 | IamInstanceProfile: 672 | Arn: !GetAtt InstanceProfile.Arn 673 | ImageId: !If [UsingGraviton2Ami, !Ref LatestArmAmiId, !Ref LatestAmiId] 674 | InstanceType: !Ref InstanceType 675 | SecurityGroupIds: 676 | - !Ref PipelineSecurityGroup 677 | UserData: 678 | "Fn::Base64": 679 | !Sub | 680 | #!/bin/bash -xe 681 | sudo systemctl enable amazon-ssm-agent 682 | sudo systemctl start amazon-ssm-agent 683 | dnf install -y git pip 684 | 685 | /opt/aws/bin/cfn-init --configsets moodle_git_config --verbose --stack ${AWS::StackName} --resource PipelineHelperLaunchTemplate --region ${AWS::Region} 686 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource PipelineHelperASGroup --region ${AWS::Region} 687 | 688 | Outputs: 689 | CodeArtifactS3BucketArn: 690 | Value: !GetAtt CodeArtifactS3Bucket.Arn 691 | CodeArtifactS3BucketName: 692 | Value: !Ref CodeArtifactS3Bucket 693 | MoodleRepoBucketName: 694 | Value: !Ref MoodleGitBucket 695 | MoodleRepoBucketArn: 696 | Value: !GetAtt MoodleGitBucket.Arn 697 | -------------------------------------------------------------------------------- /templates/00-main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Moodle 4 | 5 | Metadata: 6 | AWS::CloudFormation::Interface: 7 | ParameterGroups: 8 | - Label: 9 | default: General AWS 10 | Parameters: 11 | - DeploymentLocation 12 | - HostedZoneName 13 | - DomainName 14 | - NotifyEmailAddress 15 | - Label: 16 | default: Network 17 | Parameters: 18 | - NumberOfAZs 19 | - AvailabilityZones 20 | - VpcCidr 21 | - PublicSubnet0Cidr 22 | - PublicSubnet1Cidr 23 | - PublicSubnet2Cidr 24 | - AppSubnet0Cidr 25 | - AppSubnet1Cidr 26 | - AppSubnet2Cidr 27 | - DataSubnet0Cidr 28 | - DataSubnet1Cidr 29 | - DataSubnet2Cidr 30 | - Label: 31 | default: Shared File Storage 32 | Parameters: 33 | - SharedStorageEncryptionCmk 34 | - Label: 35 | default: Database Tier 36 | Parameters: 37 | - DatabaseType 38 | - DatabaseUseServerless 39 | - DatabaseMinCapacity 40 | - DatabaseMaxCapacity 41 | - DatabaseInstanceType 42 | - DatabaseCmk 43 | - DatabaseMasterUsername 44 | # - DatabaseMasterPassword 45 | - DatabaseName 46 | - Label: 47 | default: Caching Tier 48 | Parameters: 49 | - CacheEngineType 50 | - UseSessionCacheBoolean 51 | - UseServerlessSessionCache 52 | - SessionCacheNodeType 53 | - UseApplicationCacheBoolean 54 | - UseServerlessApplicationCache 55 | - ApplicationCacheNodeType 56 | - UseCloudFrontBoolean 57 | - CloudFrontAcmCertificate 58 | - Label: 59 | default: Web Tier 60 | Parameters: 61 | - PublicAlbAcmCertificate 62 | - WebInstanceType 63 | - WebAsgMax 64 | - WebAsgMin 65 | - Label: 66 | default: Moodle 67 | Parameters: 68 | - MoodleLocale 69 | ParameterLabels: 70 | DeploymentLocation: 71 | default: "Location to deploy from (S3 URL without trailing slash, e.g.: https://.s3..amazonaws.com)" 72 | CloudFrontAcmCertificate: 73 | default: CloudFront Certificate ARN 74 | DatabaseCmk: 75 | default: AWS KMS CMK for RDS 76 | DatabaseUseServerless: 77 | default: Should the database use Serverless engines? 78 | DatabaseMinCapacity: 79 | default: (If serverless) Minimum capacity for database 80 | DatabaseMaxCapacity: 81 | default: (If serverless) Maximum capacity for database 82 | DatabaseInstanceType: 83 | default: (If NOT serverless) DB Instance Type 84 | DatabaseMasterUsername: 85 | default: DB Master Username 86 | DatabaseName: 87 | default: DB Name 88 | SharedStorageEncryptionCmk: 89 | default: (Optional) Shared File Storage Encryption Key ARN (AWS KMS CMK) 90 | PublicAlbAcmCertificate: 91 | default: ALB Certificate ARN 92 | UseApplicationCacheBoolean: 93 | default: Use Application Cache 94 | ApplicationCacheNodeType: 95 | default: Application Cache Node Type 96 | UseSessionCacheBoolean: 97 | default: Use Session Cache 98 | SessionCacheNodeType: 99 | default: Session Cache Node Type 100 | WebAsgMax: 101 | default: Web ASG Max 102 | WebAsgMin: 103 | default: Web ASG Min 104 | WebInstanceType: 105 | default: Web Tier Instance Type 106 | HostedZoneName: 107 | default: Hosted Zone 108 | DomainName: 109 | default: Domain Name for Moodle site 110 | MoodleLocale: 111 | default: Language Code 112 | AvailabilityZones: 113 | default: Availability Zones 114 | NumberOfAZs: 115 | default: Number of Availability Zones 116 | VpcCidr: 117 | default: VpcCidr 118 | PublicSubnet0Cidr: 119 | default: Public Subnet 0 120 | PublicSubnet1Cidr: 121 | default: Public Subnet 1 122 | PublicSubnet2Cidr: 123 | default: Public Subnet 2 124 | AppSubnet0Cidr: 125 | default: App Subnet 0 126 | AppSubnet1Cidr: 127 | default: App Subnet 1 128 | AppSubnet2Cidr: 129 | default: App Subnet 2 130 | DataSubnet0Cidr: 131 | default: Data Subnet 0 132 | DataSubnet1Cidr: 133 | default: Data Subnet 1 134 | DataSubnet2Cidr: 135 | default: Data Subnet 2 136 | NotifyEmailAddress: 137 | default: Email address for any notification 138 | UseCloudFrontBoolean: 139 | default: Use CloudFront 140 | 141 | Parameters: 142 | DeploymentLocation: 143 | Description: Templates location (S3 URL). By default, uses AWS provided templates. 144 | Type: String 145 | Default: https://s3.amazonaws.com/aws-refarch/moodle/4.4/templates 146 | PublicAlbAcmCertificate: 147 | AllowedPattern: ^$|(arn:aws:acm:)([a-z0-9/:-])*([a-z0-9])$ 148 | Description: '[ Optional ] The AWS Certification Manager certificate ARN for the ALB certificate - this certificate should be created in the region you wish to run the ALB and must reference the domain name you use below.' 149 | Type: String 150 | CloudFrontAcmCertificate: 151 | AllowedPattern: ^$|(arn:aws:acm:)([a-z0-9/:-])*([a-z0-9])$ 152 | 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.' 153 | Type: String 154 | DatabaseCmk: 155 | Description: AWS KMS Customer Master Key (CMK) to encrypt database cluster 156 | Type: String 157 | DatabaseType: 158 | AllowedValues: 159 | - MySQL 160 | - PostgreSQL 161 | Default: PostgreSQL 162 | Description: Indicates whether to use Aurora MySQL or PostgreSQL. 163 | Type: String 164 | DatabaseUseServerless: 165 | AllowedValues: 166 | - true 167 | - false 168 | Default: true 169 | Description: Indicates whether the database should use the serverless engines. 170 | Type: String 171 | DatabaseMinCapacity: 172 | AllowedValues: 173 | - 0.5 174 | - 1 175 | - 2 176 | - 4 177 | - 5 178 | - 16 179 | - 32 180 | - 64 181 | - 128 182 | Default: 0.5 183 | Description: The minimum capacity for an Aurora DB cluster, starts with 0.5 and maximum up to 1 184 | Type: String 185 | DatabaseMaxCapacity: 186 | AllowedValues: 187 | - 0.5 188 | - 1 189 | - 2 190 | - 4 191 | - 5 192 | - 16 193 | - 32 194 | - 64 195 | - 128 196 | Default: 64 197 | Description: The maximum capacity for an Aurora DB cluster, starts with 0.5 and maximum up to 1 198 | Type: String 199 | DatabaseInstanceType: 200 | AllowedValues: 201 | - db.t3.medium 202 | - db.t3.large 203 | - db.t4g.medium 204 | - db.t4g.large 205 | - db.r5.large 206 | - db.r5.xlarge 207 | - db.r5.2xlarge 208 | - db.r5.4xlarge 209 | - db.r5.8xlarge 210 | - db.r5.12xlarge 211 | - db.r5.16xlarge 212 | - db.r6g.large 213 | - db.r6g.xlarge 214 | - db.r6g.2xlarge 215 | - db.r6g.4xlarge 216 | - db.r6g.8xlarge 217 | - db.r6g.12xlarge 218 | - db.r6g.16xlarge 219 | - db.r6i.large 220 | - db.r6i.xlarge 221 | - db.r6i.2xlarge 222 | - db.r6i.4xlarge 223 | - db.r6i.8xlarge 224 | - db.r6i.12xlarge 225 | - db.r6i.16xlarge 226 | - db.r6i.24xlarge 227 | - db.r6i.32xlarge 228 | - db.r6gd.xlarge 229 | - db.r6gd.2xlarge 230 | - db.r6gd.4xlarge 231 | - db.r6gd.8xlarge 232 | - db.r6gd.12xlarge 233 | - db.r6gd.16xlarge 234 | ConstraintDescription: Must be a valid Aurora RDS instance type. 235 | Default: db.r6g.large 236 | Description: Amazon RDS database instance type (only used if non-serverless) 237 | Type: String 238 | DatabaseMasterUsername: 239 | AllowedPattern: ^([a-zA-Z0-9]*)$ 240 | Description: Aurora RDS master username 241 | ConstraintDescription: Must contain only alphanumeric characters and be at least 8 characters. 242 | MaxLength: 16 243 | MinLength: 1 244 | Type: String 245 | Default: moodle 246 | DatabaseName: 247 | AllowedPattern: ^([a-zA-Z0-9]*)$ 248 | Description: Aurora RDS database name 249 | Type: String 250 | Default: moodle 251 | SharedStorageEncryptionCmk: 252 | AllowedPattern: ^$|(arn:aws:kms:)([a-z0-9/:-])*([a-z0-9])$ 253 | ConstraintDescription: Must be an existing ARN for an AWS KMS CMK. 254 | Description: '[ Optional ] The AWS KMS customer-managed CMK ARN to encrypt & decrypt the Shared file storage.' 255 | Type: String 256 | 257 | CacheEngineType: 258 | AllowedValues: 259 | - Redis 260 | - Memcached 261 | Default: Memcached 262 | Description: Indicates whether to use ElastiCache Memcached or Redis. 263 | Type: String 264 | UseSessionCacheBoolean: 265 | AllowedValues: 266 | - true 267 | - false 268 | Default: true 269 | Description: Set to true to deploy ElastiCache session cache. This will not be used at first and will be integrated only after IsMoodleSetupComplete parameter changes to 'Yes'. 270 | Type: String 271 | UseServerlessSessionCache: 272 | AllowedValues: 273 | - true 274 | - false 275 | Default: true 276 | Description: Set to true to deploy ElastiCache session cache using serverless approach. Set to false to use instances instead. 277 | Type: String 278 | SessionCacheNodeType: 279 | AllowedValues: 280 | - cache.t2.micro 281 | - cache.t2.small 282 | - cache.t2.medium 283 | - cache.t3.micro 284 | - cache.t3.small 285 | - cache.t3.medium 286 | - cache.t4g.micro 287 | - cache.t4g.small 288 | - cache.t4g.medium 289 | - cache.m4.large 290 | - cache.m4.xlarge 291 | - cache.m4.2xlarge 292 | - cache.m4.4xlarge 293 | - cache.m4.10xlarge 294 | - cache.m5.large 295 | - cache.m5.xlarge 296 | - cache.m5.2xlarge 297 | - cache.m5.4xlarge 298 | - cache.m5.12xlarge 299 | - cache.m5.24xlarge 300 | - cache.m6g.large 301 | - cache.m6g.xlarge 302 | - cache.m6g.2xlarge 303 | - cache.m6g.4xlarge 304 | - cache.m6g.8xlarge 305 | - cache.m6g.12xlarge 306 | - cache.m6g.16xlarge 307 | - cache.r4.large 308 | - cache.r4.xlarge 309 | - cache.r4.2xlarge 310 | - cache.r4.4xlarge 311 | - cache.r4.8xlarge 312 | - cache.r4.16xlarge 313 | - cache.r5.large 314 | - cache.r5.xlarge 315 | - cache.r5.2xlarge 316 | - cache.r5.4xlarge 317 | - cache.r5.12xlarge 318 | - cache.r5.24xlarge 319 | - cache.r6g.large 320 | - cache.r6g.xlarge 321 | - cache.r6g.2xlarge 322 | - cache.r6g.4xlarge 323 | - cache.r6g.8xlarge 324 | - cache.r6g.12xlarge 325 | - cache.r6g.16xlarge 326 | - cache.r6gd.xlarge 327 | - cache.r6gd.2xlarge 328 | - cache.r6gd.4xlarge 329 | - cache.r6gd.8xlarge 330 | - cache.r6gd.12xlarge 331 | - cache.r6gd.16xlarge 332 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 333 | Default: cache.r6g.large 334 | Description: The Amazon ElastiCache cluster node type. 335 | Type: String 336 | UseApplicationCacheBoolean: 337 | AllowedValues: 338 | - true 339 | - false 340 | Default: false 341 | Description: Specifies whether an ElastiCache Cache Cluster should be created to cache application content. 342 | Type: String 343 | UseServerlessApplicationCache: 344 | AllowedValues: 345 | - true 346 | - false 347 | Default: true 348 | Description: Indicates whether to use serverless ElastiCache for application. 349 | Type: String 350 | ApplicationCacheNodeType: 351 | AllowedValues: 352 | - cache.t2.micro 353 | - cache.t2.small 354 | - cache.t2.medium 355 | - cache.t3.micro 356 | - cache.t3.small 357 | - cache.t3.medium 358 | - cache.t4g.micro 359 | - cache.t4g.small 360 | - cache.t4g.medium 361 | - cache.m4.large 362 | - cache.m4.xlarge 363 | - cache.m4.2xlarge 364 | - cache.m4.4xlarge 365 | - cache.m4.10xlarge 366 | - cache.m5.large 367 | - cache.m5.xlarge 368 | - cache.m5.2xlarge 369 | - cache.m5.4xlarge 370 | - cache.m5.12xlarge 371 | - cache.m5.24xlarge 372 | - cache.m6g.large 373 | - cache.m6g.xlarge 374 | - cache.m6g.2xlarge 375 | - cache.m6g.4xlarge 376 | - cache.m6g.8xlarge 377 | - cache.m6g.12xlarge 378 | - cache.m6g.16xlarge 379 | - cache.r4.large 380 | - cache.r4.xlarge 381 | - cache.r4.2xlarge 382 | - cache.r4.4xlarge 383 | - cache.r4.8xlarge 384 | - cache.r4.16xlarge 385 | - cache.r5.large 386 | - cache.r5.xlarge 387 | - cache.r5.2xlarge 388 | - cache.r5.4xlarge 389 | - cache.r5.12xlarge 390 | - cache.r5.24xlarge 391 | - cache.r6g.large 392 | - cache.r6g.xlarge 393 | - cache.r6g.2xlarge 394 | - cache.r6g.4xlarge 395 | - cache.r6g.8xlarge 396 | - cache.r6g.12xlarge 397 | - cache.r6g.16xlarge 398 | - cache.r6gd.xlarge 399 | - cache.r6gd.2xlarge 400 | - cache.r6gd.4xlarge 401 | - cache.r6gd.8xlarge 402 | - cache.r6gd.12xlarge 403 | - cache.r6gd.16xlarge 404 | ConstraintDescription: Must be a valid Amazon ElastiCache node type. 405 | Default: cache.r6g.large 406 | Description: The Amazon ElastiCache cluster node type. 407 | Type: String 408 | WebAsgMax: 409 | AllowedPattern: ^((?!0$)[1-2]?[0-9]|30)$ 410 | ConstraintDescription: Must be a number between 1 and 30. 411 | Default: 1 412 | Description: Specifies the maximum number of EC2 instances in the Web Autoscaling Group. 413 | Type: String 414 | WebAsgMin: 415 | AllowedPattern: ^([0-0]?[0-9]|10)$ 416 | ConstraintDescription: Must be a number between 0 and 10. 417 | Default: 1 418 | Description: Specifies the minimum number of EC2 instances in the Web Autoscaling Group. 419 | Type: String 420 | WebInstanceType: 421 | AllowedValues: 422 | - t3.nano 423 | - t3.micro 424 | - t3.small 425 | - t3.medium 426 | - t3.large 427 | - t3.xlarge 428 | - t3.2xlarge 429 | - m5.large 430 | - m5.xlarge 431 | - m5.2xlarge 432 | - m5.4xlarge 433 | - m5.8xlarge 434 | - m5.12xlarge 435 | - m5.16xlarge 436 | - m5.24xlarge 437 | - c5.large 438 | - c5.xlarge 439 | - c5.2xlarge 440 | - c5.4xlarge 441 | - c5.9xlarge 442 | - c5.12xlarge 443 | - c5.18xlarge 444 | - c5.24xlarge 445 | - r5.large 446 | - r5.xlarge 447 | - r5.2xlarge 448 | - r5.4xlarge 449 | - r5.8xlarge 450 | - r5.12xlarge 451 | - r5.16xlarge 452 | - r5.24xlarge 453 | - t3a.nano 454 | - t3a.micro 455 | - t3a.small 456 | - t3a.medium 457 | - t3a.large 458 | - t3a.xlarge 459 | - t3a.2xlarge 460 | - m5a.large 461 | - m5a.xlarge 462 | - m5a.2xlarge 463 | - m5a.4xlarge 464 | - m5a.8xlarge 465 | - m5a.12xlarge 466 | - m5a.16xlarge 467 | - m5a.24xlarge 468 | - c5a.large 469 | - c5a.xlarge 470 | - c5a.2xlarge 471 | - c5a.4xlarge 472 | - c5a.9xlarge 473 | - c5a.12xlarge 474 | - c5a.18xlarge 475 | - c5a.24xlarge 476 | - r5a.large 477 | - r5a.xlarge 478 | - r5a.2xlarge 479 | - r5a.4xlarge 480 | - r5a.8xlarge 481 | - r5a.12xlarge 482 | - r5a.16xlarge 483 | - r5a.24xlarge 484 | - t4g.nano 485 | - t4g.micro 486 | - t4g.small 487 | - t4g.medium 488 | - t4g.large 489 | - t4g.xlarge 490 | - t4g.2xlarge 491 | - m6g.large 492 | - m6g.xlarge 493 | - m6g.2xlarge 494 | - m6g.4xlarge 495 | - m6g.8xlarge 496 | - m6g.12xlarge 497 | - m6g.16xlarge 498 | - m6g.24xlarge 499 | - m7g.medium 500 | - m7g.large 501 | - m7g.xlarge 502 | - m7g.2xlarge 503 | - m7g.4xlarge 504 | - m7g.8xlarge 505 | - m7g.12xlarge 506 | - m7g.16xlarge 507 | - c6g.large 508 | - c6g.xlarge 509 | - c6g.2xlarge 510 | - c6g.4xlarge 511 | - c6g.9xlarge 512 | - c6g.12xlarge 513 | - c6g.18xlarge 514 | - c6g.24xlarge 515 | - r6g.large 516 | - r6g.xlarge 517 | - r6g.2xlarge 518 | - r6g.4xlarge 519 | - r6g.8xlarge 520 | - r6g.12xlarge 521 | - r6g.16xlarge 522 | - r6g.24xlarge 523 | - c7g.medium 524 | - c7g.large 525 | - c7g.xlarge 526 | - c7g.2xlarge 527 | - c7g.4xlarge 528 | - c7g.8xlarge 529 | - c7g.12xlarge 530 | - c7g.16xlarge 531 | ConstraintDescription: Must be a valid Amazon EC2 instance type. 532 | Default: c7g.xlarge 533 | Description: The Amazon EC2 instance type for your web instances. 534 | Type: String 535 | HostedZoneName: 536 | 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])$ 537 | Description: '[ Optional ] The Route 53 hosted zone to create the domain in (e.g. example.edu).' 538 | Type: String 539 | DomainName: 540 | 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])$ 541 | Description: '[ Optional ] The main domain name of the Moodle site (e.g. moodle.example.edu).' 542 | Type: String 543 | MoodleLocale: 544 | Description: "The main language of the Moodle site, during initial configuration." 545 | Type: String 546 | Default: en 547 | AvailabilityZones: 548 | Description: 'List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved.' 549 | Type: List 550 | NumberOfAZs: 551 | AllowedValues: 552 | - 1 553 | - 2 554 | - 3 555 | Default: 2 556 | Description: Number of Availability Zones to use in the VPC. This must match your 557 | selections in the list of Availability Zones parameter. 558 | Type: Number 559 | VpcCidr: 560 | 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]))$" 561 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 562 | Default: 10.0.0.0/16 563 | Description: CIDR block for the VPC 564 | Type: String 565 | DataSubnet0Cidr: 566 | 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]))$" 567 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 568 | Default: 10.0.100.0/24 569 | Description: CIDR block for data subnet 0 located in Availability Zone 0 570 | Type: String 571 | DataSubnet1Cidr: 572 | 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]))$" 573 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 574 | Default: 10.0.101.0/24 575 | Description: CIDR block for data subnet 1 located in Availability Zone 1 576 | Type: String 577 | DataSubnet2Cidr: 578 | 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]))$" 579 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 580 | Default: 10.0.102.0/24 581 | Description: CIDR block for data subnet 2 located in Availability Zone 2 582 | Type: String 583 | PublicSubnet0Cidr: 584 | 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]))$" 585 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 586 | Default: 10.0.200.0/24 587 | Description: CIDR block for Public subnet 0 located in Availability Zone 0 588 | Type: String 589 | PublicSubnet1Cidr: 590 | 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]))$" 591 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 592 | Default: 10.0.201.0/24 593 | Description: CIDR block for Public subnet 1 located in Availability Zone 1 594 | Type: String 595 | PublicSubnet2Cidr: 596 | 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]))$" 597 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 598 | Default: 10.0.202.0/24 599 | Description: CIDR block for Public subnet 2 located in Availability Zone 2 600 | Type: String 601 | AppSubnet0Cidr: 602 | 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]))$" 603 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 604 | Default: 10.0.0.0/22 605 | Description: CIDR block for App Subnet 0 located in Availability Zone 0 606 | Type: String 607 | AppSubnet1Cidr: 608 | 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]))$" 609 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 610 | Default: 10.0.4.0/22 611 | Description: CIDR block for App Subnet 1 located in Availability Zone 1 612 | Type: String 613 | AppSubnet2Cidr: 614 | 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]))$" 615 | ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 616 | Default: 10.0.8.0/22 617 | Description: CIDR block for App Subnet 2 located in Availability Zone 2 618 | Type: String 619 | UseCloudFrontBoolean: 620 | AllowedValues: 621 | - true 622 | - false 623 | Default: false 624 | Description: Specifies whether a CloudFront Distribution should be created to serve the Moodle website content. 625 | Type: String 626 | NotifyEmailAddress: 627 | AllowedPattern: ^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+$ 628 | Description: Email address for notification 629 | Type: String 630 | Default: hello@yourdomain.com 631 | MoodleDirectDownloadURL: 632 | Default: "https://download.moodle.org/download.php/direct/stable404/moodle-4.4.tgz" 633 | Description: Specifies the TGZ Moodle direct download URL 634 | Type: String 635 | 636 | Conditions: 637 | DeployWithoutSessionCache: 638 | !Equals [ false, !Ref UseSessionCacheBoolean ] 639 | DeployWithSessionCache: 640 | !Equals [ true, !Ref UseSessionCacheBoolean ] 641 | DeployWithServerlessSessionCache: 642 | !Equals [ true, !Ref UseServerlessSessionCache ] 643 | DeployApplicationCache: 644 | !Equals [ true, !Ref UseApplicationCacheBoolean ] 645 | DeployWithServerlessApplicationCache: 646 | !Equals [ true, !Ref UseServerlessApplicationCache ] 647 | DeployRoute53: 648 | !And [ 649 | !Not [!Equals ['', !Ref DomainName ] ], 650 | !Not [ !Equals ['', !Ref HostedZoneName ] ] 651 | ] 652 | ErrorChoices: 653 | !Or [ 654 | !And [ 655 | !Not [!Equals ['', !Ref DomainName ] ], #with domain name 656 | !Equals [ true, !Ref UseCloudFrontBoolean ], #with cloudfront 657 | !Equals ['', !Ref CloudFrontAcmCertificate ] #no SSL certificate 658 | ], 659 | !And [ 660 | !Not [!Equals ['', !Ref HostedZoneName ] ], #with domain name internal 661 | !Equals [ true, !Ref UseCloudFrontBoolean ], #with cloudfront 662 | !Equals ['', !Ref CloudFrontAcmCertificate ] #no SSL certificate 663 | ] 664 | ] 665 | DeployCloudFront: 666 | !Or [ 667 | !And [ #Domain Name with CloudFront but no SSL certificate will treat as CloudFront domain name only 668 | !Not [!Equals ['', !Ref DomainName ] ], 669 | !Equals [ true, !Ref UseCloudFrontBoolean ], 670 | !Not [ !Equals ['', !Ref CloudFrontAcmCertificate ] ] 671 | ], 672 | !And [ 673 | !Equals ['', !Ref DomainName ], 674 | !Equals [ true, !Ref UseCloudFrontBoolean ] 675 | ] 676 | ] 677 | SharedStorageEFS: 678 | !Equals ['EFS', 'EFS' ] 679 | DeployUsingRDSServerless: 680 | !Equals [ true, !Ref DatabaseUseServerless ] 681 | DeployUsingRDSInstances: 682 | !Equals [ false, !Ref DatabaseUseServerless ] 683 | NeedSSL: 684 | !Or [ 685 | !Equals [ true, !Ref UseCloudFrontBoolean ], #Either CloudFront 686 | !Not [ !Equals ['', !Ref PublicAlbAcmCertificate ] ] #Either ACM certificate at ALB 687 | ] 688 | CustomDomainName: 689 | !Or [ 690 | !And [ #Domain Name with CloudFront but no SSL certificate will treat as CloudFront domain name only 691 | !Not [!Equals ['', !Ref DomainName ] ], 692 | !Equals [ true, !Ref UseCloudFrontBoolean ], 693 | !Not [ !Equals ['', !Ref CloudFrontAcmCertificate ] ] 694 | ], 695 | !And [ #Domain Name with CloudFront but no SSL certificate will treat as CloudFront domain name only 696 | !Not [!Equals ['', !Ref DomainName ] ], 697 | !Equals [ false, !Ref UseCloudFrontBoolean ] 698 | ] 699 | ] 700 | CloudFrontDnsName: 701 | !And [ 702 | !Equals ['', !Ref DomainName ], 703 | !Equals [ true, !Ref UseCloudFrontBoolean ] ] 704 | AlbDnsName: 705 | !And [ 706 | !Equals ['', !Ref DomainName ], 707 | !Equals [ false, !Ref UseCloudFrontBoolean ] ] 708 | 709 | 710 | Resources: 711 | #Made it to fail the CloudformationDeployment due to Error choices. 712 | SomeErrorInCloudFormationParamCombinationCheckErrorChoices: 713 | Condition: ErrorChoices 714 | Type: AWS::SSM::Parameter 715 | Properties: 716 | Name: '@#$' 717 | Type: String 718 | Value: '' 719 | 720 | rdsInstanceSecret: 721 | Type: AWS::SecretsManager::Secret 722 | Properties: 723 | Description: 'Credentials for Moodle RDS instance' 724 | GenerateSecretString: 725 | SecretStringTemplate: !Sub '{"username": "${DatabaseMasterUsername}"}' 726 | GenerateStringKey: 'password' 727 | PasswordLength: 16 728 | ExcludeCharacters: '"@/\' 729 | 730 | IsMoodleSetupCompletedParam: 731 | Type: AWS::SSM::Parameter 732 | Properties: 733 | Name: !Join [ '', [ '/Moodle/',!Sub '${AWS::StackName}', '/IsMoodleSetupCompleted' ] ] 734 | Type: String 735 | Value: 'No' 736 | Description: SSM Parameter for Moodle Setup completed or not. Default 'No' any other value will be considered as 'Yes' 737 | vpc: 738 | Type: AWS::CloudFormation::Stack 739 | Properties: 740 | Parameters: 741 | NumberOfAZs: 742 | !Ref NumberOfAZs 743 | AvailabilityZones: 744 | !Join 745 | - ',' 746 | - !Ref AvailabilityZones 747 | VpcCidr: 748 | !Ref VpcCidr 749 | PublicSubnet0Cidr: 750 | !Ref PublicSubnet0Cidr 751 | PublicSubnet1Cidr: 752 | !Ref PublicSubnet1Cidr 753 | PublicSubnet2Cidr: 754 | !Ref PublicSubnet2Cidr 755 | AppSubnet0Cidr: 756 | !Ref AppSubnet0Cidr 757 | AppSubnet1Cidr: 758 | !Ref AppSubnet1Cidr 759 | AppSubnet2Cidr: 760 | !Ref AppSubnet2Cidr 761 | DataSubnet0Cidr: 762 | !Ref DataSubnet0Cidr 763 | DataSubnet1Cidr: 764 | !Ref DataSubnet1Cidr 765 | DataSubnet2Cidr: 766 | !Ref DataSubnet2Cidr 767 | TemplateURL: !Sub '${DeploymentLocation}/01-newvpc.yaml' 768 | 769 | securitygroups: 770 | DependsOn: vpc 771 | Type: AWS::CloudFormation::Stack 772 | Properties: 773 | Parameters: 774 | Vpc: 775 | !GetAtt [ vpc, Outputs.Vpc ] 776 | DatabaseType: 777 | !Ref DatabaseType 778 | ElastiCacheType: 779 | !Ref CacheEngineType 780 | TemplateURL: !Sub '${DeploymentLocation}/02-securitygroups.yaml' 781 | 782 | publicalb: 783 | DependsOn: securitygroups 784 | Type: AWS::CloudFormation::Stack 785 | Properties: 786 | Parameters: 787 | NumberOfSubnets: 788 | !Ref NumberOfAZs 789 | Subnet: 790 | !GetAtt [ vpc, Outputs.PublicSubnet ] 791 | PublicAlbAcmCertificate: 792 | !Ref PublicAlbAcmCertificate 793 | PublicAlbSecurityGroup: 794 | !GetAtt [ securitygroups, Outputs.PublicAlbSecurityGroup ] 795 | Vpc: 796 | !GetAtt [ vpc, Outputs.Vpc ] 797 | ProjectName: 798 | !Sub '${AWS::StackName}' 799 | TemplateURL: !Sub '${DeploymentLocation}/03-publicalb.yaml' 800 | 801 | rds: 802 | DependsOn: [ securitygroups ] 803 | Type: AWS::CloudFormation::Stack 804 | Condition: DeployUsingRDSInstances 805 | Properties: 806 | Parameters: 807 | DatabaseType: 808 | !Ref DatabaseType 809 | DatabaseInstanceType: 810 | !Ref DatabaseInstanceType 811 | # DatabaseMasterUsername: 812 | # !Ref DatabaseMasterUsername 813 | RDSInstanceSecretArn: 814 | !Ref rdsInstanceSecret 815 | DatabaseName: 816 | !Ref DatabaseName 817 | DatabaseCmk: 818 | !Ref DatabaseCmk 819 | DatabaseSecurityGroup: 820 | !GetAtt [ securitygroups, Outputs.DatabaseSecurityGroup ] 821 | Subnet: 822 | !GetAtt [ vpc, Outputs.DataSubnet ] 823 | NumberOfSubnets: 824 | !Ref NumberOfAZs 825 | ProjectName: 826 | !Sub '${AWS::StackName}' 827 | TemplateURL: !Sub '${DeploymentLocation}/03-rds.yaml' 828 | 829 | rdsserverless: 830 | DependsOn: [ securitygroups ] 831 | Type: AWS::CloudFormation::Stack 832 | Condition: DeployUsingRDSServerless 833 | Properties: 834 | Parameters: 835 | DatabaseType: 836 | !Ref DatabaseType 837 | RDSInstanceSecretArn: 838 | !Ref rdsInstanceSecret 839 | DatabaseName: 840 | !Ref DatabaseName 841 | DatabaseMinCapacity: 842 | !Ref DatabaseMinCapacity 843 | DatabaseMaxCapacity: 844 | !Ref DatabaseMaxCapacity 845 | DatabaseCmk: 846 | !Ref DatabaseCmk 847 | DatabaseSecurityGroup: 848 | !GetAtt [ securitygroups, Outputs.DatabaseSecurityGroup ] 849 | Subnet: 850 | !GetAtt [ vpc, Outputs.DataSubnet ] 851 | NumberOfSubnets: 852 | !Ref NumberOfAZs 853 | ProjectName: 854 | !Sub '${AWS::StackName}' 855 | TemplateURL: !Sub '${DeploymentLocation}/03-rdsserverless.yaml' 856 | 857 | sharedEFS: 858 | Condition: SharedStorageEFS 859 | DependsOn: securitygroups 860 | Type: AWS::CloudFormation::Stack 861 | Properties: 862 | Parameters: 863 | Cmk: 864 | !Ref SharedStorageEncryptionCmk 865 | SecurityGroup: 866 | !GetAtt [ securitygroups, Outputs.EfsSecurityGroup ] 867 | NumberOfSubnets: 868 | !Ref NumberOfAZs 869 | Subnet: 870 | !GetAtt [ vpc, Outputs.DataSubnet ] 871 | ProjectName: 872 | !Sub '${AWS::StackName}' 873 | TemplateURL: !Sub '${DeploymentLocation}/03-efsfilesystem.yaml' 874 | 875 | pipelineHelper: 876 | DependsOn: securitygroups 877 | Type: AWS::CloudFormation::Stack 878 | Properties: 879 | Parameters: 880 | PipelineSecurityGroup: 881 | !GetAtt [ securitygroups, Outputs.WebSecurityGroup ] 882 | NumberOfSubnets: 883 | !Ref NumberOfAZs 884 | PipelineSubnet: 885 | !GetAtt [ vpc, Outputs.AppSubnet ] 886 | RDSInstanceSecretArn: 887 | !Ref rdsInstanceSecret 888 | DomainName: 889 | !Ref DomainName 890 | MoodleLocale: 891 | !Ref MoodleLocale 892 | ProjectName: 893 | !Sub '${AWS::StackName}' 894 | WebAsgMax: 895 | !Ref WebAsgMax 896 | WebAsgMin: 897 | !Ref WebAsgMin 898 | MoodleDirectDownloadURL: 899 | !Ref MoodleDirectDownloadURL 900 | TemplateURL: !Sub '${DeploymentLocation}/03-pipelinehelper.yaml' 901 | 902 | sessioncache: 903 | Condition: DeployWithSessionCache 904 | DependsOn: securitygroups 905 | Type: AWS::CloudFormation::Stack 906 | Properties: 907 | Parameters: 908 | CacheEngineType: !Ref CacheEngineType 909 | CacheUsageType: 'session' 910 | Subnet: !GetAtt [ vpc, Outputs.DataSubnet ] 911 | ElastiCacheClusterName: !Sub '${AWS::StackName}session' 912 | ElastiCacheNodeType: !Ref SessionCacheNodeType 913 | ElastiCacheSecurityGroup: !GetAtt [ securitygroups, Outputs.ElastiCacheSecurityGroup ] 914 | NumberOfSubnets: !Ref NumberOfAZs 915 | ProjectName: !Sub '${AWS::StackName}' 916 | TemplateURL: !If [ DeployWithServerlessSessionCache, !Sub '${DeploymentLocation}/03-elasticacheserverless.yaml', !Sub '${DeploymentLocation}/03-elasticache.yaml' ] 917 | 918 | applicationcache: 919 | Condition: DeployApplicationCache 920 | DependsOn: securitygroups 921 | Type: AWS::CloudFormation::Stack 922 | Properties: 923 | Parameters: 924 | CacheEngineType: !Ref CacheEngineType 925 | CacheUsageType: 'application' 926 | Subnet: !GetAtt [ vpc, Outputs.DataSubnet ] 927 | ElastiCacheClusterName: !Sub '${AWS::StackName}application' 928 | ElastiCacheNodeType: !Ref ApplicationCacheNodeType 929 | ElastiCacheSecurityGroup: !GetAtt [ securitygroups, Outputs.ElastiCacheSecurityGroup ] 930 | NumberOfSubnets: !Ref NumberOfAZs 931 | ProjectName: !Sub '${AWS::StackName}' 932 | TemplateURL: !If [ DeployWithServerlessApplicationCache, !Sub '${DeploymentLocation}/03-elasticacheserverless.yaml', !Sub '${DeploymentLocation}/03-elasticache.yaml' ] 933 | 934 | webapp: 935 | DependsOn: [ publicalb, pipelineHelper ] 936 | Type: AWS::CloudFormation::Stack 937 | Properties: 938 | Parameters: 939 | RDSInstanceSecretArn: 940 | !Ref rdsInstanceSecret 941 | NumberOfSubnets: 942 | !Ref NumberOfAZs 943 | Subnet: 944 | !GetAtt [ vpc, Outputs.AppSubnet ] 945 | PublicAlbTargetGroupArn: 946 | !GetAtt [ publicalb, Outputs.PublicAlbTargetGroupArn ] 947 | WebAsgMax: 1 948 | WebAsgMin: 1 949 | WebInstanceType: 950 | !Ref WebInstanceType 951 | WebSecurityGroup: 952 | !GetAtt [ securitygroups, Outputs.WebSecurityGroup ] 953 | CodeArtifactS3BucketArn: !GetAtt [ pipelineHelper, Outputs.CodeArtifactS3BucketArn] 954 | ProjectName: 955 | !Sub '${AWS::StackName}' 956 | TemplateURL: !Sub '${DeploymentLocation}/04-web.yaml' 957 | 958 | codePipeline: 959 | DependsOn: [webapp, pipelineHelper, rds] 960 | Condition: DeployUsingRDSInstances 961 | Type: AWS::CloudFormation::Stack 962 | Properties: 963 | Parameters: 964 | GitS3RemoteRepoName: 965 | !GetAtt [ pipelineHelper, Outputs.MoodleRepoBucketName ] 966 | GitS3RemoteRepoBucketArn: 967 | !GetAtt [ pipelineHelper, Outputs.MoodleRepoBucketArn ] 968 | BranchName: 'main' 969 | AppAutoScalingGroupName: 970 | !GetAtt [ webapp, Outputs.WebAutoScalingGroupName ] 971 | MoodleAppTargetGroupName: 972 | !GetAtt [ publicalb, Outputs.PublicAlbTargetGroupName ] 973 | CodeArtifactS3BucketName: 974 | !GetAtt [ pipelineHelper, Outputs.CodeArtifactS3BucketName ] 975 | CodeArtifactS3BucketArn: 976 | !GetAtt [ pipelineHelper, Outputs.CodeArtifactS3BucketArn ] 977 | ProjectName: 978 | !Sub '${AWS::StackName}' 979 | TemplateURL: !Sub '${DeploymentLocation}/05-codepipeline.yaml' 980 | 981 | codePipelineServerless: 982 | DependsOn: [webapp, pipelineHelper, rdsserverless] 983 | Condition: DeployUsingRDSServerless 984 | Type: AWS::CloudFormation::Stack 985 | Properties: 986 | Parameters: 987 | GitS3RemoteRepoName: 988 | !GetAtt [ pipelineHelper, Outputs.MoodleRepoBucketName ] 989 | GitS3RemoteRepoBucketArn: 990 | !GetAtt [ pipelineHelper, Outputs.MoodleRepoBucketArn ] 991 | BranchName: 'main' 992 | AppAutoScalingGroupName: 993 | !GetAtt [ webapp, Outputs.WebAutoScalingGroupName ] 994 | MoodleAppTargetGroupName: 995 | !GetAtt [ publicalb, Outputs.PublicAlbTargetGroupName ] 996 | CodeArtifactS3BucketName: 997 | !GetAtt [ pipelineHelper, Outputs.CodeArtifactS3BucketName ] 998 | CodeArtifactS3BucketArn: 999 | !GetAtt [ pipelineHelper, Outputs.CodeArtifactS3BucketArn ] 1000 | ProjectName: 1001 | !Sub '${AWS::StackName}' 1002 | TemplateURL: !Sub '${DeploymentLocation}/05-codepipeline.yaml' 1003 | 1004 | cloudfront: 1005 | Condition: DeployCloudFront 1006 | DependsOn: publicalb 1007 | Type: AWS::CloudFormation::Stack 1008 | Properties: 1009 | Parameters: 1010 | CloudFrontAcmCertificate: 1011 | !Ref CloudFrontAcmCertificate 1012 | PublicAlbDnsName: 1013 | !GetAtt [ publicalb, Outputs.PublicAlbDnsName ] 1014 | DomainName: 1015 | !Ref DomainName 1016 | TemplateURL: !Sub '${DeploymentLocation}/04-cloudfront.yaml' 1017 | 1018 | CustomDnsParam: 1019 | Condition: CustomDomainName 1020 | Type: AWS::SSM::Parameter 1021 | Properties: 1022 | Name: !Join [ '', [ '/Moodle/',!Sub '${AWS::StackName}', '/Network/DomainName' ] ] 1023 | Type: String 1024 | Value: !Join [ '', [ !If [ NeedSSL, 'https://','http://'] ,!Ref DomainName ] ] 1025 | Description: SSM Parameter for Moodle Public DNS URL 1026 | CloudFrontDnsParam: 1027 | DependsOn: cloudfront 1028 | Condition: CloudFrontDnsName 1029 | Type: AWS::SSM::Parameter 1030 | Properties: 1031 | Name: !Join [ '', [ '/Moodle/',!Sub '${AWS::StackName}', '/Network/DomainName' ] ] 1032 | Type: String 1033 | Value: !Join [ '', ['https://',!GetAtt [ cloudfront, Outputs.DnsName ] ] ] 1034 | Description: SSM Parameter for Moodle Public DNS URL 1035 | AlbDnsParam: 1036 | DependsOn: publicalb 1037 | Condition: AlbDnsName 1038 | Type: AWS::SSM::Parameter 1039 | Properties: 1040 | Name: !Join [ '', [ '/Moodle/',!Sub '${AWS::StackName}', '/Network/DomainName' ] ] 1041 | Type: String 1042 | Value: !Join [ '', [ !If [ NeedSSL, 'https://','http://'] ,!GetAtt [ publicalb, Outputs.PublicAlbDnsName ] ] ] 1043 | Description: SSM Parameter for Moodle Public DNS URL 1044 | 1045 | route53: 1046 | Condition: DeployRoute53 1047 | DependsOn: publicalb 1048 | Type: AWS::CloudFormation::Stack 1049 | Properties: 1050 | Parameters: 1051 | DnsEndpoint: 1052 | !If [ DeployCloudFront, !GetAtt [ cloudfront, Outputs.DnsName ], !GetAtt [ publicalb, Outputs.PublicAlbDnsName ] ] 1053 | DnsHostId: 1054 | !If [ DeployCloudFront, 'Z2FDTNDATAQYW2', !GetAtt [ publicalb, Outputs.PublicAlbCanonicalHostedZoneId ] ] 1055 | HostedZoneName: 1056 | !Ref HostedZoneName 1057 | DomainName: 1058 | !Ref DomainName 1059 | TemplateURL: !Sub '${DeploymentLocation}/05-route53.yaml' 1060 | 1061 | Outputs: 1062 | MoodleDomainName: 1063 | Description: Moodle URL 1064 | Value: 1065 | !If [ CustomDomainName, 1066 | !Join [ '', [ !If [ NeedSSL, 'https://','http://'] , !Ref DomainName ] ], 1067 | !If [ DeployCloudFront, 1068 | !Join [ '', [ !If [ NeedSSL, 'https://','http://'] , !GetAtt [ cloudfront, Outputs.DnsName ] ] ], 1069 | !Join [ '', [ !If [ NeedSSL, 'https://','http://'] , !GetAtt [ publicalb, Outputs.PublicAlbDnsName ] ] ] 1070 | ] 1071 | ] 1072 | 1073 | DnsName: 1074 | Description: AWS-generated Moodle DNS Name (use this value to configure your custom DNS CNAME, if needed) 1075 | Value: 1076 | !If [ DeployCloudFront, 1077 | !GetAtt [ cloudfront, Outputs.DnsName ], 1078 | !GetAtt [ publicalb, Outputs.PublicAlbDnsName ] ] 1079 | 1080 | IsMoodleSetupCompleted: 1081 | Description: Once Moodle installation is completed, change this parameter value to 'Yes' and run pipeline 1082 | Value: !Join [ '', [ 'https://',!Ref "AWS::Region", '.console.aws.amazon.com/systems-manager/parameters', 1083 | !Ref IsMoodleSetupCompletedParam, '/description?region=',!Ref "AWS::Region" ] ] 1084 | 1085 | MoodleGitRepo: 1086 | Description: git-remote-s3 git repository having Moodle source code 1087 | Value: 1088 | !Join [ '', [ 's3+zip://' , !GetAtt [ pipelineHelper, Outputs.MoodleRepoBucketName ] , '/' , !Ref AWS::StackName ] ] 1089 | 1090 | MoodleCodePipeline: 1091 | Description: Moodle Code pipeline allows you to deploy your changes 1092 | Value: 1093 | !Join [ '', [ 'https://',!Ref "AWS::Region", '.console.aws.amazon.com/codesuite/codepipeline/pipelines/', 1094 | !Ref AWS::StackName, '-Pipeline/view?region=',!Ref "AWS::Region" ] ] 1095 | 1096 | ApplicationCacheServerEndpoint: 1097 | Description: Application Cache server endpoint, use this to configure application cache endpoint in Moodle 1098 | Value: !If [DeployApplicationCache, !GetAtt applicationcache.Outputs.ElastiCacheClusterEndpointAddress, 'Not enabled'] 1099 | --------------------------------------------------------------------------------