An AWS serverless web app for starting/stopping EC2 instances.
EC2 controller uses the following technologies:
- Authentication and Authorization: Cognito
- Static web content: Cloudfront and S3
- Frontend: React and AWS Amplify
- Backend: API-Gateway (REST API) and Lambda
- Storage: DynamoDb
- Deployment: CloudFormation / CDK
- Backend framework: Java 21 and Micronaut with SnapStart
To deploy this in your AWS account you will need the following:
- A region where you want to deploy, e.g.
eu-west-1
- A Route53 hosted zone, e.g.
example.com.
and its IDXXXXXXXXXXXXXX
- A subdomain of
example.com
you want to use for the web app, e.g.ec2-controller.example.com
- An ACM certificate for
ec2-controller.example.com
or*.example.com
inus-east-1
and its ARNarn:aws:acm:us-east-1:000000000000:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
On your local machine you will need the following:
- Node.js 22
- Java Development Kit (JDK) 21 (e.g. from Adoptium)
- AWS Command line interface
- Configure AWS credentials for the AWS CLI by calling
aws configure
- Configure AWS credentials for the AWS CLI by calling
cd backend
./gradlew clean build
The backend will be deployed later when we deploy the infrastructure.
To update the backend after making changes, first build it ./gradlew build
, then deploy the infrastructure with npm run cdk deploy
.
Create file infrastructure/infrastructure-config.ts
based on the following template and fill in your configuration:
import { InfrastructureConfig } from "./lib/infrastructure-config-interface";
export const CONFIG: InfrastructureConfig = {
region: "eu-west-1",
domain: "ec2-controller.example.com",
hostedZoneName: "example.com.",
hostedZoneId: "XXXXXXXXXXXXXX",
sslCertificateArn: "arn:aws:acm:us-east-1:000000000000:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
stackName: "ec2-controller",
contactEmailAddress: "admin@example.com"
};
cd infrastructure
npm run cdk deploy
This command will take up to 30 minutes. At the end it will output information you need for configuring the frontend in the next step.
Create file frontend/src/frontend-config.ts
based on the following template and fill in the values for your deployed stack:
import { FrontendConfig } from "./environment";
export const CONFIG: FrontendConfig = {
region: "eu-west-1",
apiGatewayEndpointUrl: "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/prod",
cognitoIdentityPoolId: "eu-west-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
cognitoUserPoolId: "eu-west-1_xxxxxxxxx",
cognitoUserPoolWebClientId: "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};
Create file frontend/deploy/deploy-config.js
based on the following template and fill in the values for your deployed stack:
exports.CONFIG = {
staticWebsiteBucket: 'ec2-controller-staticcontentbucketxxxxxxxxxxxxxxxxxxxxxx',
cloudfrontDistributionId: 'XXXXXXXXXXXXXX'
}
cd frontend
npm run deploy
Add entries to the DynamoDB table for instances that you want to control:
{
"id": "i-xxxxxxxxxxxxxxxxx",
"controlAllowed": true,
"domain": "instance.example.com",
"sortOrder": 1
}
id
: AWS EC2 instance ID.controlAllowed
: Allow starting/stopping this instance.domain
: Domain name for this instance. Used to check if DNS entry is in sync.sortOrder
: Specify order in which to display entries in the web app.
Go to the AWS Cognito console and create users for your new web app. Don't forget to add them to group Users
.
Configure environment variables for local server: create file backend/local-server-env.properties
based on the following template and fill in the values for your deployed stack:
TABLENAME_DYNAMODBINSTANCE = ec2-controller-instances-Backend
HOSTED_ZONE_ID = XXXXXXXXXXXXXX
Then start the local server:
cd backend
./gradlew runServer
./gradlew dependencyUpdates
cd backend
./gradlew eclipse
Run local frontend during development:
cd frontend
npm start
npx npm-check-updates -u && npm install
This project requires some configuration files with deployment specific information, e.g. domain names that should not be stored in a public git repository. That's why these files are added to .gitignore
. If you want to still keep your configuration under version control you can do so in a private branch (e.g. private-main
) that you could push to a private repository only.
When switching from private-main
to the public main
branch, git will delete the configuration files. To restore them you can use the following command:
git show private-main:frontend/deploy/deploy-config.js > frontend/deploy/deploy-config.js \
&& git show private-main:frontend/src/frontend-config.ts > frontend/src/frontend-config.ts \
&& git show private-main:backend/local-server-env.properties > backend/local-server-env.properties \
&& git show private-main:infrastructure/infrastructure-config.ts > infrastructure/infrastructure-config.ts
Cannot find file './frontend-config' in '.\src'.
Create frontend/src/frontend-config.ts
as described above.
Add an entry for your instance to the DynamoDB table and set controlAllowed
to true
.
Configure project using gradle and refresh project in Eclipse:
cd backend && ./gradlew eclipse
Configure project using gradle and refresh project in Eclipse:
cd backend && ./gradlew eclipse
Make sure your Cognito user has role Users
.
Creating an account on the login page fails with message SignUp is not permitted for this user pool
.
Registering of new users is deactivated for the Cognito user pool. You can change this by setting allowAdminCreateUserOnly
to false
in cognito.ts
.
The backend uses Java Lambdas which need some time for initialization when inactive for araound 15 minutes (cold start).
Solution: rewrite Lambda code e.g. in TypeScript.