├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── LambdaWordpress ├── bin │ ├── lambda_wordpress.d.ts │ ├── lambda_wordpress.js │ └── lambda_wordpress.ts ├── cdk.json ├── jest.config.js ├── lib │ ├── lambda_wordpress-stack.d.ts │ ├── lambda_wordpress-stack.js │ ├── lambda_wordpress-stack.ts │ └── phpLambdaFunc │ │ ├── Makefile │ │ ├── handler.php │ │ └── php.ini ├── package-lock.json ├── package.json ├── test │ ├── lambda_wordpress.test.d.ts │ ├── lambda_wordpress.test.js │ └── lambda_wordpress.test.ts └── tsconfig.json └── README.md /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /LambdaWordpress/bin/lambda_wordpress.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | -------------------------------------------------------------------------------- /LambdaWordpress/bin/lambda_wordpress.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | require("source-map-support/register"); 5 | const cdk = require("@aws-cdk/core"); 6 | const lambda_wordpress_stack_1 = require("../lib/lambda_wordpress-stack"); 7 | const app = new cdk.App(); 8 | new lambda_wordpress_stack_1.LambdaWordpressStack(app, 'LambdaWordpressStack'); 9 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFtYmRhX3dvcmRwcmVzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxhbWJkYV93b3JkcHJlc3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsdUNBQXFDO0FBQ3JDLHFDQUFxQztBQUNyQywwRUFBcUU7QUFFckUsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7QUFDMUIsSUFBSSw2Q0FBb0IsQ0FBQyxHQUFHLEVBQUUsc0JBQXNCLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIiMhL3Vzci9iaW4vZW52IG5vZGVcbmltcG9ydCAnc291cmNlLW1hcC1zdXBwb3J0L3JlZ2lzdGVyJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdAYXdzLWNkay9jb3JlJztcbmltcG9ydCB7IExhbWJkYVdvcmRwcmVzc1N0YWNrIH0gZnJvbSAnLi4vbGliL2xhbWJkYV93b3JkcHJlc3Mtc3RhY2snO1xuXG5jb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xubmV3IExhbWJkYVdvcmRwcmVzc1N0YWNrKGFwcCwgJ0xhbWJkYVdvcmRwcmVzc1N0YWNrJyk7XG4iXX0= -------------------------------------------------------------------------------- /LambdaWordpress/bin/lambda_wordpress.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from '@aws-cdk/core'; 4 | import { LambdaWordpressStack } from '../lib/lambda_wordpress-stack'; 5 | 6 | const app = new cdk.App(); 7 | new LambdaWordpressStack(app, 'LambdaWordpressStack'); 8 | -------------------------------------------------------------------------------- /LambdaWordpress/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node bin/lambda_wordpress.ts", 3 | "context": { 4 | "@aws-cdk/core:enableStackNameDuplicates": "true", 5 | "aws-cdk:enableDiffNoFail": "true", 6 | "domainName": "forhead.online", 7 | "keyName": "virginia", 8 | "dbPassword":"dbPassword0!" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LambdaWordpress/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /LambdaWordpress/lib/lambda_wordpress-stack.d.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | export declare class LambdaWordpressStack extends cdk.Stack { 3 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps); 4 | } 5 | -------------------------------------------------------------------------------- /LambdaWordpress/lib/lambda_wordpress-stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.LambdaWordpressStack = void 0; 4 | const cdk = require("@aws-cdk/core"); 5 | const ec2 = require("@aws-cdk/aws-ec2"); 6 | const efs = require("@aws-cdk/aws-efs"); 7 | const lambda = require("@aws-cdk/aws-lambda"); 8 | const rds = require("@aws-cdk/aws-rds"); 9 | const elbv2 = require("@aws-cdk/aws-elasticloadbalancingv2"); 10 | const targets = require("@aws-cdk/aws-elasticloadbalancingv2-targets"); 11 | const cm = require("@aws-cdk/aws-certificatemanager"); 12 | const aws_elasticloadbalancingv2_1 = require("@aws-cdk/aws-elasticloadbalancingv2"); 13 | const path = require("path"); 14 | class LambdaWordpressStack extends cdk.Stack { 15 | constructor(scope, id, props) { 16 | super(scope, id, props); 17 | var DB_HOST = null; 18 | var HTTP_HOST = null; 19 | const DB_NAME = 'wordpress'; 20 | const DB_USER = 'wordpressuser'; 21 | const BASE_PATH = '/mnt/efs'; 22 | const ACCESSPOINT_PATH = '/wordpress'; 23 | const WORDPRESS_PATH = '/mnt/efs'; 24 | const KEY_NAME = this.node.tryGetContext('keyName'); 25 | const DOMAIN_NAME = this.node.tryGetContext('domainName'); 26 | const DB_PASSWORD = this.node.tryGetContext('dbPassword'); 27 | //set the certificate 28 | const myCertificate = new cm.Certificate(this, 'myCertificate', { 29 | domainName: DOMAIN_NAME, 30 | validation: cm.CertificateValidation.fromDns(), 31 | }); 32 | //create VPC 33 | const serverlessVPC = new ec2.Vpc(this, 'serverlessWordpressVPC', { 34 | cidr: '10.0.0.0/16', 35 | subnetConfiguration: [ 36 | { 37 | subnetType: ec2.SubnetType.PUBLIC, 38 | name: 'public', 39 | cidrMask: 24, 40 | }, 41 | { 42 | cidrMask: 24, 43 | name: 'private', 44 | subnetType: ec2.SubnetType.PRIVATE, 45 | }, 46 | ], 47 | }); 48 | /** 49 | * create security group in VPC 50 | */ 51 | // NFS security group which used for ec2 to copy file 52 | const sgNFSSG = new ec2.SecurityGroup(this, 'NFSAllowAllSG', { 53 | vpc: serverlessVPC, 54 | description: 'allow 2049 inbound for ec2', 55 | allowAllOutbound: true, 56 | }); 57 | sgNFSSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(2049), 'allow 2049 inbound from ec2'); 58 | //ALB security group which allow 80 and 443 59 | const albSG = new ec2.SecurityGroup(this, 'albSG', { 60 | vpc: serverlessVPC, 61 | description: 'allow 80 and 443', 62 | allowAllOutbound: true, 63 | }); 64 | albSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'allow 80 inbound'); 65 | albSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'allow 443 inbound'); 66 | //EC2 security group which allow port 22 67 | const ec2SG = new ec2.SecurityGroup(this, 'ec2SG', { 68 | vpc: serverlessVPC, 69 | description: 'allow 22 inbound for ec2', 70 | allowAllOutbound: true, 71 | }); 72 | ec2SG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'allow 22 inbound from ec2'); 73 | // RDS security group which allow port 3306 74 | const rdsSG = new ec2.SecurityGroup(this, 'wordpressRdsSecurityGroup', { 75 | vpc: serverlessVPC, 76 | description: 'allow 3306 inbound', 77 | allowAllOutbound: true, 78 | }); 79 | rdsSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(3306), 'allow 3306 inbound from lambda'); 80 | /** 81 | * create EFS attached on Lambda 82 | */ 83 | const fileSystem = new efs.FileSystem(this, 'wordpressEFS', { 84 | vpc: serverlessVPC, 85 | encrypted: false, 86 | performanceMode: efs.PerformanceMode.GENERAL_PURPOSE, 87 | throughputMode: efs.ThroughputMode.BURSTING, 88 | securityGroup: sgNFSSG, 89 | removalPolicy: cdk.RemovalPolicy.DESTROY, 90 | }); 91 | //create access point on efs 92 | const accessPoint = fileSystem.addAccessPoint('LambdaAccessPoint', { 93 | path: ACCESSPOINT_PATH, 94 | createAcl: { 95 | ownerUid: '1000', 96 | ownerGid: '1000', 97 | permissions: '0777', 98 | }, 99 | posixUser: { 100 | uid: '1000', 101 | gid: '1000', 102 | }, 103 | }); 104 | /** 105 | * Create lambda function 106 | */ 107 | const lambdaFunc = new lambda.Function(this, 'wordpressLambdaFUnction', { 108 | code: lambda.Code.fromAsset(path.join(__dirname, 'phpLambdaFunc')), 109 | handler: 'handler.php', 110 | memorySize: 1024, 111 | timeout: cdk.Duration.minutes(15), 112 | tracing: lambda.Tracing.ACTIVE, 113 | runtime: lambda.Runtime.PROVIDED, 114 | layers: [lambda.LayerVersion.fromLayerVersionArn(this, 'customPhpLayer', 'arn:aws:lambda:us-east-1:887080169480:layer:php73:3')], 115 | vpc: serverlessVPC, 116 | filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, BASE_PATH), 117 | }); 118 | /* 119 | * create alb and integrate it with lambda 120 | */ 121 | const lb = new elbv2.ApplicationLoadBalancer(this, 'serverlessALB', { 122 | vpc: serverlessVPC, 123 | internetFacing: true, 124 | securityGroup: albSG, 125 | }); 126 | const lambdaTarget = new targets.LambdaTarget(lambdaFunc); 127 | const albTargetGroup = new elbv2.ApplicationTargetGroup(this, 'albTargetGroup', { 128 | targets: [lambdaTarget], 129 | }); 130 | albTargetGroup.setAttribute('lambda.multi_value_headers.enabled', 'true'); 131 | const listener80 = lb.addListener('Listener80', { 132 | port: 80, 133 | open: true, 134 | }); 135 | listener80.addAction('80action', { 136 | action: aws_elasticloadbalancingv2_1.ListenerAction.forward([albTargetGroup]) 137 | }); 138 | const listener443 = lb.addListener('Listener443', { 139 | port: 443, 140 | open: true, 141 | certificateArns: [myCertificate.certificateArn], 142 | }); 143 | listener443.addAction('443action', { 144 | action: aws_elasticloadbalancingv2_1.ListenerAction.forward([albTargetGroup]) 145 | }); 146 | /** 147 | * create RDS 148 | */ 149 | const secret = cdk.SecretValue.plainText(DB_PASSWORD); 150 | const auroraServerlessCluster = new rds.DatabaseCluster(this, 'ServerlessWordpressAuroraCluster', { 151 | engine: rds.DatabaseClusterEngine.AURORA_MYSQL, 152 | credentials: rds.Credentials.fromPassword(DB_USER, secret), 153 | removalPolicy: cdk.RemovalPolicy.DESTROY, 154 | instanceProps: { 155 | vpc: serverlessVPC, 156 | securityGroups: [rdsSG], 157 | }, 158 | defaultDatabaseName: DB_NAME, 159 | }); 160 | /*** 161 | * set the DB_HOST and HTTP_HOST which will used in the lambda environment 162 | */ 163 | DB_HOST = auroraServerlessCluster.clusterEndpoint.hostname; 164 | HTTP_HOST = lb.loadBalancerDnsName; 165 | //SET lambda enviromnent 166 | lambdaFunc.addEnvironment('DB_HOST', DB_HOST); 167 | lambdaFunc.addEnvironment('DB_NAME', DB_NAME); 168 | lambdaFunc.addEnvironment('DB_USER', DB_USER); 169 | lambdaFunc.addEnvironment('DB_PASSWORD', DB_PASSWORD); 170 | lambdaFunc.addEnvironment('WORDPRESS_PATH', WORDPRESS_PATH); 171 | lambdaFunc.addEnvironment('HTTP_HOST', HTTP_HOST); 172 | // create EC2 which used to install wordpress files to EFS 173 | const amznLinux = ec2.MachineImage.latestAmazonLinux({ 174 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX, 175 | edition: ec2.AmazonLinuxEdition.STANDARD, 176 | virtualization: ec2.AmazonLinuxVirt.HVM, 177 | storage: ec2.AmazonLinuxStorage.GENERAL_PURPOSE, 178 | }); 179 | const ec2EFS = new ec2.Instance(this, 'efsInstance', { 180 | vpc: serverlessVPC, 181 | vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC }, 182 | machineImage: amznLinux, 183 | instanceType: new ec2.InstanceType('t2.large'), 184 | securityGroup: ec2SG, 185 | keyName: KEY_NAME, 186 | }); 187 | ec2EFS.userData.addCommands( 188 | //install efs tool and create mount point 189 | 'sudo yum install -y amazon-efs-utils', 'sudo mkdir /mnt', 'sudo mkdir /mnt/efs'); 190 | new cdk.CfnOutput(this, 'outputEFS', { 191 | description: 'efs id', 192 | value: 'efs id: ' + fileSystem.fileSystemId, 193 | }); 194 | new cdk.CfnOutput(this, 'outputALBDNS', { 195 | description: 'alb dns name', 196 | value: 'alb dns name: ' + lb.loadBalancerDnsName, 197 | }); 198 | } 199 | } 200 | exports.LambdaWordpressStack = LambdaWordpressStack; 201 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFtYmRhX3dvcmRwcmVzcy1zdGFjay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImxhbWJkYV93b3JkcHJlc3Mtc3RhY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEscUNBQXFDO0FBQ3JDLHdDQUF3QztBQUN4Qyx3Q0FBd0M7QUFDeEMsOENBQThDO0FBQzlDLHdDQUF3QztBQUN4Qyw2REFBNkQ7QUFDN0QsdUVBQXVFO0FBQ3ZFLHNEQUFzRDtBQUN0RCxvRkFBK0c7QUFDL0csNkJBQTZCO0FBQzdCLE1BQWEsb0JBQXFCLFNBQVEsR0FBRyxDQUFDLEtBQUs7SUFDakQsWUFBWSxLQUFvQixFQUFFLEVBQVUsRUFBRSxLQUFzQjtRQUNsRSxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUV4QixJQUFJLE9BQU8sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxTQUFTLEdBQUcsSUFBSSxDQUFDO1FBQ3JCLE1BQU0sT0FBTyxHQUFHLFdBQVcsQ0FBQztRQUM1QixNQUFNLE9BQU8sR0FBRyxlQUFlLENBQUM7UUFDaEMsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDO1FBQzdCLE1BQU0sZ0JBQWdCLEdBQUcsWUFBWSxDQUFDO1FBQ3RDLE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQztRQUNsQyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNwRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUMxRCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUUxRCxxQkFBcUI7UUFDckIsTUFBTSxhQUFhLEdBQUcsSUFBSSxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksRUFBRSxlQUFlLEVBQUM7WUFDN0QsVUFBVSxFQUFFLFdB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dCAqIGFzIGVjMiBmcm9tICdAYXdzLWNkay9hd3MtZWMyJztcbmltcG9ydCAqIGFzIGVmcyBmcm9tICdAYXdzLWNkay9hd3MtZWZzJztcbmltcG9ydCAqIGFzIGxhbWJkYSBmcm9tICdAYXdzLWNkay9hd3MtbGFtYmRhJztcbmltcG9ydCAqIGFzIHJkcyBmcm9tICdAYXdzLWNkay9hd3MtcmRzJztcbmltcG9ydCAqIGFzIGVsYnYyIGZyb20gJ0Bhd3MtY2RrL2F3cy1lbGFzdGljbG9hZGJhbGFuY2luZ3YyJztcbmltcG9ydCAqIGFzIHRhcmdldHMgZnJvbSAnQGF3cy1jZGsvYXdzLWVsYXN0aWNsb2FkYmFsYW5jaW5ndjItdGFyZ2V0cyc7XG5pbXBvcnQgKiBhcyBjbSBmcm9tICdAYXdzLWNkay9hd3MtY2VydGlmaWNhdGVtYW5hZ2VyJztcbmltcG9ydCB7QXBwbGljYXRpb25UYXJnZXRHcm91cCwgQXBwbGljYXRpb25Qcm90b2NvbCwgTGlzdGVuZXJBY3Rpb259IGZyb20gJ0Bhd3MtY2RrL2F3cy1lbGFzdGljbG9hZGJhbGFuY2luZ3YyJ1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmV4cG9ydCBjbGFzcyBMYW1iZGFXb3JkcHJlc3NTdGFjayBleHRlbmRzIGNkay5TdGFjayB7XG4gIGNvbnN0cnVjdG9yKHNjb3BlOiBjZGsuQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IGNkay5TdGFja1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICB2YXIgREJfSE9TVCA9IG51bGw7XG4gICAgdmFyIEhUVFBfSE9TVCA9IG51bGw7XG4gICAgY29uc3QgREJfTkFNRSA9ICd3b3JkcHJlc3MnO1xuICAgIGNvbnN0IERCX1VTRVIgPSAnd29yZHByZXNzdXNlcic7XG4gICAgY29uc3QgQkFTRV9QQVRIID0gJy9tbnQvZWZzJztcbiAgICBjb25zdCBBQ0NFU1NQT0lOVF9QQVRIID0gJy93b3JkcHJlc3MnO1xuICAgIGNvbnN0IFdPUkRQUkVTU19QQVRIID0gJy9tbnQvZWZzJztcbiAgICBjb25zdCBLRVlfTkFNRSA9IHRoaXMubm9kZS50cnlHZXRDb250ZXh0KCdrZXlOYW1lJyk7XG4gICAgY29uc3QgRE9NQUlOX05BTUUgPSB0aGlzLm5vZGUudHJ5R2V0Q29udGV4dCgnZG9tYWluTmFtZScpO1xuICAgIGNvbnN0IERCX1BBU1NXT1JEID0gdGhpcy5ub2RlLnRyeUdldENvbnRleHQoJ2RiUGFzc3dvcmQnKTtcblxuICAgIC8vc2V0IHRoZSBjZXJ0aWZpY2F0ZVxuICAgIGNvbnN0IG15Q2VydGlmaWNhdGUgPSBuZXcgY20uQ2VydGlmaWNhdGUodGhpcywgJ215Q2VydGlmaWNhdGUnLHtcbiAgICAgIGRvbWFpbk5hbWU6IERPTUFJTl9OQU1FLFxuICAgICAgdmFsaWRhdGlvbjogY20uQ2VydGlmaWNhdGVWYWxpZGF0aW9uLmZyb21EbnMoKSxcbiAgICB9KTtcblxuICAgIC8vY3JlYXRlIFZQQyBcbiAgICBjb25zdCBzZXJ2ZXJsZXNzVlBDID0gbmV3IGVjMi5WcGModGhpcywgJ3NlcnZlcmxlc3NXb3JkcHJlc3NWUEMnLCB7XG4gICAgICBjaWRyOiAnMTAuMC4wLjAvMTYnLFxuICAgICAgc3VibmV0Q29uZmlndXJhdGlvbjogW1xuICAgICAgICB7XG4gICAgICAgICAgc3VibmV0VHlwZTogZWMyLlN1Ym5ldFR5cGUuUFVCTElDLFxuICAgICAgICAgIG5hbWU6ICdwdWJsaWMnLFxuICAgICAgICAgIGNpZHJNYXNrOiAyNCxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGNpZHJNYXNrOiAyNCxcbiAgICAgICAgICBuYW1lOiAncHJpdmF0ZScsXG4gICAgICAgICAgc3VibmV0VHlwZTogZWMyLlN1Ym5ldFR5cGUuUFJJVkFURSxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICAvKipcbiAgICAgKiBjcmVhdGUgc2VjdXJpdHkgZ3JvdXAgaW4gVlBDXG4gICAgICovXG4gICAgLy8gTkZTIHNlY3VyaXR5IGdyb3VwIHdoaWNoIHVzZWQgZm9yIGVjMiB0byBjb3B5IGZpbGVcbiAgICBjb25zdCBzZ05GU1NHID0gbmV3IGVjMi5TZWN1cml0eUdyb3VwKHRoaXMsICdORlNBbGxvd0FsbFNHJywge1xuICAgICAgdnBjOiBzZXJ2ZXJsZXNzVlBDLFxuICAgICAgZGVzY3JpcHRpb246ICdhbGxvdyAyMDQ5IGluYm91bmQgZm9yIGVjMicsXG4gICAgICBhbGxvd0FsbE91dGJvdW5kOiB0cnVlLFxuICAgIH0pO1xuICAgIHNnTkZTU0cuYWRkSW5ncmVzc1J1bGUoZWMyLlBlZXIuYW55SXB2NCgpLCBlYzIuUG9ydC50Y3AoMjA0OSksICdhbGxvdyAyMDQ5IGluYm91bmQgZnJvbSBlYzInKVxuXG4gICAgLy9BTEIgc2VjdXJpdHkgZ3JvdXAgd2hpY2ggYWxsb3cgODAgYW5kIDQ0M1xuICAgIGNvbnN0IGFsYlNHID0gbmV3IGVjMi5TZWN1cml0eUdyb3VwKHRoaXMsICdhbGJTRycsIHtcbiAgICAgIHZwYzogc2VydmVybGVzc1ZQQyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnYWxsb3cgODAgYW5kIDQ0MycsXG4gICAgICBhbGxvd0FsbE91dGJvdW5kOiB0cnVlLFxuICAgIH0pO1xuICAgIGFsYlNHLmFkZEluZ3Jlc3NSdWxlKGVjMi5QZWVyLmFueUlwdjQoKSwgZWMyLlBvcnQudGNwKDgwKSwgJ2FsbG93IDgwIGluYm91bmQnKTtcbiAgICBhbGJTRy5hZGRJbmdyZXNzUnVsZShlYzIuUGVlci5hbnlJcHY0KCksIGVjMi5Qb3J0LnRjcCg0NDMpLCAnYWxsb3cgNDQzIGluYm91bmQnKTtcblxuICAgIC8vRUMyIHNlY3VyaXR5IGdyb3VwIHdoaWNoIGFsbG93IHBvcnQgMjJcbiAgICBjb25zdCBlYzJTRyA9IG5ldyBlYzIuU2VjdXJpdHlHcm91cCh0aGlzLCAnZWMyU0cnLCB7XG4gICAgICB2cGM6IHNlcnZlcmxlc3NWUEMsXG4gICAgICBkZXNjcmlwdGlvbjogJ2FsbG93IDIyIGluYm91bmQgZm9yIGVjMicsXG4gICAgICBhbGxvd0FsbE91dGJvdW5kOiB0cnVlLFxuICAgIH0pO1xuICAgIGVjMlNHLmFkZEluZ3Jlc3NSdWxlKGVjMi5QZWVyLmFueUlwdjQoKSwgZWMyLlBvcnQudGNwKDIyKSwgJ2FsbG93IDIyIGluYm91bmQgZnJvbSBlYzInKVxuXG4gICAgLy8gUkRTIHNlY3VyaXR5IGdyb3VwIHdoaWNoIGFsbG93IHBvcnQgMzMwNlxuICAgIGNvbnN0IHJkc1NHID0gbmV3IGVjMi5TZWN1cml0eUdyb3VwKHRoaXMsICd3b3JkcHJlc3NSZHNTZWN1cml0eUdyb3VwJywge1xuICAgICAgdnBjOiBzZXJ2ZXJsZXNzVlBDLFxuICAgICAgZGVzY3JpcHRpb246ICdhbGxvdyAzMzA2IGluYm91bmQnLFxuICAgICAgYWxsb3dBbGxPdXRib3VuZDogdHJ1ZSxcbiAgICB9KTtcbiAgICByZHNTRy5hZGRJbmdyZXNzUnVsZShlYzIuUGVlci5hbnlJcHY0KCksIGVjMi5Qb3J0LnRjcCgzMzA2KSwgJ2FsbG93IDMzMDYgaW5ib3VuZCBmcm9tIGxhbWJkYScpO1xuXG4gICAgLyoqXG4gICAgICogY3JlYXRlIEVGUyBhdHRhY2hlZCBvbiBMYW1iZGFcbiAgICAgKi9cbiAgICBjb25zdCBmaWxlU3lzdGVtID0gbmV3IGVmcy5GaWxlU3lzdGVtKHRoaXMsICd3b3JkcHJlc3NFRlMnLCB7XG4gICAgICB2cGM6IHNlcnZlcmxlc3NWUEMsXG4gICAgICBlbmNyeXB0ZWQ6IGZhbHNlLFxuICAgICAgcGVyZm9ybWFuY2VNb2RlOiBlZnMuUGVyZm9ybWFuY2VNb2RlLkdFTkVSQUxfUFVSUE9TRSxcbiAgICAgIHRocm91Z2hwdXRNb2RlOiBlZnMuVGhyb3VnaHB1dE1vZGUuQlVSU1RJTkcsXG4gICAgICBzZWN1cml0eUdyb3VwOiBzZ05GU1NHLFxuICAgICAgcmVtb3ZhbFBvbGljeTogY2RrLlJlbW92YWxQb2xpY3kuREVTVFJPWSxcbiAgICB9KTtcbiAgICAvL2NyZWF0ZSBhY2Nlc3MgcG9pbnQgb24gZWZzXG4gICAgY29uc3QgYWNjZXNzUG9pbnQgPSBmaWxlU3lzdGVtLmFkZEFjY2Vzc1BvaW50KCdMYW1iZGFBY2Nlc3NQb2ludCcsIHtcbiAgICAgIHBhdGg6IEFDQ0VTU1BPSU5UX1BBVEgsXG4gICAgICBjcmVhdGVBY2w6IHtcbiAgICAgICAgb3duZXJVaWQ6ICcxMDAwJyxcbiAgICAgICAgb3duZXJHaWQ6ICcxMDAwJyxcbiAgICAgICAgcGVybWlzc2lvbnM6ICcwNzc3JyxcbiAgICAgIH0sXG4gICAgICBwb3NpeFVzZXI6IHtcbiAgICAgICAgdWlkOiAnMTAwMCcsXG4gICAgICAgIGdpZDogJzEwMDAnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBsYW1iZGEgZnVuY3Rpb25cbiAgICAgKi9cbiAgICBjb25zdCBsYW1iZGFGdW5jID0gbmV3IGxhbWJkYS5GdW5jdGlvbih0aGlzLCAnd29yZHByZXNzTGFtYmRhRlVuY3Rpb24nLCB7XG4gICAgICBjb2RlOiBsYW1iZGEuQ29kZS5mcm9tQXNzZXQocGF0aC5qb2luKF9fZGlybmFtZSwgJ3BocExhbWJkYUZ1bmMnKSksXG4gICAgICBoYW5kbGVyOiAnaGFuZGxlci5waHAnLFxuICAgICAgbWVtb3J5U2l6ZTogMTAyNCxcbiAgICAgIHRpbWVvdXQ6IGNkay5EdXJhdGlvbi5taW51dGVzKDE1KSxcbiAgICAgIHRyYWNpbmc6IGxhbWJkYS5UcmFjaW5nLkFDVElWRSxcbiAgICAgIHJ1bnRpbWU6IGxhbWJkYS5SdW50aW1lLlBST1ZJREVELFxuICAgICAgbGF5ZXJzOiBbbGFtYmRhLkxheWVyVmVyc2lvbi5mcm9tTGF5ZXJWZXJzaW9uQXJuKHRoaXMsICdjdXN0b21QaHBMYXllcicsICdhcm46YXdzOmxhbWJkYTp1cy1lYXN0LTE6ODg3MDgwMTY5NDgwOmxheWVyOnBocDczOjMnKV0sXG4gICAgICB2cGM6IHNlcnZlcmxlc3NWUEMsXG4gICAgICBmaWxlc3lzdGVtOiBsYW1iZGEuRmlsZVN5c3RlbS5mcm9tRWZzQWNjZXNzUG9pbnQoYWNjZXNzUG9pbnQsIEJBU0VfUEFUSCksXG4gICAgfSk7XG5cbiAgICAvKlxuICAgICAqIGNyZWF0ZSBhbGIgYW5kIGludGVncmF0ZSBpdCB3aXRoIGxhbWJkYVxuICAgICAqL1xuICAgIGNvbnN0IGxiID0gbmV3IGVsYnYyLkFwcGxpY2F0aW9uTG9hZEJhbGFuY2VyKHRoaXMsICdzZXJ2ZXJsZXNzQUxCJywge1xuICAgICAgdnBjOiBzZXJ2ZXJsZXNzVlBDLFxuICAgICAgaW50ZXJuZXRGYWNpbmc6IHRydWUsXG4gICAgICBzZWN1cml0eUdyb3VwOiBhbGJTRyxcbiAgICB9KTtcblxuICAgIGNvbnN0IGxhbWJkYVRhcmdldCA9IG5ldyB0YXJnZXRzLkxhbWJkYVRhcmdldChsYW1iZGFGdW5jKTtcbiAgICBjb25zdCBhbGJUYXJnZXRHcm91cCA9IG5ldyBlbGJ2Mi5BcHBsaWNhdGlvblRhcmdldEdyb3VwKHRoaXMsJ2FsYlRhcmdldEdyb3VwJyx7XG4gICAgICB0YXJnZXRzOiBbbGFtYmRhVGFyZ2V0XSxcbiAgICB9KTtcbiAgICBhbGJUYXJnZXRHcm91cC5zZXRBdHRyaWJ1dGUoJ2xhbWJkYS5tdWx0aV92YWx1ZV9oZWFkZXJzLmVuYWJsZWQnLCAndHJ1ZScpO1xuXG4gICAgY29uc3QgbGlzdGVuZXI4MCA9IGxiLmFkZExpc3RlbmVyKCdMaXN0ZW5lcjgwJywge1xuICAgICAgcG9ydDogODAsXG4gICAgICBvcGVuOiB0cnVlLFxuICAgIH0pO1xuICAgIGxpc3RlbmVyODAuYWRkQWN0aW9uKCc4MGFjdGlvbicse1xuICAgICAgYWN0aW9uOiBMaXN0ZW5lckFjdGlvbi5mb3J3YXJkKFthbGJUYXJnZXRHcm91cF0pXG4gICAgfSk7XG5cbiAgICBjb25zdCBsaXN0ZW5lcjQ0MyA9IGxiLmFkZExpc3RlbmVyKCdMaXN0ZW5lcjQ0MycsIHtcbiAgICAgIHBvcnQ6IDQ0MyxcbiAgICAgIG9wZW46IHRydWUsXG4gICAgICBjZXJ0aWZpY2F0ZUFybnM6W215Q2VydGlmaWNhdGUuY2VydGlmaWNhdGVBcm5dLFxuICAgIH0pO1xuICAgIGxpc3RlbmVyNDQzLmFkZEFjdGlvbignNDQzYWN0aW9uJyx7XG4gICAgICBhY3Rpb246IExpc3RlbmVyQWN0aW9uLmZvcndhcmQoW2FsYlRhcmdldEdyb3VwXSlcbiAgICB9KTtcblxuICAgIC8qKlxuICAgICAqIGNyZWF0ZSBSRFNcbiAgICAgKi9cblxuICAgIGNvbnN0IHNlY3JldCA9IGNkay5TZWNyZXRWYWx1ZS5wbGFpblRleHQoREJfUEFTU1dPUkQpO1xuICAgIGNvbnN0IGF1cm9yYVNlcnZlcmxlc3NDbHVzdGVyID0gbmV3IHJkcy5EYXRhYmFzZUNsdXN0ZXIodGhpcywgJ1NlcnZlcmxlc3NXb3JkcHJlc3NBdXJvcmFDbHVzdGVyJywge1xuICAgICAgZW5naW5lOiByZHMuRGF0YWJhc2VDbHVzdGVyRW5naW5lLkFVUk9SQV9NWVNRTCxcbiAgICAgIGNyZWRlbnRpYWxzOiByZHMuQ3JlZGVudGlhbHMuZnJvbVBhc3N3b3JkKERCX1VTRVIsc2VjcmV0KSxcbiAgICAgIHJlbW92YWxQb2xpY3k6IGNkay5SZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICBpbnN0YW5jZVByb3BzOiB7XG4gICAgICAgIHZwYzogc2VydmVybGVzc1ZQQyxcbiAgICAgICAgc2VjdXJpdHlHcm91cHM6IFtyZHNTR10sXG4gICAgICB9LFxuICAgICAgZGVmYXVsdERhdGFiYXNlTmFtZTogREJfTkFNRSxcbiAgICB9KTtcblxuICAgIC8qKipcbiAgICAgKiAgc2V0IHRoZSBEQl9IT1NUIGFuZCBIVFRQX0hPU1Qgd2hpY2ggd2lsbCB1c2VkIGluIHRoZSBsYW1iZGEgZW52aXJvbm1lbnRcbiAgICAgKi9cbiAgICBEQl9IT1NUID0gYXVyb3JhU2VydmVybGVzc0NsdXN0ZXIuY2x1c3RlckVuZHBvaW50Lmhvc3RuYW1lO1xuICAgIEhUVFBfSE9TVCA9IGxiLmxvYWRCYWxhbmNlckRuc05hbWU7XG5cbiAgICAvL1NFVCBsYW1iZGEgZW52aXJvbW5lbnRcbiAgICBsYW1iZGFGdW5jLmFkZEVudmlyb25tZW50KCdEQl9IT1NUJywgREJfSE9TVCk7XG4gICAgbGFtYmRhRnVuYy5hZGRFbnZpcm9ubWVudCgnREJfTkFNRScsIERCX05BTUUpO1xuICAgIGxhbWJkYUZ1bmMuYWRkRW52aXJvbm1lbnQoJ0RCX1VTRVInLCBEQl9VU0VSKTtcbiAgICBsYW1iZGFGdW5jLmFkZEVudmlyb25tZW50KCdEQl9QQVNTV09SRCcsIERCX1BBU1NXT1JEKTtcbiAgICBsYW1iZGFGdW5jLmFkZEVudmlyb25tZW50KCdXT1JEUFJFU1NfUEFUSCcsIFdPUkRQUkVTU19QQVRIKTtcbiAgICBsYW1iZGFGdW5jLmFkZEVudmlyb25tZW50KCdIVFRQX0hPU1QnLCBIVFRQX0hPU1QpO1xuXG4gICAgLy8gY3JlYXRlIEVDMiB3aGljaCB1c2VkIHRvIGluc3RhbGwgd29yZHByZXNzIGZpbGVzIHRvIEVGU1xuICAgIGNvbnN0IGFtem5MaW51eCA9IGVjMi5NYWNoaW5lSW1hZ2UubGF0ZXN0QW1hem9uTGludXgoe1xuICAgICAgZ2VuZXJhdGlvbjogZWMyLkFtYXpvbkxpbnV4R2VuZXJhdGlvbi5BTUFaT05fTElOVVgsXG4gICAgICBlZGl0aW9uOiBlYzIuQW1hem9uTGludXhFZGl0aW9uLlNUQU5EQVJELFxuICAgICAgdmlydHVhbGl6YXRpb246IGVjMi5BbWF6b25MaW51eFZpcnQuSFZNLFxuICAgICAgc3RvcmFnZTogZWMyLkFtYXpvbkxpbnV4U3RvcmFnZS5HRU5FUkFMX1BVUlBPU0UsXG4gICAgfSk7XG5cbiAgICBjb25zdCBlYzJFRlMgPSBuZXcgZWMyLkluc3RhbmNlKHRoaXMsJ2Vmc0luc3RhbmNlJyx7XG4gICAgICB2cGM6IHNlcnZlcmxlc3NWUEMsXG4gICAgICB2cGNTdWJuZXRzOiB7c3VibmV0VHlwZTplYzIuU3VibmV0VHlwZS5QVUJMSUN9LFxuICAgICAgbWFjaGluZUltYWdlIDogYW16bkxpbnV4LFxuICAgICAgaW5zdGFuY2VUeXBlOiBuZXcgZWMyLkluc3RhbmNlVHlwZSgndDIubGFyZ2UnKSxcbiAgICAgIHNlY3VyaXR5R3JvdXA6IGVjMlNHLFxuICAgICAga2V5TmFtZTpLRVlfTkFNRSxcbiAgICB9KTtcblxuICAgIGVjMkVGUy51c2VyRGF0YS5hZGRDb21tYW5kcyhcbiAgICAgIC8vaW5zdGFsbCBlZnMgdG9vbCBhbmQgY3JlYXRlIG1vdW50IHBvaW50XG4gICAgICAnc3VkbyB5dW0gaW5zdGFsbCAteSBhbWF6b24tZWZzLXV0aWxzJyxcbiAgICAgICdzdWRvIG1rZGlyIC9tbnQnLFxuICAgICAgJ3N1ZG8gbWtkaXIgL21udC9lZnMnLFxuICAgICk7XG5cbiAgICBuZXcgY2RrLkNmbk91dHB1dCh0aGlzLCAnb3V0cHV0RUZTJywge1xuICAgICAgZGVzY3JpcHRpb246ICdlZnMgaWQnLFxuICAgICAgdmFsdWU6ICdlZnMgaWQ6ICcgK2ZpbGVTeXN0ZW0uZmlsZVN5c3RlbUlkLFxuICAgIH0pO1xuXG4gICAgbmV3IGNkay5DZm5PdXRwdXQodGhpcywgJ291dHB1dEFMQkROUycsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnYWxiIGRucyBuYW1lJyxcbiAgICAgIHZhbHVlOiAnYWxiIGRucyBuYW1lOiAnICtsYi5sb2FkQmFsYW5jZXJEbnNOYW1lLFxuICAgIH0pO1xuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /LambdaWordpress/lib/lambda_wordpress-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as ec2 from '@aws-cdk/aws-ec2'; 3 | import * as efs from '@aws-cdk/aws-efs'; 4 | import * as lambda from '@aws-cdk/aws-lambda'; 5 | import * as rds from '@aws-cdk/aws-rds'; 6 | import * as elbv2 from '@aws-cdk/aws-elasticloadbalancingv2'; 7 | import * as targets from '@aws-cdk/aws-elasticloadbalancingv2-targets'; 8 | import * as cm from '@aws-cdk/aws-certificatemanager'; 9 | import {ApplicationTargetGroup, ApplicationProtocol, ListenerAction} from '@aws-cdk/aws-elasticloadbalancingv2' 10 | import * as path from 'path'; 11 | export class LambdaWordpressStack extends cdk.Stack { 12 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 13 | super(scope, id, props); 14 | 15 | var DB_HOST = null; 16 | var HTTP_HOST = null; 17 | const DB_NAME = 'wordpress'; 18 | const DB_USER = 'wordpressuser'; 19 | const BASE_PATH = '/mnt/efs'; 20 | const ACCESSPOINT_PATH = '/wordpress'; 21 | const WORDPRESS_PATH = '/mnt/efs'; 22 | const KEY_NAME = this.node.tryGetContext('keyName'); 23 | const DOMAIN_NAME = this.node.tryGetContext('domainName'); 24 | const DB_PASSWORD = this.node.tryGetContext('dbPassword'); 25 | 26 | //set the certificate 27 | const myCertificate = new cm.Certificate(this, 'myCertificate',{ 28 | domainName: DOMAIN_NAME, 29 | validation: cm.CertificateValidation.fromDns(), 30 | }); 31 | 32 | //create VPC 33 | const serverlessVPC = new ec2.Vpc(this, 'serverlessWordpressVPC', { 34 | cidr: '10.0.0.0/16', 35 | subnetConfiguration: [ 36 | { 37 | subnetType: ec2.SubnetType.PUBLIC, 38 | name: 'public', 39 | cidrMask: 24, 40 | }, 41 | { 42 | cidrMask: 24, 43 | name: 'private', 44 | subnetType: ec2.SubnetType.PRIVATE, 45 | }, 46 | ], 47 | }); 48 | 49 | /** 50 | * create security group in VPC 51 | */ 52 | // NFS security group which used for ec2 to copy file 53 | const sgNFSSG = new ec2.SecurityGroup(this, 'NFSAllowAllSG', { 54 | vpc: serverlessVPC, 55 | description: 'allow 2049 inbound for ec2', 56 | allowAllOutbound: true, 57 | }); 58 | sgNFSSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(2049), 'allow 2049 inbound from ec2') 59 | 60 | //ALB security group which allow 80 and 443 61 | const albSG = new ec2.SecurityGroup(this, 'albSG', { 62 | vpc: serverlessVPC, 63 | description: 'allow 80 and 443', 64 | allowAllOutbound: true, 65 | }); 66 | albSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'allow 80 inbound'); 67 | albSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'allow 443 inbound'); 68 | 69 | //EC2 security group which allow port 22 70 | const ec2SG = new ec2.SecurityGroup(this, 'ec2SG', { 71 | vpc: serverlessVPC, 72 | description: 'allow 22 inbound for ec2', 73 | allowAllOutbound: true, 74 | }); 75 | ec2SG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'allow 22 inbound from ec2') 76 | 77 | // RDS security group which allow port 3306 78 | const rdsSG = new ec2.SecurityGroup(this, 'wordpressRdsSecurityGroup', { 79 | vpc: serverlessVPC, 80 | description: 'allow 3306 inbound', 81 | allowAllOutbound: true, 82 | }); 83 | rdsSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(3306), 'allow 3306 inbound from lambda'); 84 | 85 | /** 86 | * create EFS attached on Lambda 87 | */ 88 | const fileSystem = new efs.FileSystem(this, 'wordpressEFS', { 89 | vpc: serverlessVPC, 90 | encrypted: false, 91 | performanceMode: efs.PerformanceMode.GENERAL_PURPOSE, 92 | throughputMode: efs.ThroughputMode.BURSTING, 93 | securityGroup: sgNFSSG, 94 | removalPolicy: cdk.RemovalPolicy.DESTROY, 95 | }); 96 | //create access point on efs 97 | const accessPoint = fileSystem.addAccessPoint('LambdaAccessPoint', { 98 | path: ACCESSPOINT_PATH, 99 | createAcl: { 100 | ownerUid: '1000', 101 | ownerGid: '1000', 102 | permissions: '0777', 103 | }, 104 | posixUser: { 105 | uid: '1000', 106 | gid: '1000', 107 | }, 108 | }); 109 | 110 | /** 111 | * Create lambda function 112 | */ 113 | const lambdaFunc = new lambda.Function(this, 'wordpressLambdaFUnction', { 114 | code: lambda.Code.fromAsset(path.join(__dirname, 'phpLambdaFunc')), 115 | handler: 'handler.php', 116 | memorySize: 1024, 117 | timeout: cdk.Duration.minutes(15), 118 | tracing: lambda.Tracing.ACTIVE, 119 | runtime: lambda.Runtime.PROVIDED, 120 | layers: [lambda.LayerVersion.fromLayerVersionArn(this, 'customPhpLayer', 'arn:aws:lambda:us-east-1:887080169480:layer:php73:3')], 121 | vpc: serverlessVPC, 122 | filesystem: lambda.FileSystem.fromEfsAccessPoint(accessPoint, BASE_PATH), 123 | }); 124 | 125 | /* 126 | * create alb and integrate it with lambda 127 | */ 128 | const lb = new elbv2.ApplicationLoadBalancer(this, 'serverlessALB', { 129 | vpc: serverlessVPC, 130 | internetFacing: true, 131 | securityGroup: albSG, 132 | }); 133 | 134 | const lambdaTarget = new targets.LambdaTarget(lambdaFunc); 135 | const albTargetGroup = new elbv2.ApplicationTargetGroup(this,'albTargetGroup',{ 136 | targets: [lambdaTarget], 137 | }); 138 | albTargetGroup.setAttribute('lambda.multi_value_headers.enabled', 'true'); 139 | 140 | const listener80 = lb.addListener('Listener80', { 141 | port: 80, 142 | open: true, 143 | }); 144 | listener80.addAction('80action',{ 145 | action: ListenerAction.forward([albTargetGroup]) 146 | }); 147 | 148 | const listener443 = lb.addListener('Listener443', { 149 | port: 443, 150 | open: true, 151 | certificateArns:[myCertificate.certificateArn], 152 | }); 153 | listener443.addAction('443action',{ 154 | action: ListenerAction.forward([albTargetGroup]) 155 | }); 156 | 157 | /** 158 | * create RDS 159 | */ 160 | 161 | const secret = cdk.SecretValue.plainText(DB_PASSWORD); 162 | const auroraServerlessCluster = new rds.DatabaseCluster(this, 'ServerlessWordpressAuroraCluster', { 163 | engine: rds.DatabaseClusterEngine.AURORA_MYSQL, 164 | credentials: rds.Credentials.fromPassword(DB_USER,secret), 165 | removalPolicy: cdk.RemovalPolicy.DESTROY, 166 | instanceProps: { 167 | vpc: serverlessVPC, 168 | securityGroups: [rdsSG], 169 | }, 170 | defaultDatabaseName: DB_NAME, 171 | }); 172 | 173 | /*** 174 | * set the DB_HOST and HTTP_HOST which will used in the lambda environment 175 | */ 176 | DB_HOST = auroraServerlessCluster.clusterEndpoint.hostname; 177 | HTTP_HOST = lb.loadBalancerDnsName; 178 | 179 | //SET lambda enviromnent 180 | lambdaFunc.addEnvironment('DB_HOST', DB_HOST); 181 | lambdaFunc.addEnvironment('DB_NAME', DB_NAME); 182 | lambdaFunc.addEnvironment('DB_USER', DB_USER); 183 | lambdaFunc.addEnvironment('DB_PASSWORD', DB_PASSWORD); 184 | lambdaFunc.addEnvironment('WORDPRESS_PATH', WORDPRESS_PATH); 185 | lambdaFunc.addEnvironment('HTTP_HOST', HTTP_HOST); 186 | 187 | // create EC2 which used to install wordpress files to EFS 188 | const amznLinux = ec2.MachineImage.latestAmazonLinux({ 189 | generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX, 190 | edition: ec2.AmazonLinuxEdition.STANDARD, 191 | virtualization: ec2.AmazonLinuxVirt.HVM, 192 | storage: ec2.AmazonLinuxStorage.GENERAL_PURPOSE, 193 | }); 194 | 195 | const ec2EFS = new ec2.Instance(this,'efsInstance',{ 196 | vpc: serverlessVPC, 197 | vpcSubnets: {subnetType:ec2.SubnetType.PUBLIC}, 198 | machineImage : amznLinux, 199 | instanceType: new ec2.InstanceType('t2.large'), 200 | securityGroup: ec2SG, 201 | keyName:KEY_NAME, 202 | }); 203 | 204 | ec2EFS.userData.addCommands( 205 | //install efs tool and create mount point 206 | 'sudo yum install -y amazon-efs-utils', 207 | 'sudo mkdir /mnt', 208 | 'sudo mkdir /mnt/efs', 209 | ); 210 | 211 | new cdk.CfnOutput(this, 'outputEFS', { 212 | description: 'efs id', 213 | value: 'efs id: ' +fileSystem.fileSystemId, 214 | }); 215 | 216 | new cdk.CfnOutput(this, 'outputALBDNS', { 217 | description: 'alb dns name', 218 | value: 'alb dns name: ' +lb.loadBalancerDnsName, 219 | }); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /LambdaWordpress/lib/phpLambdaFunc/Makefile: -------------------------------------------------------------------------------- 1 | build-WordpressFunction: 2 | cp ./*.php ./*.ini $(ARTIFACTS_DIR) -------------------------------------------------------------------------------- /LambdaWordpress/lib/phpLambdaFunc/handler.php: -------------------------------------------------------------------------------- 1 | "text/css", 7 | "js" => "application/javascript", 8 | "png" => "image/png", 9 | "jpeg" => "image/jpeg", 10 | "jpg" => "image/jpeg", 11 | "svg" => "image/svg+xml" 12 | ); 13 | 14 | $request_uri = explode("?", $_SERVER['REQUEST_URI']); 15 | $local_file_path = getenv('WORDPRESS_PATH') . $request_uri[0]; 16 | 17 | $split = explode(".", $local_file_path); 18 | $extension = strtolower(array_pop($split)); 19 | $mapped_type = null; 20 | if (isset($extension_map[$extension])) { 21 | $mapped_type = $extension_map[$extension]; 22 | } 23 | 24 | if ( $mapped_type && file_exists( $local_file_path ) ) { 25 | header("Content-Type: {$mapped_type}"); 26 | readfile($local_file_path); 27 | 28 | } elseif ( $extension == "php" && file_exists( $local_file_path ) ) { 29 | require( $local_file_path ); 30 | 31 | } elseif ( substr($local_file_path, -1) == "/" && file_exists( $local_file_path . "index.php" ) ) { 32 | $exec_file_path = $local_file_path . "index.php"; 33 | require( $exec_file_path ); 34 | 35 | } else { 36 | $exec_file_path = getenv('WORDPRESS_PATH') . '/index.php'; 37 | require( $exec_file_path ); 38 | } 39 | -------------------------------------------------------------------------------- /LambdaWordpress/lib/phpLambdaFunc/php.ini: -------------------------------------------------------------------------------- 1 | extension=json 2 | extension=curl 3 | extension=mbstring 4 | extension=mysqlnd 5 | extension=mysqli 6 | extension=iconv 7 | extension=gettext 8 | extension=fileinfo 9 | extension=simplexml 10 | 11 | memory_limit = 1024M 12 | -------------------------------------------------------------------------------- /LambdaWordpress/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lambda_wordpress", 3 | "version": "0.1.0", 4 | "bin": { 5 | "lambda_wordpress": "bin/lambda_wordpress.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "dependencies": { 14 | "@aws-cdk/assert": "^1.180.0", 15 | "@aws-cdk/aws-certificatemanager": "^1.180.0", 16 | "@aws-cdk/aws-ec2": "^1.180.0", 17 | "@aws-cdk/aws-efs": "^1.180.0", 18 | "@aws-cdk/aws-elasticloadbalancingv2": "^1.180.0", 19 | "@aws-cdk/aws-elasticloadbalancingv2-targets": "^1.180.0", 20 | "@aws-cdk/aws-lambda": "^1.180.0", 21 | "@aws-cdk/aws-rds": "^1.180.0", 22 | "@aws-cdk/aws-secretsmanager": "^1.180.0", 23 | "@aws-cdk/core": "^1.180.0", 24 | "@types/jest": "^26.0.10", 25 | "jest": "^26.4.2", 26 | "path": "^0.12.7" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LambdaWordpress/test/lambda_wordpress.test.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /LambdaWordpress/test/lambda_wordpress.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const assert_1 = require("@aws-cdk/assert"); 4 | const cdk = require("@aws-cdk/core"); 5 | const LambdaWordpress = require("../lib/lambda_wordpress-stack"); 6 | test('Empty Stack', () => { 7 | const app = new cdk.App(); 8 | // WHEN 9 | const stack = new LambdaWordpress.LambdaWordpressStack(app, 'MyTestStack'); 10 | // THEN 11 | assert_1.expect(stack).to(assert_1.matchTemplate({ 12 | "Resources": {} 13 | }, assert_1.MatchStyle.EXACT)); 14 | }); 15 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFtYmRhX3dvcmRwcmVzcy50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsibGFtYmRhX3dvcmRwcmVzcy50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsNENBQWlGO0FBQ2pGLHFDQUFxQztBQUNyQyxpRUFBaUU7QUFFakUsSUFBSSxDQUFDLGFBQWEsRUFBRSxHQUFHLEVBQUU7SUFDckIsTUFBTSxHQUFHLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDMUIsT0FBTztJQUNQLE1BQU0sS0FBSyxHQUFHLElBQUksZUFBZSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxhQUFhLENBQUMsQ0FBQztJQUMzRSxPQUFPO0lBQ1AsZUFBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxzQkFBYSxDQUFDO1FBQ2hDLFdBQVcsRUFBRSxFQUFFO0tBQ2hCLEVBQUUsbUJBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFBO0FBQ3pCLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhwZWN0IGFzIGV4cGVjdENESywgbWF0Y2hUZW1wbGF0ZSwgTWF0Y2hTdHlsZSB9IGZyb20gJ0Bhd3MtY2RrL2Fzc2VydCc7XG5pbXBvcnQgKiBhcyBjZGsgZnJvbSAnQGF3cy1jZGsvY29yZSc7XG5pbXBvcnQgKiBhcyBMYW1iZGFXb3JkcHJlc3MgZnJvbSAnLi4vbGliL2xhbWJkYV93b3JkcHJlc3Mtc3RhY2snO1xuXG50ZXN0KCdFbXB0eSBTdGFjaycsICgpID0+IHtcbiAgICBjb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xuICAgIC8vIFdIRU5cbiAgICBjb25zdCBzdGFjayA9IG5ldyBMYW1iZGFXb3JkcHJlc3MuTGFtYmRhV29yZHByZXNzU3RhY2soYXBwLCAnTXlUZXN0U3RhY2snKTtcbiAgICAvLyBUSEVOXG4gICAgZXhwZWN0Q0RLKHN0YWNrKS50byhtYXRjaFRlbXBsYXRlKHtcbiAgICAgIFwiUmVzb3VyY2VzXCI6IHt9XG4gICAgfSwgTWF0Y2hTdHlsZS5FWEFDVCkpXG59KTtcbiJdfQ== -------------------------------------------------------------------------------- /LambdaWordpress/test/lambda_wordpress.test.ts: -------------------------------------------------------------------------------- 1 | import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; 2 | import * as cdk from '@aws-cdk/core'; 3 | import * as LambdaWordpress from '../lib/lambda_wordpress-stack'; 4 | 5 | test('Empty Stack', () => { 6 | const app = new cdk.App(); 7 | // WHEN 8 | const stack = new LambdaWordpress.LambdaWordpressStack(app, 'MyTestStack'); 9 | // THEN 10 | expectCDK(stack).to(matchTemplate({ 11 | "Resources": {} 12 | }, MatchStyle.EXACT)) 13 | }); 14 | -------------------------------------------------------------------------------- /LambdaWordpress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | this project is archived. please check another similar project 2 | 3 | https://github.com/aws-samples/serverless-woocommerce-workshop 4 | 5 | # cdk-serverless-wordpress 6 | 7 | ## what does this repo do 8 | 9 | this project help to run the serverless wordpress with AWS Lambda and AWS EFS 10 | 11 | ### init CDK project 12 | 13 | 1. run below commands to install cdk components 14 | 15 | ```ts 16 | npm install @aws-cdk/aws-ec2 @aws-cdk/aws-efs @aws-cdk/aws-lambda @aws-cdk/aws-rds @aws-cdk/aws-elasticloadbalancingv2 @aws-cdk/aws-elasticloadbalancingv2-targets @aws-cdk/aws-secretsmanager path 17 | ``` 18 | 2. find the cdk.json file, replace the domainName, keyName, dbPassword with your own value. 19 | 20 | | | | 21 | | ---------- | --- | 22 | | domainName | your domain name, which used to validate the certificate | 23 | | keyName | the key pairs which used to login to the EC2 | 24 | | dbPassword | the rds password | 25 | 26 | 27 | 3. compile and deploy 28 | 29 | ```ts 30 | npm run build 31 | cdk deploy 32 | ``` 33 | remeber use us-east-1 region, open aws console, find Certificate Manager service and validate the certificate with DNS name, you can refer this doc https://docs.aws.amazon.com/zh_cn/acm/latest/userguide/gs-acm-validate-dns.html 34 | 35 | you can find the EFS ID at the output 36 | 37 | 4. Launch EC2 and install wordpress on EFS 38 | 39 | ``` 40 | sudo mount -t efs YOUR_EFS_ID:/ /mnt/efs 41 | ``` 42 | 43 | you can download the wordpress package and unzip them into path /mnt/efs/wordpress 44 | then edit below items in the wp-config.php file 45 | 46 | ```php 47 | define( 'DB_NAME', getenv('DB_NAME') ); 48 | define( 'DB_USER', getenv('DB_USER') ); 49 | define( 'DB_PASSWORD', getenv('DB_PASSWORD') ); 50 | define( 'DB_HOST', getenv('DB_HOST') ); 51 | define('WP_SITEURL', 'https://' . getenv('HTTP_HOST') ); 52 | define('WP_HOME', 'https://' . getenv('HTTP_HOST') ); 53 | $_SERVER['HTTP_HOST'] = getenv('HTTP_HOST') ; 54 | ``` 55 | you can also download the source from below github 56 | https://github.com/forhead/wordpressForLambda.git 57 | 58 | 5. launch the serverless wordpress with alb DNS name, you can config the dns on your own domain 59 | 60 | 61 | ## Security 62 | 63 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 64 | 65 | ## License 66 | 67 | This library is licensed under the MIT-0 License. See the LICENSE file. 68 | 69 | --------------------------------------------------------------------------------