Skip to content
This repository was archived by the owner on Feb 7, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
src/php/wp-config.php
serverless-output.yaml
samconfig.toml
83 changes: 78 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,85 @@

Use at your own caution!!!

[Read more](https://keita.blog/?p=1796)
[Read the original article](https://keita.blog/?p=1796)

## How To Use
## Quick Start

This creates a cloudfront domain, api gateway, lambda function, aurora (MySQL) serverless cluster and security groups in a previously provisioned VPC.

### 0. Prerequisites

1. AWS CLI ([download](https://aws.amazon.com/cli/))
2. A recent version (^0.33.1) of the AWS SAM CLI with guided deployments ([download](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html))

### 1. Use the guided SAM template to deploy the template once.
Run the following the deploy using your default AWS profile in ~/.aws/credentials:
```bash
sam deploy -g
```
or use the following to deploy in a custom profile:
```bash
sam deploy -g --profile REPLACE_THIS_WITH_NAMED_PROFILE
```
| Parameter | Description |
| ------------- | ------------- |
| Stack Name | Anything you want. Must be unique from what you already have in the region unless you want to update the stack. |
| AWS Region | Region you are deploying to. |
| VpcId | The VPC in the region you are deploying to. You can obtain this by going to https://console.aws.amazon.com/vpc/home?region=`AWS_REGION_HERE`#vpcs:sort=VpcId |
| VpcSubnetIds | A comma-delimited string of subnet IDs belonging to the VPC you specified earlier. You must specify at least 1 subnet ID. |
| StageName | (optional) Stage name of API gateway. |
| CloudFrontPriceClass | (optional) See [here](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cloudfront-distribution-distributionconfig.html#cfn-cloudfront-distribution-distributionconfig-priceclass) for a list of valid values. |
| DBName | (optional) The name of the database in the cluster to connect to |
| DBUser | (optional) Default: `admin`. The username of the database cluster |
| DBPassword | The password corresponding to DBUser. |
| Confirm changes before deploy | Y if you want to check changes, N otherwise. |
| Allow SAM CLI role creation | Y, to let SAM manage our deployment bucket. |
| Save arguments to samconfig.toml | Y, so you don't need to go through the steps here again later. |

This will deploy the stack and required resources. Visiting the cloudfront domain won't work just yet.

### 2. Set up `wp-config.php`

Copy the modified wp-config-sample.php and replace all `cloudfront_domain_name_here` with the value of the `CloudFrontDistributionDomainName` (e.g. abcd.cloudfront.net) stack output you obtained from the previous step.

Linux/Mac:
```bash
cp ./src/php/wp-config-sample.php ./src/php/wp-config.php
sed -i "s/cloudfront_domain_name_here/REPLACE_THIS_WITH_CLOUDFRONT_DOMAIN/g" ./src/php/wp-config.php
```

### 3. Redeploy

```bash
$ sam package --template-file template.yaml --output-template-file serverless-output.yaml --s3-bucket "$DEPLOY_BUCKET"
$ sam deploy --template-file serverless-output.yaml --stack-name wordpress-on-lambda --capabilities CAPABILITY_IAM
$ aws s3 sync ./src/php s3://deploy-bucket-XXXXX/prod --exclude "*.php" --exclude "*.ini"
sam deploy
```

We need to redeploy the updated `wp-config.php` onto our lambda function as it contains our settings. There is a circular dependency between cloudfront and the autogenerated IAM permissions for the function so placing the cloudfront domain as an environment variable won't work.

### 4. Deploy static assets

Replace `REPLACE_THIS_WITH_ASSETS_BUCKET_NAME` with the value of `AssetsBucketName` from the stack outputs obtained from the previous step.

```bash
aws s3 sync ./src/php s3://REPLACE_THIS_WITH_ASSETS_BUCKET_NAME --exclude "*.php" --exclude "*.ini"
```

or the following if you used a named profile in step 1:

```bash
aws s3 sync ./src/php s3://REPLACE_THIS_WITH_ASSETS_BUCKET_NAME --exclude "*.php" --exclude "*.ini" --profile REPLACE_THIS_WITH_NAMED_PROFILE
```

### 5. Visit cloudfront domain

You should see the following Wordpress 5.2.1 installation (everything under `./src/php`):

![Wordpress Install](./docs/success.png)

## Using a different Wordpress version

Throw everything away under ./src/php *except* for:

- `wp-config.php` or `wp-config-sample.php`
- `php.ini`
- `src/php/wp-content/S3-Uploads-2.1.0/*`
Binary file added docs/success.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions src/php/wp-config-sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );
define( 'DB_NAME', getenv('DATABASE_NAME') );

/** MySQL database username */
define( 'DB_USER', 'username_here' );
define( 'DB_USER', getenv('DATABASE_USER') );

/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );
define( 'DB_PASSWORD', getenv('DATABASE_PASS') );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
define( 'DB_HOST', getenv('DATABASE_ENDPOINT') );

/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );

/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

define('WP_SITEURL', 'https://[CLOUDFRONT DOMAIN NAME HERE]');
define('WP_HOME', 'https://[CLOUDFRONT DOMAIN NAME HERE]');
$_SERVER['HTTP_HOST'] = '[CLOUDFRONT DOMAIN NAME HERE]';
define('WP_SITEURL', 'https://cloudfront_domain_name_here');
define('WP_HOME', 'https://cloudfront_domain_name_here');
$_SERVER['HTTP_HOST'] = 'cloudfront_domain_name_here';

define( 'S3_UPLOADS_BUCKET', getenv('S3_UPLOADS_BUCKET') . '/wp-content' );
define( 'S3_UPLOADS_USE_INSTANCE_PROFILE', true );
Expand Down
142 changes: 103 additions & 39 deletions template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,43 @@ Description: WordPress on AWS Lambda!
Transform: AWS::Serverless-2016-10-31

Parameters:
VpcSecurityGroupIds:
Type: 'List<AWS::EC2::SecurityGroup::Id>'
Default: 'sg-53af6735'
VpcId:
Description: The default VPC ID in the region you're deploying to.
Type: 'AWS::EC2::VPC::Id'
VpcSubnetIds:
Type: 'List<AWS::EC2::Subnet::Id>'
Default: 'subnet-0dcd7a44,subnet-a8757ef0,subnet-b19c01d6'
StageName:
Type: String
Default: 'Prod'
CloudFrontPriceClass:
Type: String
AllowedValues:
- PriceClass_100
- PriceClass_200
- PriceClass_All
Default: 'PriceClass_200'
DBName:
Default: wordpressdb
Description: The WordPress database name.
Type: String
MinLength: '1'
MaxLength: '64'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBUser:
Default: admin
Description: The WordPress database admin account username
Type: String
MinLength: '1'
MaxLength: '16'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: must begin with a letter and contain only alphanumeric characters.
DBPassword:
NoEcho: 'true'
Description: The WordPress database admin account password. The password validation regex varies for different wordpress versions.
Type: String
MinLength: '8'
MaxLength: '41'

Resources:
phpserver:
Expand All @@ -30,11 +55,17 @@ Resources:
Tracing: Active
Layers:
# - arn:aws:lambda:us-west-2:887080169480:layer:php73:2
- arn:aws:lambda:us-west-2:777160072469:layer:php73:11
- !Sub "arn:aws:lambda:${AWS::Region}:887080169480:layer:php73:3"

Environment:
Variables:
S3_UPLOADS_BUCKET: !Ref assetsS3
DATABASE_ENDPOINT: !GetAtt [dbCluster, Endpoint.Address]
DATABASE_NAME: !Ref DBName
DATABASE_USER: !Ref DBUser
# Comment out the line below or use KMS if you have requirements for security.
# Otherwise, security groups should be secure enough for networking.
DATABASE_PASS: !Ref DBPassword

Policies:
- VPCAccessPolicy: {}
Expand All @@ -58,9 +89,25 @@ Resources:

# Comment this out to run in public mode.
VpcConfig:
SecurityGroupIds: !Ref VpcSecurityGroupIds
SecurityGroupIds:
- !GetAtt phpserverSecurityGroup.GroupId
SubnetIds: !Ref VpcSubnetIds

phpserverSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: >-
Security group for lambda
VpcId: !Ref VpcId

phpserverSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
DependsOn: phpserverSecurityGroup
Properties:
GroupId: !Ref phpserverSecurityGroup
IpProtocol: '-1'
SourceSecurityGroupId: !Ref phpserverSecurityGroup

restapi:
Type: AWS::Serverless::Api
DeletionPolicy: "Retain"
Expand All @@ -78,37 +125,29 @@ Resources:
DistributionConfig:
Origins:
- Id: PhpServer
DomainName: !Join
- ''
- - !Ref restapi
- '.execute-api.'
- !Ref AWS::Region
- '.amazonaws.com'
DomainName: !Sub "${restapi}.execute-api.${AWS::Region}.amazonaws.com"
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: https-only
OriginPath: !Join ['', ['/', !Ref StageName]]
OriginPath: !Sub "/${StageName}"

- Id: Assets
DomainName: !Join
- ""
- - !Ref assetsS3
- ".s3-"
- !Ref AWS::Region
- ".amazonaws.com"
DomainName: !Sub "${assetsS3}.s3-${AWS::Region}.amazonaws.com"
S3OriginConfig:
OriginAccessIdentity: !Join [ '', [ 'origin-access-identity/cloudfront/', !Ref cfOriginAccessIdentity ]]
OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${cfOriginAccessIdentity}"

Enabled: 'true'
IPV6Enabled: 'true'
HttpVersion: 'http2'

PriceClass: !Ref CloudFrontPriceClass

CustomErrorResponses:
- ErrorCode: 404
ErrorCachingMinTTL: 0
- ErrorCode: 504
ErrorCachingMinTTL: 0

DefaultCacheBehavior:
AllowedMethods:
Expand Down Expand Up @@ -164,7 +203,7 @@ Resources:
Forward: 'none'
TargetOriginId: Assets
ViewerProtocolPolicy: redirect-to-https

- PathPattern: "*.jpeg"
AllowedMethods:
- GET
Expand Down Expand Up @@ -249,7 +288,7 @@ Resources:

assetsS3:
Type: AWS::S3::Bucket

assetsS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Expand All @@ -259,35 +298,60 @@ Resources:
- Sid: CloudFrontOrigin
Effect: Allow
Principal:
CanonicalUser: !GetAtt cfOriginAccessIdentity.S3CanonicalUserId
CanonicalUser: !Sub "${cfOriginAccessIdentity.S3CanonicalUserId}"
Action:
- s3:GetObject
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref assetsS3
- '/*'
Resource: !Sub "${assetsS3.Arn}/*"

cfOriginAccessIdentity:
Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
Properties:
CloudFrontOriginAccessIdentityConfig:
Comment: The Origin Access Identity to allow CloudFront to serve static files from the assets bucket (WordPress on Lambda)

dbSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Open database for access
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '3306'
ToPort: '3306'
SourceSecurityGroupId: !GetAtt phpserverSecurityGroup.GroupId
VpcId: !Ref VpcId

dbCluster:
Type: AWS::RDS::DBCluster
Properties:
DBClusterIdentifier: !Join ["-", [!Ref "AWS::StackName", !Select [0, !Split ["-", !Select [2, !Split ["/", !Ref "AWS::StackId"]]]]]]
DatabaseName: !Ref DBName
MasterUsername: !Ref DBUser
MasterUserPassword: !Ref DBPassword
Engine: aurora
EngineMode: serverless
ScalingConfiguration:
AutoPause: true
MaxCapacity: 16
MinCapacity: 1
SecondsUntilAutoPause: 300
VpcSecurityGroupIds:
- !GetAtt dbSecurityGroup.GroupId

Outputs:
RestApiDomainName:
Value: !Join
- ''
- - !Ref restapi
- '.execute-api.'
- !Ref AWS::Region
- '.amazonaws.com'

Value: !Sub "${restapi}.execute-api.${AWS::Region}.amazonaws.com"

CloudFrontDistributionId:
Value: !Ref cloudfront

CloudFrontDistributionDomainName:
Value: !GetAtt cloudfront.DomainName

AssetsBucketName:
Value: !Ref assetsS3

DatabaseEndpoint:
Value: !GetAtt [dbCluster, Endpoint.Address]

DatabaseName:
Value: !Ref DBName