Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kaklakariada/aws-ec2-controller
An AWS serverless web app for starting/stopping EC2 instances.
https://github.com/kaklakariada/aws-ec2-controller
amazon-web-services aws ec2 serverless webapp
Last synced: 3 months ago
JSON representation
An AWS serverless web app for starting/stopping EC2 instances.
- Host: GitHub
- URL: https://github.com/kaklakariada/aws-ec2-controller
- Owner: kaklakariada
- License: gpl-3.0
- Created: 2020-02-16T10:59:29.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2024-05-11T17:49:50.000Z (8 months ago)
- Last Synced: 2024-05-12T10:34:35.792Z (8 months ago)
- Topics: amazon-web-services, aws, ec2, serverless, webapp
- Language: TypeScript
- Homepage:
- Size: 6.71 MB
- Stars: 4
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# EC2 Controller
An AWS serverless web app for starting/stopping EC2 instances.
[![Build](https://github.com/kaklakariada/aws-ec2-controller/actions/workflows/build.yml/badge.svg)](https://github.com/kaklakariada/aws-ec2-controller/actions/workflows/build.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=aws-ec2-controller-backend&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=aws-ec2-controller-backend)## Architecture
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![Architecture](arch/architecture.png "Architecture")
## Deployment to AWS
### Preconitions
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 ID `XXXXXXXXXXXXXX`
* 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` in `us-east-1` and its ARN `arn:aws:acm:us-east-1:000000000000:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`On your local machine you will need the following:
* [Node.js](https://nodejs.org/en/) 22
* Java Development Kit (JDK) 21 (e.g. from [Adoptium](https://adoptium.net/?variant=openjdk21&jvmVariant=hotspot))
* [AWS Command line interface](https://aws.amazon.com/cli/)
* Configure AWS credentials for the AWS CLI by calling `aws configure`### Build backend
```sh
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`.
### Configure infrastructure
Create file `infrastructure/infrastructure-config.ts` based on the following template and fill in your configuration:
```typescript
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: "[email protected]"
};
```### Deploy infrastructure and backend
```sh
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.
### Configure frontend
Create file `frontend/src/frontend-config.ts` based on the following template and fill in the values for your deployed stack:
```typescript
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:
```javascript
exports.CONFIG = {
staticWebsiteBucket: 'ec2-controller-staticcontentbucketxxxxxxxxxxxxxxxxxxxxxx',
cloudfrontDistributionId: 'XXXXXXXXXXXXXX'
}
```### Deploy frontend
```sh
cd frontend
npm run deploy
```### Configure backend
#### Add EC2 instances to whitelist
Add entries to the DynamoDB table for instances that you want to control:
```json
{
"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.#### Create Cognito users
Go to the AWS Cognito console and create users for your new web app. Don't forget to add them to group `Users`.
## Development
### Backend
#### Run local backend server
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:
```properties
TABLENAME_DYNAMODBINSTANCE = ec2-controller-instances-Backend
HOSTED_ZONE_ID = XXXXXXXXXXXXXX
```Then start the local server:
```sh
cd backend
./gradlew runServer
```#### Check for dependency updates
```sh
./gradlew dependencyUpdates
```#### Configure eclipse project
```sh
cd backend
./gradlew eclipse
```### Frontend
Run local frontend during development:
```sh
cd frontend
npm start
```### Upgrade dependencies in `package.json`
```sh
npx npm-check-updates -u && npm install
```### Managing configuration in a private branch
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:
```sh
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
```### Troubleshooting
#### Building frontend fails because `./frontend-config` is not found
```.\src\environment.ts
Cannot find file './frontend-config' in '.\src'.
```Create `frontend/src/frontend-config.ts` as described above.
#### There is no "Run" button for my EC2 instance in the web app
Add an entry for your instance to the DynamoDB table and set `controlAllowed` to `true`.
#### Local server returns `404` when running in Eclipse
Configure project using gradle and refresh project in Eclipse:
```sh
cd backend && ./gradlew eclipse
```#### Unit test fail with a `NullPointerException` when running in Eclipse
Configure project using gradle and refresh project in Eclipse:
```sh
cd backend && ./gradlew eclipse
```#### `GET /instances` returns error 403 Forbidden
Make sure your Cognito user has role `Users`.
#### Creating a new account does not work.
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`.
#### Loading instances sometimes takes up to 10s
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.