Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement CDK deployment #18

Merged
merged 22 commits into from
Nov 29, 2024
Merged
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
Expand Up @@ -21,3 +21,4 @@ __pycache__
# LocalStack

volume/
.idea
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ EXTRA_CORS_ALLOWED_ORIGINS='*' DISABLE_CUSTOM_CORS_APIGATEWAY=1 DISABLE_CUSTOM_C
If you run into specific CORS issues, disable it using a [browser extension](https://webextension.org/listing/access-control.html).

## Local Deployment
### awslocal

To deploy the app locally, run the following command:

Expand All @@ -61,6 +62,22 @@ API Gateway Endpoint: http://localhost:4566/_aws/execute-api/4xu5emxibf/test

Navigate to the CloudFront URL to check out the app. The script would also seed some quiz data and user data to make local testing easier.

### cdk

To deploy the application to AWS, ensure your account is bootstraped via `cdk bootstrap` and then run

```bash
AWS_CMD=aws CDK_CMD=cdk bash ./bin/deploy_cdk.sh
```

### cdklocal

Alternatively the application can be deployed to LocalStack via `cdklocal`, our wrapper around the AWS CDK. Perform the following steps:
1. Bootstrap LocalStack: `cd cdk && cdklocal bootstrap`
2. Deploy the application: `AWS_CMD=awslocal CDK_CMD=cdklocal bash ./bin/deploy_cdk.sh`

_Note: while the core quiz application works with CDK, additional features have not been implemented yet._

## Local Testing

To run an automated test suite against the local deployment, run the following command:
Expand Down
36 changes: 36 additions & 0 deletions bin/deploy_cdk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash

set -euo pipefail

AWS_CMD=${AWS_CMD:-aws}
CDK_CMD=${CDK_CMD:-cdk}

# stub build the frontend code since the CDK stack needs this code to
# synthesise the FrontendStack, but we don't yet know the backend URL to inject
# into the static HTML
if [ ! -d frontend/build ]; then
(cd frontend
echo "REACT_APP_API_ENDPOINT=https://example.com" > .env.local
npx react-scripts build
)
fi

# deploy bulk of the application
(cd cdk
npm run ${CDK_CMD} -- deploy --require-approval never QuizAppStack
)

# get the backend API url
API_URL=$($AWS_CMD cloudformation describe-stacks --stack-name QuizAppStack --query Stacks[0].Outputs[0].OutputValue --output text)
echo "Backend API URL: $API_URL"

# build the frontend code
(cd frontend
echo "REACT_APP_API_ENDPOINT=$API_URL" > .env.local
npx react-scripts build
)

# deploy the frontend stack
(cd cdk
npm run ${CDK_CMD} -- deploy --require-approval never FrontendStack
)
2 changes: 2 additions & 0 deletions cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
cdk.out
31 changes: 31 additions & 0 deletions cdk/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
import os

import aws_cdk as cdk

from quiz_app.frontend_stack import FrontendStack
from quiz_app.quiz_app_stack import QuizAppStack


app = cdk.App()
QuizAppStack(app, "QuizAppStack",
# If you don't specify 'env', this stack will be environment-agnostic.
# Account/Region-dependent features and context lookups will not work,
# but a single synthesized template can be deployed anywhere.

# Uncomment the next line to specialize this stack for the AWS Account
# and Region that are implied by the current CLI configuration.

#env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),

# Uncomment the next line if you know exactly what Account and Region you
# want to deploy the stack to. */

#env=cdk.Environment(account='123456789012', region='us-east-1'),

# For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
)

FrontendStack(app, "FrontendStack")

app.synth()
66 changes: 66 additions & 0 deletions cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"**/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true
}
}
67 changes: 67 additions & 0 deletions cdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "serverless-quiz-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"cdk": "cdk",
"cdklocal": "cdklocal"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"aws-cdk": "^2.171.0",
"aws-cdk-local": "^2.19.0"
}
}
Empty file added cdk/quiz_app/__init__.py
Empty file.
58 changes: 58 additions & 0 deletions cdk/quiz_app/frontend_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import os

import aws_cdk
from aws_cdk import (
Stack,
aws_s3 as s3,
aws_cloudfront as cf,
aws_cloudfront_origins as origins,
aws_s3_deployment as s3deploy,
CfnOutput,
)
from constructs import Construct


class FrontendStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

webapp_bucket = s3.Bucket(
self,
"WebAppBucket",
auto_delete_objects=True,
removal_policy=aws_cdk.RemovalPolicy.DESTROY,
)
origin_access_identity = cf.OriginAccessIdentity(self, "OriginAccessIdentity")
webapp_bucket.grant_read(origin_access_identity)

# deploy process
distribution = cf.Distribution(
self,
"FrontendDistribution",
default_root_object="index.html",
default_behavior=cf.BehaviorOptions(
origin=origins.S3Origin(
webapp_bucket,
origin_access_identity=origin_access_identity,
),
viewer_protocol_policy=cf.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
),
)

s3deploy.BucketDeployment(
self,
"DeployApp",
sources=[
s3deploy.Source.asset(
os.path.join(
os.path.dirname(__file__), "..", "..", "frontend", "build"
)
),
],
destination_bucket=webapp_bucket,
distribution=distribution,
distribution_paths=["/*"],
)

CfnOutput(self, "DistributionDomainName", value=distribution.domain_name)

Loading