From 4b84e15747f87da6c75d2fa41074f6deee295aae Mon Sep 17 00:00:00 2001 From: AC Date: Sat, 13 Jul 2024 12:03:43 -0700 Subject: [PATCH] added terraform templates and bootstrap cdk templates --- .github/workflows/aws-bootstrap-template.yml | 134 ++++++++++++++++++ .github/workflows/aws-template-terraform.yml | 108 ++++++++++++++ .github/workflows/deploy-infra-sandbox.yml | 18 +++ Makefile | 16 +++ .../cloud/environments/dev/.gitkeep | 0 .../cloud/environments/sandbox/.gitkeep | 0 .../cloud/environments/sandbox/providers.tf | 28 ++++ .../cloud/environments/sandbox/variables.tf | 6 + .../cloud/environments/sandbox/webapp.tf | 22 +++ .../cloud/environments/test/.gitkeep | 0 infrastructure/cloud/modules/compute/.gitkeep | 0 infrastructure/cloud/modules/security/kms.tf | 20 +++ .../cloud/modules/security/outputs.tf | 4 + .../cloud/modules/security/variables.tf | 19 +++ .../cloud/modules/storage/s3buckets.tf | 42 ++++++ .../cloud/modules/storage/variables.tf | 23 +++ .../jasper-aws-bootstrap/.gitignore | 8 ++ .../jasper-aws-bootstrap/.npmignore | 6 + infrastructure/jasper-aws-bootstrap/README.md | 14 ++ .../bin/jasper-aws-bootstrap.ts | 24 ++++ infrastructure/jasper-aws-bootstrap/cdk.json | 72 ++++++++++ .../jasper-aws-bootstrap/jest.config.js | 8 ++ .../lib/jasper-aws-bootstrap-stack.ts | 59 ++++++++ .../jasper-aws-bootstrap/package.json | 27 ++++ .../test/jasper-aws-bootstrap.test.ts | 43 ++++++ .../jasper-aws-bootstrap/tsconfig.json | 31 ++++ 26 files changed, 732 insertions(+) create mode 100644 .github/workflows/aws-bootstrap-template.yml create mode 100644 .github/workflows/aws-template-terraform.yml create mode 100644 .github/workflows/deploy-infra-sandbox.yml create mode 100644 Makefile create mode 100644 infrastructure/cloud/environments/dev/.gitkeep create mode 100644 infrastructure/cloud/environments/sandbox/.gitkeep create mode 100644 infrastructure/cloud/environments/sandbox/providers.tf create mode 100644 infrastructure/cloud/environments/sandbox/variables.tf create mode 100644 infrastructure/cloud/environments/sandbox/webapp.tf create mode 100644 infrastructure/cloud/environments/test/.gitkeep create mode 100644 infrastructure/cloud/modules/compute/.gitkeep create mode 100644 infrastructure/cloud/modules/security/kms.tf create mode 100644 infrastructure/cloud/modules/security/outputs.tf create mode 100644 infrastructure/cloud/modules/security/variables.tf create mode 100644 infrastructure/cloud/modules/storage/s3buckets.tf create mode 100644 infrastructure/cloud/modules/storage/variables.tf create mode 100644 infrastructure/jasper-aws-bootstrap/.gitignore create mode 100644 infrastructure/jasper-aws-bootstrap/.npmignore create mode 100644 infrastructure/jasper-aws-bootstrap/README.md create mode 100644 infrastructure/jasper-aws-bootstrap/bin/jasper-aws-bootstrap.ts create mode 100644 infrastructure/jasper-aws-bootstrap/cdk.json create mode 100644 infrastructure/jasper-aws-bootstrap/jest.config.js create mode 100644 infrastructure/jasper-aws-bootstrap/lib/jasper-aws-bootstrap-stack.ts create mode 100644 infrastructure/jasper-aws-bootstrap/package.json create mode 100644 infrastructure/jasper-aws-bootstrap/test/jasper-aws-bootstrap.test.ts create mode 100644 infrastructure/jasper-aws-bootstrap/tsconfig.json diff --git a/.github/workflows/aws-bootstrap-template.yml b/.github/workflows/aws-bootstrap-template.yml new file mode 100644 index 00000000..d711131a --- /dev/null +++ b/.github/workflows/aws-bootstrap-template.yml @@ -0,0 +1,134 @@ +name: AWS Bootstrap Workflow + +on: + workflow_call: + inputs: + CONTEXT_FOLDER: + required: true + type: string + ENVIRONMENT_NAME: + required: true + type: string + TOOLKIT_STACK_NAME: + required: true + type: string + QUALIFIER: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + check_changes: + name: Check Changes + runs-on: ubuntu-20.04 + outputs: + infra_changed: ${{ steps.check_changes.outputs.infra_changed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + - name: Check modified folders + id: check_changes + env: + CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} + CHANGE_FOLDER_NAME: ${{ inputs.CHANGE_FOLDER_NAME }} + run: | + echo "=============== list modified files ===============" + git diff --name-only HEAD^ HEAD + + echo "========== check paths of modified files ==========" + git diff --name-only HEAD^ HEAD >> files.txt + infra_changed=false + while IFS= read -r file + do + echo $file + if [[ $file == $CHANGE_FOLDER_NAME/* ]]; then + infra_changed=true + break + fi + done < files.txt + echo "infra_changed=$infra_changed" >> "$GITHUB_OUTPUT" + + + + synth_deploy_state_components: + name: Synth and Deploy Terraform State Components + runs-on: ubuntu-20.04 + environment: ${{ inputs.ENVIRONMENT_NAME }} + needs: [check_changes, build_push_api_auth_lambda, build_push_cdc_events_lambda, build_push_cdc_auth_lambda] + # if: needs.check_changes.outputs.infra_changed == 'true' + steps: + - uses: actions/checkout@v4 + + - name: Get AWS Account ID + run: echo "CDK_DEFAULT_ACCOUNT=${{ vars.AWS_ACCOUNT }}" >> $GITHUB_ENV + + - name: Get AWS Region + run: echo "CDK_DEFAULT_REGION=${{ vars.AWS_REGION }}" >> $GITHUB_ENV + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-skip-session-tagging: true + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + role-duration-seconds: 1800 + role-session-name: ci-deployment + + + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.12.2' + + - name: Install NPM Modules + run: "npm config set engine-strict true && npm ci" + working-directory: ${{ inputs.CONTEXT_FOLDER }}/phsa-plms-api-svc-infra + + - name: Build + run: "npm run build" + working-directory: ${{ inputs.CONTEXT_FOLDER }}/phsa-plms-api-svc-infra + + - name: Install AWS CDK + run: "npm install -g aws-cdk@2.85.0" + + - name: CDK Bootstrap + env: + TOOLKIT_STACK_NAME: ${{ inputs.TOOLKIT_STACK_NAME }} + QUALIFIER: ${{ inputs.QUALIFIER }} + BRANCH_NAME: ${{ inputs.BRANCH_NAME }} + ENV_NAME: ${{ inputs.ENVIRONMENT_NAME }} + run: | + echo "Running CDK Bootstrap" + npx cdk bootstrap --toolkit-stack-name $TOOLKIT_STACK_NAME --qualifier $QUALIFIER --context branch-name=$BRANCH_NAME + working-directory: ${{ inputs.CONTEXT_FOLDER }} + + - name: CDK Synth + env: + TOOLKIT_STACK_NAME: ${{ inputs.TOOLKIT_STACK_NAME }} + QUALIFIER: ${{ inputs.QUALIFIER }} + BRANCH_NAME: ${{ inputs.BRANCH_NAME }} + ENV_NAME: ${{ inputs.BRANCH_NAME }} + run: | + echo "Running CDK Synth" + npx cdk synth --toolkit-stack-name $TOOLKIT_STACK_NAME --qualifier $QUALIFIER --context branch-name=$BRANCH_NAME + working-directory: ${{ inputs.CONTEXT_FOLDER }} + + + + - name: CDK Deploy + id: cdk_deploy + env: + TOOLKIT_STACK_NAME: ${{ inputs.TOOLKIT_STACK_NAME }} + QUALIFIER: ${{ inputs.QUALIFIER }} + BRANCH_NAME: ${{ inputs.BRANCH_NAME }} + ENV_NAME: ${{ inputs.BRANCH_NAME }} + run: | + npx cdk deploy --toolkit-stack-name $TOOLKIT_STACK_NAME --qualifier $QUALIFIER --require-approval never --all + working-directory: ${{ inputs.CONTEXT_FOLDER }} + + \ No newline at end of file diff --git a/.github/workflows/aws-template-terraform.yml b/.github/workflows/aws-template-terraform.yml new file mode 100644 index 00000000..574e1987 --- /dev/null +++ b/.github/workflows/aws-template-terraform.yml @@ -0,0 +1,108 @@ +name: AWS Bootstrap Workflow + +on: + workflow_call: + inputs: + CONTEXT_FOLDER: + required: true + type: string + ENVIRONMENT_NAME: + required: true + type: string + CHANGE_FOLDER_NAME: + required: true + type: string + TEST_BUCKET_NAME: + required: true + type: string + +permissions: + id-token: write + contents: read + +jobs: + check_changes: + name: Check Changes + runs-on: ubuntu-20.04 + outputs: + infra_changed: ${{ steps.check_changes.outputs.infra_changed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + - name: Check modified folders + id: check_changes + env: + CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} + CHANGE_FOLDER_NAME: ${{ inputs.CHANGE_FOLDER_NAME }} + run: | + echo "=============== list modified files ===============" + git diff --name-only HEAD^ HEAD + + echo "========== check paths of modified files ==========" + git diff --name-only HEAD^ HEAD >> files.txt + infra_changed=false + while IFS= read -r file + do + echo $file + if [[ $file == $CHANGE_FOLDER_NAME/* ]]; then + infra_changed=true + break + fi + done < files.txt + echo "infra_changed=$infra_changed" >> "$GITHUB_OUTPUT" + + deploy_infra: + name: Deploy Infra + runs-on: ubuntu-latest + environment: + name: ${{ inputs.ENVIRONMENT_NAME }} + needs: [check_changes] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-skip-session-tagging: true + aws-region: ${{ vars.AWS_REGION }} + role-to-assume: ${{ vars.AWS_ROLE_ARN }} + role-duration-seconds: 1800 + role-session-name: ci-deployment + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.9.0 + - name: Terraform Init + id: init + env: + CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} + run: | + terraform init -input=false + working-directory: ${{ inputs.CONTEXT_FOLDER }} + - name: Terraform Plan + id: plan + env: + CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} + TF_VAR_test_s3_bucket_name: ${{ inputs.TEST_BUCKET_NAME }} + run: | + terraform plan -no-color -input=false + continue-on-error: true + working-directory: ${{ inputs.CONTEXT_FOLDER }} + - name: Terraform Plan Status + if: steps.plan.outcome == 'failure' + run: exit 1 + - name: Terraform Apply + env: + CONTEXT_FOLDER: ${{ inputs.CONTEXT_FOLDER }} + TF_VAR_test_s3_bucket_name: ${{ inputs.TEST_BUCKET_NAME }} + run: | + terraform apply --auto-approve -input=false + working-directory: ${{ inputs.CONTEXT_FOLDER }} + + + + + \ No newline at end of file diff --git a/.github/workflows/deploy-infra-sandbox.yml b/.github/workflows/deploy-infra-sandbox.yml new file mode 100644 index 00000000..c9db5dc7 --- /dev/null +++ b/.github/workflows/deploy-infra-sandbox.yml @@ -0,0 +1,18 @@ +name: Deploy AWS Infra to Sandbox + +on: + push: + branches: [feature-addAWSsetup] + workflow_dispatch: + +jobs: + infrastructure_deploy_snd: + uses: ./.github/workflows/aws-template-terraform.yml + with: + CONTEXT_FOLDER: ./infrastructure/cloud/environments/sandbox + CHANGE_FOLDER_NAME: environments/sandbox + ENVIRONMENT_NAME: sandbox + TEST_BUCKET_NAME: jasper-test-bucket + secrets: inherit + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..ac93f1bb --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +#!make + + +export AWS_PROFILE ?= jasperlocal +export AWS_DEFAULT_REGION ?= ca-central-1 +export AWS_ACCOUNT ?= 381491824201 +export TOOLKIT_STACK_NAME= CDK-Bootstrap-jasper-dev +export QUALIFIER= jasperdev +export BRANCH_NAME= dev +export ENV_NAME= dev + + + +run-bootstrap-jasper: + @echo "Running bootstrap" + @cd infrastructure/jasper-aws-bootstrap && cdk bootstrap aws://$(AWS_ACCOUNT)/$(AWS_DEFAULT_REGION) --toolkit-stack-name $TOOLKIT_STACK_NAME --qualifier $QUALIFIER --profile $(AWS_PROFILE) \ No newline at end of file diff --git a/infrastructure/cloud/environments/dev/.gitkeep b/infrastructure/cloud/environments/dev/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/environments/sandbox/.gitkeep b/infrastructure/cloud/environments/sandbox/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/environments/sandbox/providers.tf b/infrastructure/cloud/environments/sandbox/providers.tf new file mode 100644 index 00000000..4d740a4b --- /dev/null +++ b/infrastructure/cloud/environments/sandbox/providers.tf @@ -0,0 +1,28 @@ +terraform { + required_version = "~> 1.9.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + + tls = { + source = "hashicorp/tls" + version = "4.0.5" + } + } + + backend "s3" { + bucket = "terraform-remote-state-sandbox-12345" + key = "terraform.tfstate" + region = "ca-central-1" + dynamodb_table = "terraform-remote-state-lock-12345" + } + +} + + + +provider "aws" { + region = "ca-central-1" +} \ No newline at end of file diff --git a/infrastructure/cloud/environments/sandbox/variables.tf b/infrastructure/cloud/environments/sandbox/variables.tf new file mode 100644 index 00000000..19abccf5 --- /dev/null +++ b/infrastructure/cloud/environments/sandbox/variables.tf @@ -0,0 +1,6 @@ + + +variable test_s3_bucket_name { + type = string + description = "The name of the S3 bucket to create for testing" +} \ No newline at end of file diff --git a/infrastructure/cloud/environments/sandbox/webapp.tf b/infrastructure/cloud/environments/sandbox/webapp.tf new file mode 100644 index 00000000..76b18928 --- /dev/null +++ b/infrastructure/cloud/environments/sandbox/webapp.tf @@ -0,0 +1,22 @@ + + +locals { + environment = "snd" + application_name = "jasper-aws" +} + +module "security" { + source = "../../../modules/security" + environment = local.environment + application_name = local.application_name + kms_key_name = "jasper-kms-key" + +} + +module "storage" { + source = "../../../modules/storage" + environment = local.environment + application_name = local.application_name + kms_key_name = module.security.kms_key_alias + test_s3_bucket_name = var.test_s3_bucket_name +} \ No newline at end of file diff --git a/infrastructure/cloud/environments/test/.gitkeep b/infrastructure/cloud/environments/test/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/modules/compute/.gitkeep b/infrastructure/cloud/modules/compute/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/cloud/modules/security/kms.tf b/infrastructure/cloud/modules/security/kms.tf new file mode 100644 index 00000000..1acf6bb1 --- /dev/null +++ b/infrastructure/cloud/modules/security/kms.tf @@ -0,0 +1,20 @@ +data "aws_caller_identity" "current" {} + +# kms key for encryption +resource "aws_kms_key" "kms_key" { + description = "KMS key for encryption" + deletion_window_in_days = 10 + enable_key_rotation = true + is_enabled = true + # policy = data.aws_iam_policy_document.kms_policy.json + tags = { + Application="${var.application_name}-${var.environment}" + Name="${var.kms_key_name}-${var.environment}" + Environment="${var.environment}" + } +} + +resource "aws_kms_alias" "kms_alias" { + name = "alias/${var.kms_key_name}-${var.environment}" + target_key_id = aws_kms_key.kms_key.key_id +} diff --git a/infrastructure/cloud/modules/security/outputs.tf b/infrastructure/cloud/modules/security/outputs.tf new file mode 100644 index 00000000..5a0cf3dd --- /dev/null +++ b/infrastructure/cloud/modules/security/outputs.tf @@ -0,0 +1,4 @@ + +output kms_key_alias { + value = aws_kms_alias.kms_alias.name +} \ No newline at end of file diff --git a/infrastructure/cloud/modules/security/variables.tf b/infrastructure/cloud/modules/security/variables.tf new file mode 100644 index 00000000..d01e7022 --- /dev/null +++ b/infrastructure/cloud/modules/security/variables.tf @@ -0,0 +1,19 @@ + + +variable application_name { + type = string + description = "The name of the application" + default = "bcgov-jasper-aws-bootstrap" +} + +variable environment { + type = string + description = "The environment to deploy the application to" + default = "dev" +} + +variable kms_key_name { + type = string + description = "The name of the KMS key to create" + default = "jasper-kms-key" +} \ No newline at end of file diff --git a/infrastructure/cloud/modules/storage/s3buckets.tf b/infrastructure/cloud/modules/storage/s3buckets.tf new file mode 100644 index 00000000..3b52c52c --- /dev/null +++ b/infrastructure/cloud/modules/storage/s3buckets.tf @@ -0,0 +1,42 @@ + +data "aws_caller_identity" "current" {} + +data "aws_kms_alias" "encryption_key_alias" { + name = var.kms_key_name +} + +# a test s3 bucket +resource "aws_s3_bucket" "test_s3_bucket" { + bucket = "${var.test_s3_bucket_name}-${var.environment}" + + tags = { + Application="${var.application_name}-${var.environment}" + Name="${var.test_s3_bucket_name}-${var.environment}" + Environment="${var.environment}" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "test_bucket_encryption" { + bucket = aws_s3_bucket.test_s3_bucket.id + + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = data.aws_kms_alias.encryption_key_alias.target_key_id + sse_algorithm = "aws:kms" + } + } +} + +resource "aws_s3_bucket_ownership_controls" "test_bucket_ownership_controls" { + bucket = aws_s3_bucket.test_s3_bucket.id + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +resource "aws_s3_bucket_acl" "test_bucket_acl" { + depends_on = [aws_s3_bucket_ownership_controls.test_bucket_ownership_controls] + + bucket = aws_s3_bucket.test_s3_bucket.id + acl = "private" +} diff --git a/infrastructure/cloud/modules/storage/variables.tf b/infrastructure/cloud/modules/storage/variables.tf new file mode 100644 index 00000000..d861ae30 --- /dev/null +++ b/infrastructure/cloud/modules/storage/variables.tf @@ -0,0 +1,23 @@ + +variable test_s3_bucket_name { + type = string + description = "The name of the S3 bucket to create for testing" +} + +variable application_name { + type = string + description = "The name of the application" + default = "bcgov-jasper-aws-bootstrap" +} + +variable environment { + type = string + description = "The environment to deploy the application to" + default = "dev" +} + +variable kms_key_name { + type = string + description = "The name of the KMS key" + default = "jasper-kms-key" +} \ No newline at end of file diff --git a/infrastructure/jasper-aws-bootstrap/.gitignore b/infrastructure/jasper-aws-bootstrap/.gitignore new file mode 100644 index 00000000..f60797b6 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/infrastructure/jasper-aws-bootstrap/.npmignore b/infrastructure/jasper-aws-bootstrap/.npmignore new file mode 100644 index 00000000..c1d6d45d --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/infrastructure/jasper-aws-bootstrap/README.md b/infrastructure/jasper-aws-bootstrap/README.md new file mode 100644 index 00000000..9315fe5b --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/README.md @@ -0,0 +1,14 @@ +# Welcome to your CDK TypeScript project + +This is a blank project for CDK development with TypeScript. + +The `cdk.json` file tells the CDK Toolkit how to execute your app. + +## Useful commands + +* `npm run build` compile typescript to js +* `npm run watch` watch for changes and compile +* `npm run test` perform the jest unit tests +* `npx cdk deploy` deploy this stack to your default AWS account/region +* `npx cdk diff` compare deployed stack with current state +* `npx cdk synth` emits the synthesized CloudFormation template diff --git a/infrastructure/jasper-aws-bootstrap/bin/jasper-aws-bootstrap.ts b/infrastructure/jasper-aws-bootstrap/bin/jasper-aws-bootstrap.ts new file mode 100644 index 00000000..4e1e1a2e --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/bin/jasper-aws-bootstrap.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { JasperAwsBootstrapStack } from '../lib/jasper-aws-bootstrap-stack'; + + +const appName = 'bcgov-jasper-aws-bootstrap'; +const branch=process.env.ENV_NAME || 'dev'; +const namespace = `${appName}-${branch}`; + + + +const app = new cdk.App(); +cdk.Tags.of(app).add('Application', namespace); + + + +new JasperAwsBootstrapStack(app, namespace, { + + env: { + account: process.env.CDK_DEFAULT_ACCOUNT, + region: process.env.CDK_DEFAULT_REGION +} +}); \ No newline at end of file diff --git a/infrastructure/jasper-aws-bootstrap/cdk.json b/infrastructure/jasper-aws-bootstrap/cdk.json new file mode 100644 index 00000000..f6a6e632 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/cdk.json @@ -0,0 +1,72 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/jasper-aws-bootstrap.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "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, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false + } +} diff --git a/infrastructure/jasper-aws-bootstrap/jest.config.js b/infrastructure/jasper-aws-bootstrap/jest.config.js new file mode 100644 index 00000000..08263b89 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/infrastructure/jasper-aws-bootstrap/lib/jasper-aws-bootstrap-stack.ts b/infrastructure/jasper-aws-bootstrap/lib/jasper-aws-bootstrap-stack.ts new file mode 100644 index 00000000..5f900276 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/lib/jasper-aws-bootstrap-stack.ts @@ -0,0 +1,59 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +// import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as ddb from 'aws-cdk-lib/aws-dynamodb'; + +export class JasperAwsBootstrapStack extends cdk.Stack { + stateBucket: s3.Bucket; + stateLockTable: ddb.Table; + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const namespace = this.stackName; + const env = process.env.ENV_NAME || 'dev'; + + let removalPolicy = cdk.RemovalPolicy.DESTROY; + let s3BucketAutoDel: boolean = true; + if (env == 'prod') { + removalPolicy = cdk.RemovalPolicy.RETAIN; + s3BucketAutoDel = false; + } + + const kmsCmkey = new cdk.aws_kms.Key(this, `${namespace}-state-KMSKey`, { + description: `KMS CMK for ${namespace} state`, + enableKeyRotation: true, + alias: `${namespace}-cmk-state-key`, + removalPolicy: removalPolicy, + }); + cdk.Tags.of(kmsCmkey).add('Name', `${namespace}-state-KMSKey`); + const kmsCmkState = kmsCmkey.addAlias(namespace + "-state-alias-key") + + + // create s3 bucket for state + const s3BucketName = `${namespace}-state`; + this.stateBucket = new s3.Bucket(this, s3BucketName, { + versioned: true, + removalPolicy: removalPolicy, + bucketName: s3BucketName, + autoDeleteObjects: s3BucketAutoDel, + encryption: s3.BucketEncryption.KMS, + encryptionKey: kmsCmkState, + objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED, + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + }); + + // create dynamodb table for state lock + const stateLockTableName = `${namespace}-state-table`; + this.stateLockTable = new ddb.Table(this, stateLockTableName, { + tableName: stateLockTableName, + partitionKey: { name: 'LockID', type: ddb.AttributeType.STRING }, + removalPolicy: cdk.RemovalPolicy.RETAIN_ON_UPDATE_OR_DELETE, + billingMode: ddb.BillingMode.PAY_PER_REQUEST, + encryption: ddb.TableEncryption.CUSTOMER_MANAGED, + encryptionKey:kmsCmkey, + }); + + cdk.Tags.of(this.stateLockTable).add('Name', stateLockTableName); + } +} diff --git a/infrastructure/jasper-aws-bootstrap/package.json b/infrastructure/jasper-aws-bootstrap/package.json new file mode 100644 index 00000000..b6aa41f4 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/package.json @@ -0,0 +1,27 @@ +{ + "name": "jasper-aws-bootstrap", + "version": "0.1.0", + "bin": { + "jasper-aws-bootstrap": "bin/jasper-aws-bootstrap.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "20.14.2", + "jest": "^29.7.0", + "ts-jest": "^29.1.4", + "aws-cdk": "2.146.0", + "ts-node": "^10.9.2", + "typescript": "~5.4.5" + }, + "dependencies": { + "aws-cdk-lib": "2.146.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} \ No newline at end of file diff --git a/infrastructure/jasper-aws-bootstrap/test/jasper-aws-bootstrap.test.ts b/infrastructure/jasper-aws-bootstrap/test/jasper-aws-bootstrap.test.ts new file mode 100644 index 00000000..1a8c7312 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/test/jasper-aws-bootstrap.test.ts @@ -0,0 +1,43 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template,Match } from 'aws-cdk-lib/assertions'; +import * as JasperAwsBootstrap from '../lib/jasper-aws-bootstrap-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/jasper-aws-bootstrap-stack.ts +test('State S3 bucket created', () => { + const app = new cdk.App(); + // WHEN + const stack = new JasperAwsBootstrap.JasperAwsBootstrapStack(app, 'jasper-bootstrap-dev', { + env: { + account: '123456789012', + region: 'ca-central-1' + } + + }); + // THEN + + const template = Template.fromStack(stack); + + template.resourceCountIs('AWS::S3::Bucket', 1); + + +}); + +// test dynamodb table created +test('State Lock Table Created', () => { + const app = new cdk.App(); + // WHEN + const stack = new JasperAwsBootstrap.JasperAwsBootstrapStack(app, 'jasper-bootstrap-dev', { + env: { + account: '123456789012', + region: 'ca-central-1' + } + + }); + // THEN + + const template = Template.fromStack(stack); + + template.resourceCountIs('AWS::DynamoDB::Table', 1); + +}); diff --git a/infrastructure/jasper-aws-bootstrap/tsconfig.json b/infrastructure/jasper-aws-bootstrap/tsconfig.json new file mode 100644 index 00000000..aaa7dc51 --- /dev/null +++ b/infrastructure/jasper-aws-bootstrap/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +}