diff --git a/.circleci/config.yml b/.circleci/config.yml index bc71204..160485a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -145,7 +145,8 @@ jobs: build-and-test: executor: docker-python steps: - - checkout + - checkout: + method: full - setup_remote_docker - sonarcloud/scan - run: @@ -229,6 +230,31 @@ jobs: steps: - deploy-lambda: stage: "production" + assume-role-pre-production: + executor: docker-python + steps: + - assume-role-and-persist-workspace: + aws-account: $AWS_ACCOUNT_PRE_PRODUCTION + terraform-init-and-plan-pre-production: + executor: docker-terraform + steps: + - terraform-init-then-plan: + environment: "pre-production" + terraform-compliance-pre-production: + executor: docker-terraform + steps: + - terraform-compliance: + environment: "pre-production" + terraform-apply-pre-production: + executor: docker-terraform + steps: + - terraform-apply: + environment: "pre-production" + deploy-to-pre-production: + executor: docker-dotnet + steps: + - deploy-lambda: + stage: "pre-production" workflows: check-and-deploy-development: @@ -360,3 +386,69 @@ workflows: filters: branches: only: release + + deploy-terraform-pre-production: + jobs: + - permit-pre-production-terraform-workflow: + type: approval + filters: + branches: + only: release + - assume-role-pre-production: + context: api-assume-role-housing-pre-production-context + requires: + - permit-pre-production-terraform-workflow + filters: + branches: + only: release + - terraform-init-and-plan-pre-production: + requires: + - assume-role-pre-production + filters: + branches: + only: release + - terraform-compliance-pre-production: + requires: + - terraform-init-and-plan-pre-production + filters: + branches: + only: release + - permit-pre-production-terraform-deployment: + type: approval + requires: + - terraform-compliance-pre-production + filters: + branches: + only: release + - terraform-apply-pre-production: + requires: + - permit-pre-production-terraform-deployment + filters: + branches: + only: release + + deploy-code-pre-production: + jobs: + - build-and-test: + context: + - api-nuget-token-context + - SonarCloud + filters: + branches: + only: release + - assume-role-pre-production: + context: api-assume-role-housing-pre-production-context + requires: + - build-and-test + filters: + branches: + only: release + - deploy-to-pre-production: + context: + - api-nuget-token-context + - "Serverless Framework" + requires: + - assume-role-pre-production + filters: + branches: + only: release diff --git a/NotesApi/NotesApi.csproj b/NotesApi/NotesApi.csproj index 9cf4a66..16ea964 100644 --- a/NotesApi/NotesApi.csproj +++ b/NotesApi/NotesApi.csproj @@ -20,7 +20,7 @@ - + diff --git a/NotesApi/serverless.yml b/NotesApi/serverless.yml index aaf1402..090eced 100644 --- a/NotesApi/serverless.yml +++ b/NotesApi/serverless.yml @@ -132,15 +132,6 @@ resources: - Ref: 'AWS::Region' - Ref: 'AWS::AccountId' - 'log-group:/aws/lambda/*:*:*' - - Effect: "Allow" - Action: - - "s3:PutObject" - - "s3:GetObject" - Resource: - Fn::Join: - - "" - - - "arn:aws:s3:::" - - "Ref": "ServerlessDeploymentBucket" - PolicyName: lambdaInvocation PolicyDocument: Version: '2012-10-17' @@ -151,9 +142,10 @@ resources: Resource: "*" custom: authorizerArns: - development: arn:aws:lambda:eu-west-2:859159924354:function:api-auth-verify-token-new-development-apiauthverifytokennew + development: arn:aws:lambda:eu-west-2:859159924354:function:api-gateway-lambda-authorizer staging: arn:aws:lambda:eu-west-2:715003523189:function:api-auth-verify-token-new-staging-apiauthverifytokennew production: arn:aws:lambda:eu-west-2:153306643385:function:api-auth-verify-token-new-production-apiauthverifytokennew + pre-production: arn:aws:lambda:eu-west-2:578479666894:function:api-auth-verify-token-new-pre-production-apiauthverifytokennew safeguards: - title: Require authorizer safeguard: require-authorizer @@ -165,6 +157,7 @@ custom: development: vpc-0d15f152935c8716f staging: vpc-064521a7a4109ba31 production: vpc-0ce853ddb64e8fb3c + pre-production: vpc-062a957b99c8b12e6 vpc: development: securityGroupIds: @@ -184,3 +177,9 @@ custom: subnetIds: - subnet-06a697d86a9b6ed01 - subnet-0beb266003a56ca82 + pre-production: + securityGroupIds: + - Ref: LambdaSecurityGroup + subnetIds: + - subnet-08aa35159a8706faa + - subnet-0b848c5b14f841dfb diff --git a/terraform/pre-production/aws_ssm_parameter.tf b/terraform/pre-production/aws_ssm_parameter.tf new file mode 100644 index 0000000..a65dfe8 --- /dev/null +++ b/terraform/pre-production/aws_ssm_parameter.tf @@ -0,0 +1,11 @@ +resource "aws_ssm_parameter" "aspnetcore_environment" { + name = "/housing-tl/pre-production/aspnetcore-environment" + type = "String" + value = "to_be_set_manually" + + lifecycle { + ignore_changes = [ + value, + ] + } +} diff --git a/terraform/pre-production/dynamodb.tf b/terraform/pre-production/dynamodb.tf new file mode 100644 index 0000000..6f3cdaa --- /dev/null +++ b/terraform/pre-production/dynamodb.tf @@ -0,0 +1,38 @@ +resource "aws_dynamodb_table" "notesapi_dynamodb_table" { + name = "Notes" + billing_mode = "PROVISIONED" + read_capacity = 10 + write_capacity = 10 + hash_key = "targetId" + range_key = "id" + + attribute { + name = "id" + type = "S" + } + + attribute { + name = "targetId" + type = "S" + } + + attribute { + name = "createdAt" + type = "S" + } + + local_secondary_index { + name = "NotesByCreated" + range_key = "createdAt" + projection_type = "ALL" + } + + tags = merge( + local.default_tags, + { BackupPolicy = "Dev", Backup = false, Confidentiality = "Internal" } + ) + + point_in_time_recovery { + enabled = false + } +} diff --git a/terraform/pre-production/maint.tf b/terraform/pre-production/maint.tf new file mode 100644 index 0000000..1cf1dd3 --- /dev/null +++ b/terraform/pre-production/maint.tf @@ -0,0 +1,46 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} + +provider "aws" { + region = "eu-west-2" +} + +locals { + default_tags = { + Name = "notes-api-${var.environment_name}" + Environment = var.environment_name + terraform-managed = true + project_name = var.project_name + Application = "MTFH Housing Pre-Production" + TeamEmail = "developementteam@hackney.gov.uk" + } +} + +terraform { + backend "s3" { + bucket = "housing-pre-production-terraform-state" + encrypt = true + region = "eu-west-2" + key = "services/notes-api/state" + dynamodb_table = "housing-pre-production-terraform-state-lock" + } +} + +resource "aws_sns_topic" "notes" { + name = "notes.fifo" + fifo_topic = true + content_based_deduplication = true + kms_master_key_id = "alias/aws/sns" +} + +resource "aws_ssm_parameter" "notes_sns_arn" { + name = "/sns-topic/pre-production/notes/arn" + type = "String" + value = aws_sns_topic.notes.arn +} diff --git a/terraform/pre-production/terraform-compliance/dynamodb.feature b/terraform/pre-production/terraform-compliance/dynamodb.feature new file mode 100644 index 0000000..2a1b7ec --- /dev/null +++ b/terraform/pre-production/terraform-compliance/dynamodb.feature @@ -0,0 +1,26 @@ +Feature: DynamoDB is used as our NoSQL database service + In order to improve security + As engineers + We'll use ensure our DynamoDB tables are configured correctly + + Scenario: Ensure BackupPolicy tag is present + Given I have aws_dynamodb_table defined + Then it must contain tags + And it must contain BackupPolicy + + Scenario: Ensure point in time recovery disabled + Given I have aws_dynamodb_table defined + Then it must contain point_in_time_recovery + And its enabled property must be false + + Scenario: Ensure a maximum of 1 LSIs + Given I have aws_dynamodb_table defined + When it contains local_secondary_index + When I count them + Then I expect the result is less and equal to 1 + + Scenario: Ensure a maximum of 2 GSIs + Given I have aws_dynamodb_table defined + When it contains global_secondary_index + When I count them + Then I expect the result is less and equal to 2 \ No newline at end of file diff --git a/terraform/pre-production/terraform-compliance/sns.feature b/terraform/pre-production/terraform-compliance/sns.feature new file mode 100644 index 0000000..38278d6 --- /dev/null +++ b/terraform/pre-production/terraform-compliance/sns.feature @@ -0,0 +1,8 @@ +Feature: SNS is used as our notification service + In order to improve security + As engineers + We'll use ensure our SNS topics are configured correctly + + Scenario: Ensure encryption is enabled + Given I have aws_sns_topic defined + Then it must contain kms_master_key_id diff --git a/terraform/pre-production/variables.tf b/terraform/pre-production/variables.tf new file mode 100644 index 0000000..c92cb7b --- /dev/null +++ b/terraform/pre-production/variables.tf @@ -0,0 +1,9 @@ +variable "environment_name" { + type = string + default = "pre-prod" +} + +variable "project_name" { + type = string + default = "Housing-Pre-Production" +}