From 86dfb29c5c74f1818dddfaec842b752bb3334665 Mon Sep 17 00:00:00 2001 From: Alastair Lock Date: Thu, 7 Nov 2024 10:33:26 +0000 Subject: [PATCH] DTOSS-5412: Create Dev Audit root module --- ....yaml => cd-infrastructure-dev-audit.yaml} | 19 +- ...e.yaml => cd-infrastructure-dev-core.yaml} | 16 +- .../pipelines/cd-infrastructure-dev.yaml | 63 -- .../pipelines/cd-infrastructure-nft.yaml | 59 -- .../cd-infrastructure-preprod-audit.yaml | 62 -- .../cd-infrastructure-preprod-core.yaml | 62 -- .env.template | 4 - .gitignore | 1 + .gitmodules | 2 + compose.yaml | 44 +- infrastructure/.gitignore | 10 - infrastructure/tf-audit/app_insights.tf | 15 + infrastructure/tf-audit/config.tf | 21 + infrastructure/tf-audit/data.tf | 12 + .../tf-audit/environments/development.tfvars | 53 ++ .../tf-audit/environments/integration.tfvars | 45 ++ .../tf-audit/environments/preprod.tfvars | 45 ++ .../tf-audit/log_analytics_workspace.tf | 15 + infrastructure/tf-audit/networking.tf | 116 +++ infrastructure/tf-audit/outputs.tf | 3 + .../tf-audit/private_link_scopes.tf | 56 ++ infrastructure/tf-audit/providers.tf | 29 + infrastructure/tf-audit/storage.tf | 39 + infrastructure/tf-audit/variables.tf | 122 +++ infrastructure/tf-core/app_service_plan.tf | 67 ++ infrastructure/tf-core/config.tf | 21 + infrastructure/tf-core/data.tf | 70 ++ .../tf-core/environments/development.tfvars | 371 +++++++++ .../tf-core/environments/integration.tfvars | 734 ++++++++++++++++++ .../tf-core/environments/nft.tfvars | 657 ++++++++++++++++ .../tf-core/environments/preprod.tfvars | 716 +++++++++++++++++ infrastructure/tf-core/function_app.tf | 261 +++++++ infrastructure/tf-core/key_vault.tf | 25 + infrastructure/tf-core/locals.tf | 3 + infrastructure/tf-core/network_routing.tf | 82 ++ infrastructure/tf-core/networking.tf | 118 +++ infrastructure/tf-core/providers.tf | 35 + infrastructure/tf-core/rbac.tf | 11 + infrastructure/tf-core/sql_server.tf | 41 + infrastructure/tf-core/storage.tf | 48 ++ infrastructure/tf-core/variables.tf | 364 +++++++++ scripts/database/drop_tables.sql | 4 + scripts/database/permissions.sql | 59 ++ scripts/deployment/get-docker-names.sh | 9 +- .../CreateParticipantScreeningProfile/delme | 0 .../EpisodeDataService/GetEpisode/delme | 0 .../EpisodeManagementService/GetEpisode/delme | 0 src/Shared/Common/delme | 0 src/Shared/Model/delme | 0 49 files changed, 4316 insertions(+), 293 deletions(-) rename .azuredevops/pipelines/{cd-infrastructure-int-audit.yaml => cd-infrastructure-dev-audit.yaml} (81%) rename .azuredevops/pipelines/{cd-infrastructure-int-core.yaml => cd-infrastructure-dev-core.yaml} (82%) delete mode 100644 .azuredevops/pipelines/cd-infrastructure-dev.yaml delete mode 100644 .azuredevops/pipelines/cd-infrastructure-nft.yaml delete mode 100644 .azuredevops/pipelines/cd-infrastructure-preprod-audit.yaml delete mode 100644 .azuredevops/pipelines/cd-infrastructure-preprod-core.yaml create mode 100644 infrastructure/tf-audit/app_insights.tf create mode 100644 infrastructure/tf-audit/config.tf create mode 100644 infrastructure/tf-audit/data.tf create mode 100644 infrastructure/tf-audit/environments/development.tfvars create mode 100644 infrastructure/tf-audit/environments/integration.tfvars create mode 100644 infrastructure/tf-audit/environments/preprod.tfvars create mode 100644 infrastructure/tf-audit/log_analytics_workspace.tf create mode 100644 infrastructure/tf-audit/networking.tf create mode 100644 infrastructure/tf-audit/outputs.tf create mode 100644 infrastructure/tf-audit/private_link_scopes.tf create mode 100644 infrastructure/tf-audit/providers.tf create mode 100644 infrastructure/tf-audit/storage.tf create mode 100644 infrastructure/tf-audit/variables.tf create mode 100644 infrastructure/tf-core/app_service_plan.tf create mode 100644 infrastructure/tf-core/config.tf create mode 100644 infrastructure/tf-core/data.tf create mode 100644 infrastructure/tf-core/environments/development.tfvars create mode 100644 infrastructure/tf-core/environments/integration.tfvars create mode 100644 infrastructure/tf-core/environments/nft.tfvars create mode 100644 infrastructure/tf-core/environments/preprod.tfvars create mode 100644 infrastructure/tf-core/function_app.tf create mode 100644 infrastructure/tf-core/key_vault.tf create mode 100644 infrastructure/tf-core/locals.tf create mode 100644 infrastructure/tf-core/network_routing.tf create mode 100644 infrastructure/tf-core/networking.tf create mode 100644 infrastructure/tf-core/providers.tf create mode 100644 infrastructure/tf-core/rbac.tf create mode 100644 infrastructure/tf-core/sql_server.tf create mode 100644 infrastructure/tf-core/storage.tf create mode 100644 infrastructure/tf-core/variables.tf create mode 100644 scripts/database/permissions.sql rename infrastructure/environments/.gitkeep => src/BIAnalyticsDataService/CreateParticipantScreeningProfile/delme (100%) rename infrastructure/images/.gitkeep => src/EpisodeDataService/GetEpisode/delme (100%) rename infrastructure/modules/.gitkeep => src/EpisodeManagementService/GetEpisode/delme (100%) create mode 100644 src/Shared/Common/delme create mode 100644 src/Shared/Model/delme diff --git a/.azuredevops/pipelines/cd-infrastructure-int-audit.yaml b/.azuredevops/pipelines/cd-infrastructure-dev-audit.yaml similarity index 81% rename from .azuredevops/pipelines/cd-infrastructure-int-audit.yaml rename to .azuredevops/pipelines/cd-infrastructure-dev-audit.yaml index ec1be369..48c46419 100644 --- a/.azuredevops/pipelines/cd-infrastructure-int-audit.yaml +++ b/.azuredevops/pipelines/cd-infrastructure-dev-audit.yaml @@ -11,32 +11,35 @@ trigger: - infrastructure/* pool: - #vmImage: ubuntu-latest - name: private-pool-dev-uks + vmImage: ubuntu-latest + # name: private-pool-dev-uks resources: repositories: - repository: dtos-devops-templates type: github name: NHSDigital/dtos-devops-templates - ref: 46e5d81bf58a7b9324a35e191eac7f6cc3660dfd + ref: 6698a57ef95f35661bb668e1b929e6894a554ac6 endpoint: NHSDigital variables: - - group: global_variable_group - - group: hub_variable_group - - group: integration_audit_variable_group + - group: DEV_audit_backend + - group: DEV_hub_backend_remote_state - name: TF_DIRECTORY value: $(System.DefaultWorkingDirectory)/$(System.TeamProject)/infrastructure/tf-audit + - name: TF_VERSION + value: 1.9.2 + - name: TF_PLAN_ARTIFACT + value: tf_plan_audit_DEV - name: ENVIRONMENT - value: integration + value: development stages: - stage: terraform_plan displayName: Terraform Plan condition: eq(variables['Build.Reason'], 'Manual') variables: - tfvars: environments/$(ENVIRONMENT).tfvars + tfVarsFile: environments/$(ENVIRONMENT).tfvars jobs: - job: init_and_plan displayName: Init, plan, store artifact diff --git a/.azuredevops/pipelines/cd-infrastructure-int-core.yaml b/.azuredevops/pipelines/cd-infrastructure-dev-core.yaml similarity index 82% rename from .azuredevops/pipelines/cd-infrastructure-int-core.yaml rename to .azuredevops/pipelines/cd-infrastructure-dev-core.yaml index b8caf136..98dc6f94 100644 --- a/.azuredevops/pipelines/cd-infrastructure-int-core.yaml +++ b/.azuredevops/pipelines/cd-infrastructure-dev-core.yaml @@ -19,24 +19,28 @@ resources: - repository: dtos-devops-templates type: github name: NHSDigital/dtos-devops-templates - ref: 46e5d81bf58a7b9324a35e191eac7f6cc3660dfd + ref: 6698a57ef95f35661bb668e1b929e6894a554ac6 endpoint: NHSDigital variables: - - group: global_variable_group - - group: hub_variable_group - - group: integration_core_variable_group + - group: DEV_core_backend + - group: DEV_audit_backend_remote_state + - group: DEV_hub_backend_remote_state - name: TF_DIRECTORY value: $(System.DefaultWorkingDirectory)/$(System.TeamProject)/infrastructure/tf-core + - name: TF_VERSION + value: 1.9.2 + - name: TF_PLAN_ARTIFACT + value: tf_plan_core_DEV - name: ENVIRONMENT - value: integration + value: development stages: - stage: terraform_plan displayName: Terraform Plan condition: eq(variables['Build.Reason'], 'Manual') variables: - tfvars: environments/$(ENVIRONMENT).tfvars + tfVarsFile: environments/$(ENVIRONMENT).tfvars jobs: - job: init_and_plan displayName: Init, plan, store artifact diff --git a/.azuredevops/pipelines/cd-infrastructure-dev.yaml b/.azuredevops/pipelines/cd-infrastructure-dev.yaml deleted file mode 100644 index e937c686..00000000 --- a/.azuredevops/pipelines/cd-infrastructure-dev.yaml +++ /dev/null @@ -1,63 +0,0 @@ ---- - -name: $(Build.SourceBranchName)-$(Date:yyyyMMdd)_$(Rev:r) - -trigger: - branches: - include: - - main - paths: - include: - - infrastructure/* - -pool: - #vmImage: ubuntu-latest - name: private-pool-prod-uks - -resources: - repositories: - - repository: dtos-devops-templates - type: github - name: NHSDigital/dtos-devops-templates - ref: main - endpoint: NHSDigital - -variables: - - group: global_variable_group - - group: hub_variable_group - # - group: dev_core_variable_group - - name: TF_DIRECTORY - value: $(System.DefaultWorkingDirectory)/$(System.TeamProject)/infrastructure/tf-core - - name: ENVIRONMENT - value: dev - -stages: - - stage: terraform_plan - displayName: Terraform Plan - condition: eq(variables['Build.Reason'], 'Manual') - variables: - tfvars: environments/$(ENVIRONMENT).tfvars - jobs: - - job: init_and_plan - displayName: Init, plan, store artifact - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_plan.yaml@dtos-devops-templates - - - stage: terraform_apply - displayName: Terraform Apply - dependsOn: [terraform_plan] - condition: and(eq(dependencies.terraform_plan.outputs['init_and_plan.TerraformPlan.changesPresent'], 'true'), eq(variables['Build.Reason'], 'Manual')) - jobs: - - deployment: terraform_apply - displayName: Init, get plan artifact, apply - environment: $(ENVIRONMENT) - strategy: - runOnce: - deploy: - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_apply.yaml@dtos-devops-templates - diff --git a/.azuredevops/pipelines/cd-infrastructure-nft.yaml b/.azuredevops/pipelines/cd-infrastructure-nft.yaml deleted file mode 100644 index 192c316a..00000000 --- a/.azuredevops/pipelines/cd-infrastructure-nft.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# --- -# Needs re-writing, basing off int or pre-prod -# -# trigger: -# branches: -# include: -# - main -# paths: -# include: -# - infrastructure/* - -# variables: -# - group: global_variable_group -# - group: hub_variable_group -# - name: TF_DIRECTORY -# value: $(System.DefaultWorkingDirectory)/infrastructure - -# pool: -# vmImage: ubuntu-latest - -# # NFT environment - -# stages: -# - stage: terraform_plan -# displayName: Terraform Plan -# dependsOn: [] -# condition: and(succeeded(), eq(variables['Build.Reason'], 'Manual')) -# variables: -# - group: nft_variable_group -# jobs: -# - template: ../jobs/init_and_plan.yaml -# parameters: -# ENVIRONMENT: $(ENVIRONMENT) -# SERVICE_CONNECTION: $(SERVICE_CONNECTION) -# TF_DIRECTORY: $(TF_DIRECTORY) -# TF_PLAN_ARTIFACT: $(TF_PLAN_ARTIFACT) -# TARGET_SUBSCRIPTION: $(TF_VAR_TARGET_SUBSCRIPTION_ID) - -# - stage: terraform_apply -# displayName: Terraform Apply -# dependsOn: [terraform_plan] -# condition: and(eq(dependencies.terraform_plan.outputs['init_and_plan.TerraformPlan.changesPresent'], 'true'), eq(variables['Build.Reason'], 'Manual')) -# variables: -# - group: nft_variable_group -# jobs: -# - deployment: terraform_apply -# displayName: Terraform Apply -# environment: $(ENVIRONMENT) -# strategy: -# runOnce: -# deploy: -# steps: -# - template: ../jobs/init_and_apply.yaml -# parameters: -# ENVIRONMENT: $(ENVIRONMENT) -# SERVICE_CONNECTION: $(SERVICE_CONNECTION) -# TF_DIRECTORY: $(TF_DIRECTORY) -# TF_PLAN_ARTIFACT: $(TF_PLAN_ARTIFACT) -# TARGET_SUBSCRIPTION: $(TF_VAR_TARGET_SUBSCRIPTION_ID) diff --git a/.azuredevops/pipelines/cd-infrastructure-preprod-audit.yaml b/.azuredevops/pipelines/cd-infrastructure-preprod-audit.yaml deleted file mode 100644 index f6fd78cb..00000000 --- a/.azuredevops/pipelines/cd-infrastructure-preprod-audit.yaml +++ /dev/null @@ -1,62 +0,0 @@ ---- - -name: $(Build.SourceBranchName)-$(Date:yyyyMMdd)_$(Rev:r) - -trigger: - branches: - include: - - main - paths: - include: - - infrastructure/* - -pool: - #vmImage: ubuntu-latest - name: private-pool-prod-uks - -resources: - repositories: - - repository: dtos-devops-templates - type: github - name: NHSDigital/dtos-devops-templates - ref: 46e5d81bf58a7b9324a35e191eac7f6cc3660dfd - endpoint: NHSDigital - -variables: - - group: prod_global_variable_group - - group: prod_hub_variable_group - - group: preprod_audit_variable_group - - name: TF_DIRECTORY - value: $(System.DefaultWorkingDirectory)/$(System.TeamProject)/infrastructure/tf-audit - - name: ENVIRONMENT - value: preprod - -stages: - - stage: terraform_plan - displayName: Terraform Plan - condition: eq(variables['Build.Reason'], 'Manual') - variables: - tfvars: environments/$(ENVIRONMENT).tfvars - jobs: - - job: init_and_plan - displayName: Init, plan, store artifact - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_plan.yaml@dtos-devops-templates - - - stage: terraform_apply - displayName: Terraform Apply - dependsOn: [terraform_plan] - condition: and(eq(dependencies.terraform_plan.outputs['init_and_plan.TerraformPlan.changesPresent'], 'true'), eq(variables['Build.Reason'], 'Manual')) - jobs: - - deployment: terraform_apply - displayName: Init, get plan artifact, apply - environment: $(ENVIRONMENT) - strategy: - runOnce: - deploy: - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_apply.yaml@dtos-devops-templates diff --git a/.azuredevops/pipelines/cd-infrastructure-preprod-core.yaml b/.azuredevops/pipelines/cd-infrastructure-preprod-core.yaml deleted file mode 100644 index 723282f6..00000000 --- a/.azuredevops/pipelines/cd-infrastructure-preprod-core.yaml +++ /dev/null @@ -1,62 +0,0 @@ ---- - -name: $(Build.SourceBranchName)-$(Date:yyyyMMdd)_$(Rev:r) - -trigger: - branches: - include: - - main - paths: - include: - - infrastructure/* - -pool: - #vmImage: ubuntu-latest - name: private-pool-prod-uks - -resources: - repositories: - - repository: dtos-devops-templates - type: github - name: NHSDigital/dtos-devops-templates - ref: 46e5d81bf58a7b9324a35e191eac7f6cc3660dfd - endpoint: NHSDigital - -variables: - - group: prod_global_variable_group - - group: prod_hub_variable_group - - group: preprod_core_variable_group - - name: TF_DIRECTORY - value: $(System.DefaultWorkingDirectory)/$(System.TeamProject)/infrastructure/tf-core - - name: ENVIRONMENT - value: preprod - -stages: - - stage: terraform_plan - displayName: Terraform Plan - condition: eq(variables['Build.Reason'], 'Manual') - variables: - tfvars: environments/$(ENVIRONMENT).tfvars - jobs: - - job: init_and_plan - displayName: Init, plan, store artifact - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_plan.yaml@dtos-devops-templates - - - stage: terraform_apply - displayName: Terraform Apply - dependsOn: [terraform_plan] - condition: and(eq(dependencies.terraform_plan.outputs['init_and_plan.TerraformPlan.changesPresent'], 'true'), eq(variables['Build.Reason'], 'Manual')) - jobs: - - deployment: terraform_apply - displayName: Init, get plan artifact, apply - environment: $(ENVIRONMENT) - strategy: - runOnce: - deploy: - steps: - - checkout: self - - checkout: dtos-devops-templates - - template: .azuredevops/templates/steps/tf_apply.yaml@dtos-devops-templates diff --git a/.env.template b/.env.template index db0767f2..e81140a4 100644 --- a/.env.template +++ b/.env.template @@ -15,7 +15,3 @@ MESHSHAREDKEY="" #Shared Key for the MESH Mailbox, For local sandbox this is set MESHKEYPASSPHRASE="" #Passpharse for the private key for authenticating against the MESH Mailbox, for the local sandbox this is defined when creating the private key and certificate, production and integration this will be issues when provisining the environment. MESHAPIBASEURL=http://localhost:8700/messageexchange #Mesh mailbox URL host.docker.internal for mac and localhost for windows BSSMAILBOX=X26ABC1 #Mesh mailbox name - - - - diff --git a/.gitignore b/.gitignore index 34e33a5f..b9ba503d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *vulnerabilities*report*.json *report*json.zip .version +.DS_Store *.code-workspace !project.code-workspace diff --git a/.gitmodules b/.gitmodules index 38c2a85c..76eb4b32 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,5 @@ [submodule "src/Shared/dotnet-mesh-client"] + # path = src/Shared/dotnet-mesh-client path = src/Shared/dotnet-mesh-client url = https://github.com/NHSDigital/dotnet-mesh-client + branch = main diff --git a/compose.yaml b/compose.yaml index b1e4c0dd..5098268e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -189,21 +189,18 @@ services: - ASPNETCORE_URLS=http://*:6070 - ServiceInsightsDbConnectionString=Server=${DB_CONNECTION},1433;Database=${DB_NAME};User Id=SA;Password=${PASSWORD};TrustServerCertificate=True - # update-episode: - # container_name: update-episode - # restart: always - # build: - # context: ./src/ - # dockerfile: ./EpisodeDataService/UpdateEpisode/Dockerfile - # networks: - # - app-network - # ports: - # - "7777:7777" - # environment: - # - ASPNETCORE_ENVIRONMENT=Development - # - FUNCTIONS_WORKER_RUNTIME=dotnet-isolated - # - ASPNETCORE_URLS=http://*:7777 - # - ServiceInsightsDbConnectionString=Server=${DB_CONNECTION},1433;Database=${DB_NAME};User Id=SA;Password=${PASSWORD};TrustServerCertificate=True + update-episode: + container_name: update-episode + network_mode: host + build: + context: ./src/ + dockerfile: ./EpisodeDataService/UpdateEpisode/Dockerfile + ports: + - "7777:7777" + environment: + - FUNCTIONS_WORKER_RUNTIME=dotnet-isolated + - ASPNETCORE_URLS=http://*:7777 + - ServiceInsightsDbConnectionString=Server=127.0.0.1,1433;Database=${DB_NAME};User Id=SA;Password=${PASSWORD};TrustServerCertificate=True # Episode Integration Service receive-data: @@ -317,3 +314,20 @@ services: - ASPNETCORE_ENVIRONMENT=Development - FUNCTIONS_WORKER_RUNTIME=dotnet-isolated - ASPNETCORE_URLS=http://*:7074 + + # Reference Data Service + get-organisation-data: + container_name: get-organisation-data + restart: always + build: + context: ./src/ + dockerfile: ./ReferenceDataService/GetReferenceData/Dockerfile + networks: + - app-network + ports: + - "6081:6081" + environment: + - ASPNETCORE_ENVIRONMENT=Development + - FUNCTIONS_WORKER_RUNTIME=dotnet-isolated + - ASPNETCORE_URLS=http://*:6081 + - ServiceInsightsDbConnectionString=Server=${DB_CONNECTION},1433;Database=${DB_NAME};User Id=SA;Password=${PASSWORD};TrustServerCertificate=True diff --git a/infrastructure/.gitignore b/infrastructure/.gitignore index 22ebdac3..6c4ba38d 100644 --- a/infrastructure/.gitignore +++ b/infrastructure/.gitignore @@ -3,21 +3,11 @@ # Local .terraform directories **/.terraform/* -# .tfstate files -*.tfstate -*.tfstate.* # Crash log files crash.log crash.*.log -# Exclude all .tfvars files, which are likely to contain sensitive data, such as -# password, private keys, and other secrets. These should not be part of version -# control as they are data points which are potentially sensitive and subject -# to change depending on the environment. -*.tfvars -*.tfvars.json - # Ignore override files as they are usually used to override resources locally and so # are not checked in override.tf diff --git a/infrastructure/tf-audit/app_insights.tf b/infrastructure/tf-audit/app_insights.tf new file mode 100644 index 00000000..cc0bfc66 --- /dev/null +++ b/infrastructure/tf-audit/app_insights.tf @@ -0,0 +1,15 @@ +module "app_insights_audit" { + for_each = { for key, val in var.regions : key => val if val.is_primary_region } + + source = "../../../dtos-devops-templates/infrastructure/modules/app-insights" + + name = module.regions_config[each.key].names.app-insights + location = each.key + appinsights_type = var.app_insights.appinsights_type + + log_analytics_workspace_id = module.log_analytics_workspace_audit[each.key].id + + resource_group_name = azurerm_resource_group.audit[each.key].name + tags = var.tags + +} diff --git a/infrastructure/tf-audit/config.tf b/infrastructure/tf-audit/config.tf new file mode 100644 index 00000000..fee7f156 --- /dev/null +++ b/infrastructure/tf-audit/config.tf @@ -0,0 +1,21 @@ +resource "azurerm_resource_group" "audit" { + for_each = { for key, val in var.regions : key => val if val.is_primary_region } + + name = "${module.regions_config[each.key].names.resource-group}-audit" + location = each.key + + lifecycle { + ignore_changes = [tags] + } +} + +module "regions_config" { + for_each = var.regions + + source = "../../../dtos-devops-templates/infrastructure/modules/shared-config" + + location = each.key + application = var.application + env = var.environment + tags = var.tags +} diff --git a/infrastructure/tf-audit/data.tf b/infrastructure/tf-audit/data.tf new file mode 100644 index 00000000..442d60fb --- /dev/null +++ b/infrastructure/tf-audit/data.tf @@ -0,0 +1,12 @@ +data "azurerm_client_config" "current" {} + +data "terraform_remote_state" "hub" { + backend = "azurerm" + config = { + subscription_id = var.HUB_SUBSCRIPTION_ID + storage_account_name = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_NAME + container_name = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME + key = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_KEY + resource_group_name = var.HUB_BACKEND_AZURE_RESOURCE_GROUP_NAME + } +} diff --git a/infrastructure/tf-audit/environments/development.tfvars b/infrastructure/tf-audit/environments/development.tfvars new file mode 100644 index 00000000..c4e38788 --- /dev/null +++ b/infrastructure/tf-audit/environments/development.tfvars @@ -0,0 +1,53 @@ +application = "serins" +application_full_name = "service-insights" +environment = "DEV" + +features = { + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Service-Insights" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.114.0.0/16" + connect_peering = true + subnets = { + # apps = { + # cidr_newbits = 8 + # cidr_offset = 2 + # delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + # } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + } + } +} + +app_insights = { + appinsights_type = "web" +} + +law = { + law_sku = "PerGB2018" + retention_days = 30 +} + +storage_accounts = { + fnapp = { + name_suffix = "sqllogs" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = {} + } +} diff --git a/infrastructure/tf-audit/environments/integration.tfvars b/infrastructure/tf-audit/environments/integration.tfvars new file mode 100644 index 00000000..469435a5 --- /dev/null +++ b/infrastructure/tf-audit/environments/integration.tfvars @@ -0,0 +1,45 @@ +application = "cohman" +application_full_name = "cohort-manager" +environment = "INT" + +features = { + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Cohort-Manager" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.106.0.0/16" + connect_peering = true + subnets = { + # apps = { + # cidr_newbits = 8 + # cidr_offset = 2 + # delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + # } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + } + } +} + +app_insights = { + #name_suffix = "cohman" + appinsights_type = "web" +} + +law = { + #name_suffix = "cohman" + law_sku = "PerGB2018" + retention_days = 30 +} diff --git a/infrastructure/tf-audit/environments/preprod.tfvars b/infrastructure/tf-audit/environments/preprod.tfvars new file mode 100644 index 00000000..f286a53a --- /dev/null +++ b/infrastructure/tf-audit/environments/preprod.tfvars @@ -0,0 +1,45 @@ +application = "cohman" +application_full_name = "cohort-manager" +environment = "PRE" + +features = { + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Cohort-Manager" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.3.0.0/16" + connect_peering = true + subnets = { + # apps = { + # cidr_newbits = 8 + # cidr_offset = 2 + # delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_name = "Microsoft.Web/serverFarms" + # service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + # } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + } + } +} + +app_insights = { + #name_suffix = "cohman" + appinsights_type = "web" +} + +law = { + #name_suffix = "cohman" + law_sku = "PerGB2018" + retention_days = 30 +} diff --git a/infrastructure/tf-audit/log_analytics_workspace.tf b/infrastructure/tf-audit/log_analytics_workspace.tf new file mode 100644 index 00000000..caf56102 --- /dev/null +++ b/infrastructure/tf-audit/log_analytics_workspace.tf @@ -0,0 +1,15 @@ +module "log_analytics_workspace_audit" { + for_each = { for key, val in var.regions : key => val if val.is_primary_region } + + source = "../../../dtos-devops-templates/infrastructure/modules/log-analytics-workspace" + + name = module.regions_config[each.key].names.log-analytics-workspace + location = each.key + + law_sku = var.law.law_sku + retention_days = var.law.retention_days + + resource_group_name = azurerm_resource_group.audit[each.key].name + + tags = var.tags +} diff --git a/infrastructure/tf-audit/networking.tf b/infrastructure/tf-audit/networking.tf new file mode 100644 index 00000000..55082310 --- /dev/null +++ b/infrastructure/tf-audit/networking.tf @@ -0,0 +1,116 @@ +resource "azurerm_resource_group" "rg_vnet" { + for_each = var.regions + + name = "${module.regions_config[each.key].names.resource-group}-audit-networking" + location = each.key +} + +resource "azurerm_resource_group" "rg_private_endpoints" { + for_each = var.features.private_endpoints_enabled ? var.regions : {} + + name = "${module.regions_config[each.key].names.resource-group}-audit-private-endpoints" + location = each.key +} + +module "vnet" { + for_each = var.regions + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet" + + name = module.regions_config[each.key].names.virtual-network + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + location = each.key + vnet_address_space = each.value.address_space + + dns_servers = [data.terraform_remote_state.hub.outputs.private_dns_resolver_inbound_ips[each.key].private_dns_resolver_ip] + + tags = var.tags +} + +/*-------------------------------------------------------------------------------------------------- + Create Subnets +--------------------------------------------------------------------------------------------------*/ + +module "subnets" { + for_each = local.subnets_map + + source = "../../../dtos-devops-templates/infrastructure/modules/subnet" + + name = each.value.subnet_name + location = module.vnet[each.value.vnet_key].vnet.location + network_security_group_name = each.value.nsg_name + network_security_group_nsg_rules = each.value.nsg_rules + create_nsg = each.value.create_nsg + resource_group_name = module.vnet[each.value.vnet_key].vnet.resource_group_name + vnet_name = module.vnet[each.value.vnet_key].name + address_prefixes = [each.value.address_prefixes] + default_outbound_access_enabled = true + private_endpoint_network_policies = "Disabled" # Default as per compliance requirements + + delegation_name = each.value.delegation_name != null ? each.value.delegation_name : "" + service_delegation_name = each.value.service_delegation_name != null ? each.value.service_delegation_name : "" + service_delegation_actions = each.value.service_delegation_actions != null ? each.value.service_delegation_actions : [] + + tags = var.tags +} + +locals { + # Expand a flattened list of objects for all subnets (allows nested for loops) + subnets_flatlist = flatten([ + for key, val in var.regions : [ + for subnet_key, subnet in val.subnets : merge({ + vnet_key = key + subnet_name = coalesce(subnet.name, "${module.regions_config[key].names.subnet}-${subnet_key}") + nsg_name = "${module.regions_config[key].names.network-security-group}-${subnet_key}" + nsg_rules = lookup(var.network_security_group_rules, subnet_key, []) + create_nsg = coalesce(subnet.create_nsg, true) + address_prefixes = cidrsubnet(val.address_space, subnet.cidr_newbits, subnet.cidr_offset) + }, subnet) # include all the declared key/value pairs for a specific subnet + ] + ]) + # Project the above list into a map with unique keys for consumption in a for_each meta argument + subnets_map = { for subnet in local.subnets_flatlist : subnet.subnet_name => subnet } +} + +/*-------------------------------------------------------------------------------------------------- + Create peering +--------------------------------------------------------------------------------------------------*/ + +module "peering_spoke_hub" { + # loop through regions and only create peering if connect_peering is set to true + for_each = { for key, val in var.regions : key => val if val.connect_peering == true } + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet-peering" + + name = "${module.regions_config[each.key].names.virtual-network}-audit-to-hub-peering" + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + vnet_name = module.vnet[each.key].vnet.name + remote_vnet_id = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].vnet.id + + allow_virtual_network_access = true + allow_forwarded_traffic = true + allow_gateway_transit = false + + use_remote_gateways = false +} + +module "peering_hub_spoke" { + for_each = { for key, val in var.regions : key => val if val.connect_peering == true } + + providers = { + azurerm = azurerm.hub + } + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet-peering" + + name = "hub-to-${module.regions_config[each.key].names.virtual-network}-audit-peering" + resource_group_name = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].vnet.resource_group_name + vnet_name = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].name + remote_vnet_id = module.vnet[each.key].vnet.id + + allow_virtual_network_access = true + allow_forwarded_traffic = true + allow_gateway_transit = false + + use_remote_gateways = false +} diff --git a/infrastructure/tf-audit/outputs.tf b/infrastructure/tf-audit/outputs.tf new file mode 100644 index 00000000..ef59a335 --- /dev/null +++ b/infrastructure/tf-audit/outputs.tf @@ -0,0 +1,3 @@ +output "log_analytics_workspace_id" { + value = { for k, v in module.log_analytics_workspace_audit : k => v.id } +} diff --git a/infrastructure/tf-audit/private_link_scopes.tf b/infrastructure/tf-audit/private_link_scopes.tf new file mode 100644 index 00000000..36e8e0e8 --- /dev/null +++ b/infrastructure/tf-audit/private_link_scopes.tf @@ -0,0 +1,56 @@ +# Create the private link service for Application Insights and Log Analytics +module "private_link_scoped_service_app_insights" { + for_each = var.features.private_endpoints_enabled ? var.regions : {} + + source = "../../../dtos-devops-templates/infrastructure/modules/private-link-scoped-service" + + name = "${module.regions_config[each.key].names.log-analytics-workspace}-ampls-service-app-insights" + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + + linked_resource_id = module.app_insights_audit[each.key].id + scope_name = module.private_link_scope[each.key].scope_name +} + +module "private_link_scoped_service_law" { + for_each = var.features.private_endpoints_enabled ? var.regions : {} + + source = "../../../dtos-devops-templates/infrastructure/modules/private-link-scoped-service" + + name = "${module.regions_config[each.key].names.log-analytics-workspace}-ampls-service-law" + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + + linked_resource_id = module.log_analytics_workspace_audit[each.key].id + scope_name = module.private_link_scope[each.key].scope_name +} + +# Create the private link scope in the spoke subscription +module "private_link_scope" { + for_each = var.features.private_endpoints_enabled ? var.regions : {} + + source = "../../../dtos-devops-templates/infrastructure/modules/private-link-scope" + + name = module.regions_config[each.key].names.log-analytics-workspace + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + location = each.key + + ingestion_access_mode = "PrivateOnly" + query_access_mode = "Open" + + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids = [ + data.terraform_remote_state.hub.outputs.private_dns_zone_app_insight[each.key].private_dns_zone.id, + data.terraform_remote_state.hub.outputs.private_dns_zone_azure_automation[each.key].private_dns_zone.id, + data.terraform_remote_state.hub.outputs.private_dns_zone_od_insights[each.key].private_dns_zone.id, + data.terraform_remote_state.hub.outputs.private_dns_zone_op_insights[each.key].private_dns_zone.id, + data.terraform_remote_state.hub.outputs.private_dns_zone_storage_blob[each.key].private_dns_zone.id + ] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + + tags = var.tags + +} diff --git a/infrastructure/tf-audit/providers.tf b/infrastructure/tf-audit/providers.tf new file mode 100644 index 00000000..94898916 --- /dev/null +++ b/infrastructure/tf-audit/providers.tf @@ -0,0 +1,29 @@ +terraform { + backend "azurerm" {} + required_version = ">= 1.9.2" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + # version = ">= 4.2.0" + version = "= 3.112.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.53.1" + } + random = "~> 3.5.1" + } +} + +provider "azurerm" { + subscription_id = var.TARGET_SUBSCRIPTION_ID + features {} +} + +provider "azurerm" { + alias = "hub" + subscription_id = var.HUB_SUBSCRIPTION_ID + features {} +} + +provider "azuread" {} diff --git a/infrastructure/tf-audit/storage.tf b/infrastructure/tf-audit/storage.tf new file mode 100644 index 00000000..4c7bd8fd --- /dev/null +++ b/infrastructure/tf-audit/storage.tf @@ -0,0 +1,39 @@ +module "storage" { + for_each = local.storage_accounts_map + source = "../../../dtos-devops-templates/infrastructure/modules/storage" + name = substr("${module.regions_config[each.value.region_key].names.storage-account}${lower(each.value.name_suffix)}", 0, 24) + resource_group_name = azurerm_resource_group.audit[each.value.region_key].name + location = each.value.region_key + containers = each.value.containers + account_replication_type = each.value.replication_type + account_tier = each.value.account_tier + public_network_access_enabled = each.value.public_network_access_enabled + rbac_roles = {} + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids_blob = [data.terraform_remote_state.hub.outputs.private_dns_zone_storage_blob[each.value.region_key].private_dns_zone.id] + private_dns_zone_ids_queue = [data.terraform_remote_state.hub.outputs.private_dns_zone_storage_queue[each.value.region_key].private_dns_zone.id] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.value.region_key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.value.region_key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + tags = var.tags +} +locals { + storage_accounts_flatlist = flatten([ + for region_key, region_val in var.regions : [ + for storage_key, storage_val in var.storage_accounts : { + name = "${storage_key}-${region_key}" + region_key = region_key + name_suffix = storage_val.name_suffix + replication_type = storage_val.replication_type + account_tier = storage_val.account_tier + public_network_access_enabled = storage_val.public_network_access_enabled + containers = storage_val.containers + } + ] + ]) + # Project the above list into a map with unique keys for consumption in a for_each meta argument + storage_accounts_map = { for storage in local.storage_accounts_flatlist : storage.name => storage } +} diff --git a/infrastructure/tf-audit/variables.tf b/infrastructure/tf-audit/variables.tf new file mode 100644 index 00000000..7630a009 --- /dev/null +++ b/infrastructure/tf-audit/variables.tf @@ -0,0 +1,122 @@ +variable "TARGET_SUBSCRIPTION_ID" { + description = "ID of a subscription to deploy infrastructure" + type = string +} + +variable "HUB_SUBSCRIPTION_ID" { + description = "ID of the subscription hosting the DevOps resources" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_NAME" { + description = "The name of the Azure Storage Account for the backend" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME" { + description = "The name of the container in the Azure Storage Account for the backend" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_KEY" { + description = "The name of the Statefile for the hub resources" + type = string +} + +variable "HUB_BACKEND_AZURE_RESOURCE_GROUP_NAME" { + description = "The name of the resource group for the Azure Storage Account" + type = string +} + +variable "application" { + description = "Project/Application code for deployment" + type = string + default = "DToS" +} + +variable "application_full_name" { + description = "Full name of the Project/Application code for deployment" + type = string + default = "DToS" +} + +variable "environment" { + description = "Environment code for deployments" + type = string + default = "DEV" +} + +variable "features" { + description = "Feature flags for the deployment" + type = map(bool) +} + +variable "regions" { + type = map(object({ + address_space = optional(string) + is_primary_region = bool + connect_peering = optional(bool, false) + subnets = optional(map(object({ + cidr_newbits = string + cidr_offset = string + create_nsg = optional(bool, true) # defaults to true + name = optional(string) # Optional name override + delegation_name = optional(string) + service_delegation_name = optional(string) + service_delegation_actions = optional(list(string)) + }))) + })) +} + +variable "app_insights" { + description = "Configuration of the App Insights" + type = object({ + name = optional(string, "cohman") + appinsights_type = optional(string, "web") + }) +} + +variable "law" { + description = "Configuration of the Log Analytics Workspace" + type = object({ + name = optional(string, "cohman") + law_sku = optional(string, "PerGB2018") + retention_days = optional(number, 30) + }) +} + +variable "network_security_group_rules" { + description = "The network security group rules." + default = {} + type = map(list(object({ + name = string + priority = number + direction = string + access = string + protocol = string + source_port_range = string + destination_port_range = string + source_address_prefix = string + destination_address_prefix = string + }))) +} + +variable "tags" { + description = "Default tags to be applied to resources" + type = map(string) +} + + +variable "storage_accounts" { + description = "Configuration for the Storage Account, currently used for SQL Server audit logs" + type = map(object({ + name_suffix = string + account_tier = optional(string, "Standard") + replication_type = optional(string, "LRS") + public_network_access_enabled = optional(bool, false) + containers = optional(map(object({ + container_name = string + container_access_type = optional(string, "private") + })), {}) + })) +} diff --git a/infrastructure/tf-core/app_service_plan.tf b/infrastructure/tf-core/app_service_plan.tf new file mode 100644 index 00000000..41384fc9 --- /dev/null +++ b/infrastructure/tf-core/app_service_plan.tf @@ -0,0 +1,67 @@ +locals { + # Create a flat list of projects with region keys for consumption in a for_each meta argument + app_service_plans_flatlist = flatten([ + for region_key, region_val in var.regions : [ + for asp_key, asp_val in var.app_service_plan.instances : { + key = "${asp_key}-${region_key}" + asp_key = asp_key + asp_val = asp_val + region_key = region_key + } + ] + ]) + + # Project the above list into a map with unique keys for consumption in a for_each meta argument + app_service_plans_map = { for asp in local.app_service_plans_flatlist : asp.key => asp } +} + +module "app-service-plan" { + for_each = local.app_service_plans_map + + source = "../../../dtos-devops-templates/infrastructure/modules/app-service-plan" + + name = "${module.regions_config[each.value.region_key].names.app-service-plan}-${lower(each.value.asp_key)}" + resource_group_name = azurerm_resource_group.core[each.value.region_key].name + location = each.value.region_key + + os_type = var.app_service_plan.os_type + sku_name = var.app_service_plan.sku_name + + vnet_integration_subnet_id = module.subnets["${module.regions_config[each.value.region_key].names.subnet}-apps"].id + + tags = var.tags + + ## autoscale rule + metric = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.metric, var.app_service_plan.autoscale.memory_percentage.metric) : var.app_service_plan.autoscale.memory_percentage.metric + + capacity_min = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.capacity_min, var.app_service_plan.autoscale.memory_percentage.capacity_min) : var.app_service_plan.autoscale.memory_percentage.capacity_min + capacity_max = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.capacity_max, var.app_service_plan.autoscale.memory_percentage.capacity_max) : var.app_service_plan.autoscale.memory_percentage.capacity_max + capacity_def = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.capacity_def, var.app_service_plan.autoscale.memory_percentage.capacity_def) : var.app_service_plan.autoscale.memory_percentage.capacity_def + + time_grain = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.time_grain, var.app_service_plan.autoscale.memory_percentage.time_grain) : var.app_service_plan.autoscale.memory_percentage.time_grain + statistic = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.statistic, var.app_service_plan.autoscale.memory_percentage.statistic) : var.app_service_plan.autoscale.memory_percentage.statistic + time_window = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.time_window, var.app_service_plan.autoscale.memory_percentage.time_window) : var.app_service_plan.autoscale.memory_percentage.time_window + time_aggregation = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.time_aggregation, var.app_service_plan.autoscale.memory_percentage.time_aggregation) : var.app_service_plan.autoscale.memory_percentage.time_aggregation + + inc_operator = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_operator, var.app_service_plan.autoscale.memory_percentage.inc_operator) : var.app_service_plan.autoscale.memory_percentage.inc_operator + inc_threshold = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_threshold, var.app_service_plan.autoscale.memory_percentage.inc_threshold) : var.app_service_plan.autoscale.memory_percentage.inc_threshold + inc_scale_direction = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_scale_direction, var.app_service_plan.autoscale.memory_percentage.inc_scale_direction) : var.app_service_plan.autoscale.memory_percentage.inc_scale_direction + inc_scale_type = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_scale_type, var.app_service_plan.autoscale.memory_percentage.inc_scale_type) : var.app_service_plan.autoscale.memory_percentage.inc_scale_type + inc_scale_value = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_scale_value, var.app_service_plan.autoscale.memory_percentage.inc_scale_value) : var.app_service_plan.autoscale.memory_percentage.inc_scale_value + inc_scale_cooldown = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.inc_scale_cooldown, var.app_service_plan.autoscale.memory_percentage.inc_scale_cooldown) : var.app_service_plan.autoscale.memory_percentage.inc_scale_cooldown + + dec_operator = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_operator, var.app_service_plan.autoscale.memory_percentage.dec_operator) : var.app_service_plan.autoscale.memory_percentage.dec_operator + dec_threshold = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_threshold, var.app_service_plan.autoscale.memory_percentage.dec_threshold) : var.app_service_plan.autoscale.memory_percentage.dec_threshold + dec_scale_direction = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_scale_direction, var.app_service_plan.autoscale.memory_percentage.dec_scale_direction) : var.app_service_plan.autoscale.memory_percentage.dec_scale_direction + dec_scale_type = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_scale_type, var.app_service_plan.autoscale.memory_percentage.dec_scale_type) : var.app_service_plan.autoscale.memory_percentage.dec_scale_type + dec_scale_value = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_scale_value, var.app_service_plan.autoscale.memory_percentage.dec_scale_value) : var.app_service_plan.autoscale.memory_percentage.dec_scale_value + dec_scale_cooldown = each.value.asp_val.autoscale_override != null ? coalesce(each.value.asp_val.autoscale_override.memory_percentage.dec_scale_cooldown, var.app_service_plan.autoscale.memory_percentage.dec_scale_cooldown) : var.app_service_plan.autoscale.memory_percentage.dec_scale_cooldown +} + +output "app_service_plans_flatlist" { + value = local.app_service_plans_flatlist +} + +output "app_service_plans" { + value = local.app_service_plans_map +} diff --git a/infrastructure/tf-core/config.tf b/infrastructure/tf-core/config.tf new file mode 100644 index 00000000..68ac1a00 --- /dev/null +++ b/infrastructure/tf-core/config.tf @@ -0,0 +1,21 @@ +resource "azurerm_resource_group" "core" { + for_each = var.regions + + name = module.regions_config[each.key].names.resource-group + location = each.key + + lifecycle { + ignore_changes = [tags] + } +} + +module "regions_config" { + for_each = var.regions + + source = "../../../dtos-devops-templates/infrastructure/modules/shared-config" + + location = each.key + application = var.application + env = var.environment + tags = var.tags +} diff --git a/infrastructure/tf-core/data.tf b/infrastructure/tf-core/data.tf new file mode 100644 index 00000000..0fbf22fb --- /dev/null +++ b/infrastructure/tf-core/data.tf @@ -0,0 +1,70 @@ +data "azurerm_client_config" "current" {} + +data "terraform_remote_state" "hub" { + backend = "azurerm" + config = { + subscription_id = var.HUB_SUBSCRIPTION_ID + storage_account_name = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_NAME + container_name = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME + key = var.HUB_BACKEND_AZURE_STORAGE_ACCOUNT_KEY + resource_group_name = var.HUB_BACKEND_AZURE_RESOURCE_GROUP_NAME + } +} + +# Note the following two Networking data look-ups only work becasue the names for the +# resources are effectively the same in both subscriptions (with additional name suffix for Audit RG) +data "azurerm_virtual_network" "vnet_audit" { + for_each = var.regions + + provider = azurerm.audit + + name = module.regions_config[each.key].names.virtual-network + resource_group_name = "${module.regions_config[each.key].names.resource-group}-audit-networking" +} + +data "azurerm_subnet" "subnet_audit_pep" { + for_each = var.regions + + provider = azurerm.audit + + name = "${module.regions_config[each.key].names.subnet}-pep" + resource_group_name = "${module.regions_config[each.key].names.resource-group}-audit-networking" + virtual_network_name = module.regions_config[each.key].names.virtual-network +} + +data "azuread_group" "sql_admin_group" { + display_name = var.sqlserver.sql_admin_group_name +} + +data "azurerm_container_registry" "acr" { + provider = azurerm.hub + + name = var.function_apps.acr_name + resource_group_name = var.function_apps.acr_rg_name +} + +data "azurerm_user_assigned_identity" "acr_mi" { + provider = azurerm.hub + + name = var.function_apps.acr_mi_name + resource_group_name = var.function_apps.acr_rg_name +} + +data "azurerm_application_insights" "ai" { + provider = azurerm.audit + + name = var.function_apps.app_insights_name + resource_group_name = var.function_apps.app_insights_rg_name +} + + +data "terraform_remote_state" "audit" { + backend = "azurerm" + config = { + subscription_id = var.HUB_SUBSCRIPTION_ID + storage_account_name = var.AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_NAME + container_name = var.AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME + key = var.AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_KEY + resource_group_name = var.AUDIT_BACKEND_AZURE_RESOURCE_GROUP_NAME + } +} diff --git a/infrastructure/tf-core/environments/development.tfvars b/infrastructure/tf-core/environments/development.tfvars new file mode 100644 index 00000000..9c6e3fd9 --- /dev/null +++ b/infrastructure/tf-core/environments/development.tfvars @@ -0,0 +1,371 @@ +application = "serins" +application_full_name = "service-insights" +environment = "DEV" + +features = { + acr_enabled = false + api_management_enabled = false + event_grid_enabled = false + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Service-Insights" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.113.0.0/16" + connect_peering = true + subnets = { + apps = { + cidr_newbits = 8 + cidr_offset = 2 + delegation_name = "Microsoft.Web/serverFarms" + service_delegation_name = "Microsoft.Web/serverFarms" + service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + sql = { + cidr_newbits = 8 + cidr_offset = 3 + } + } + } +} + +routes = { + uksouth = { + firewall_policy_priority = 100 + application_rules = [] + nat_rules = [] + network_rules = [ + { + name = "AllowSerinsToAudit" + priority = 800 + action = "Allow" + rule_name = "SerinsToAudit" + source_addresses = ["10.113.0.0/16"] # will be populated with the serins manager subnet address space + destination_addresses = ["10.114.0.0/16"] # will be populated with the audit subnet address space + protocols = ["TCP", "UDP"] + destination_ports = ["443"] + }, + { + name = "AllowAuditToSerins" + priority = 810 + action = "Allow" + rule_name = "AuditToSerins" + source_addresses = ["10.114.0.0/16"] + destination_addresses = ["10.113.0.0/16"] + protocols = ["TCP", "UDP"] + destination_ports = ["443"] + } + ] + route_table_routes_to_audit = [ + { + name = "SerinsToAudit" + address_prefix = "10.114.0.0/16" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "" # will be populated with the Firewall Private IP address + } + ] + route_table_routes_from_audit = [ + { + name = "AuditToSerins" + address_prefix = "10.113.0.0/16" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "" # will be populated with the Firewall Private IP address + } + ] + } +} + +app_service_plan = { + os_type = "Linux" + sku_name = "P2v3" + vnet_integration_enabled = true + + autoscale = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "1" + capacity_max = "5" + capacity_def = "1" + + time_grain = "PT1M" + statistic = "Average" + time_window = "PT10M" + time_aggregation = "Average" + + inc_operator = "GreaterThan" + inc_threshold = 70 + inc_scale_direction = "Increase" + inc_scale_type = "ChangeCount" + inc_scale_value = 1 + inc_scale_cooldown = "PT5M" + + dec_operator = "LessThan" + dec_threshold = 25 + dec_scale_direction = "Decrease" + dec_scale_type = "ChangeCount" + dec_scale_value = 1 + dec_scale_cooldown = "PT5M" + } + } + + instances = { + BIAnalyticsDataService = {} + BIAnalyticsService = {} + DemographicsService = {} + EpisodeDataService = {} + EpisodeIntegrationService = {} + EpisodeManagementService = {} + MeshIntegrationService = {} + ParticipantManagementService = {} + ReferenceDataService = {} + } +} + + +function_apps = { + acr_mi_name = "dtos-service-insights-acr-push" + acr_name = "acrukshubdevserins" + acr_rg_name = "rg-hub-dev-uks-serins" + + app_insights_name = "appi-dev-uks-serins" + app_insights_rg_name = "rg-serins-dev-uks-audit" + + always_on = true + + cont_registry_use_mi = true + + docker_CI_enable = "true" + docker_env_tag = "development" + docker_img_prefix = "service-insights" + + enable_appsrv_storage = "false" + ftps_state = "Disabled" + https_only = true + remote_debugging_enabled = false + storage_uses_managed_identity = null + worker_32bit = false + + fa_config = { + + CreateParticipantScreeningEpisodeData = { + name_suffix = "create-participant-screening-episode-data" + function_endpoint_name = "CreateParticipantScreeningEpisodeData" + app_service_plan_key = "BIAnalyticsDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + + CreateParticipantScreeningProfileData = { + name_suffix = "create-participant-screening-profile-data" + function_endpoint_name = "CreateParticipantScreeningProfileData" + app_service_plan_key = "BIAnalyticsDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + + CreateParticipantScreeningEpisode = { + name_suffix = "create-participant-screening-episode" + function_endpoint_name = "CreateParticipantScreeningEpisode" + app_service_plan_key = "BIAnalyticsDataService" + app_urls = [ + { + env_var_name = "GetEpisodeUrl" + function_app_key = "GetEpisode" + }, + { + env_var_name = "CreateParticipantScreeningEpisodeUrl" + function_app_key = "CreateParticipantScreeningEpisode" + } + ] + } + + CreateParticipantScreeningProfile = { + name_suffix = "create-participant-screening-profile" + function_endpoint_name = "CreateParticipantScreeningProfile" + app_service_plan_key = "BIAnalyticsDataService" + app_urls = [ + { + env_var_name = "GetParticipantUrl" + function_app_key = "GetParticipant" + }, + { + env_var_name = "CreateParticipantScreeningProfileUrl" + function_app_key = "CreateParticipantScreeningProfile" + }, + { + env_var_name = "DemographicsServiceUrl" + function_app_key = "GetDemographicsData" + } + ] + } + + GetDemographicsData = { + name_suffix = "get-demographics-data" + function_endpoint_name = "GetDemographicsData" + app_service_plan_key = "DemographicsService" + } + + CreateEpisode = { + name_suffix = "create-episode" + function_endpoint_name = "CreateEpisode" + app_service_plan_key = "EpisodeDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + + GetEpisode = { + name_suffix = "get-episode" + function_endpoint_name = "GetEpisode" + app_service_plan_key = "EpisodeDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + + UpdateEpisode = { + name_suffix = "update-episode" + function_endpoint_name = "UpdateEpisode" + app_service_plan_key = "EpisodeDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + + ReceiveData = { + name_suffix = "receive-data" + function_endpoint_name = "ReceiveData" + app_service_plan_key = "EpisodeIntegrationService" + app_urls = [ + { + env_var_name = "EpisodeManagementUrl" + function_app_key = "CreateUpdateEpisode" + }, + { + env_var_name = "ParticipantManagementUrl" + function_app_key = "UpdateParticipant" + } + ] + } + + CreateUpdateEpisode = { + name_suffix = "create-update-episode" + function_endpoint_name = "CreateUpdateEpisode" + app_service_plan_key = "EpisodeManagementService" + app_urls = [ + { + env_var_name = "CreateEpisodeUrl" + function_app_key = "CreateEpisode" + }, + { + env_var_name = "GetEpisodeUrl" + function_app_key = "GetEpisode" + }, + { + env_var_name = "UpdateEpisodeUrl" + function_app_key = "UpdateEpisode" + } + ] + } + + GetEpisodeMgmt = { + name_suffix = "get-episode-mgmt" + function_endpoint_name = "GetEpisodeMgmt" + app_service_plan_key = "EpisodeManagementService" + app_urls = [ + { + env_var_name = "GetEpisodeUrl" + function_app_key = "GetEpisode" + } + ] + } + + RetrieveMeshFile = { + name_suffix = "retrieve-mesh-file-from-cm" + function_endpoint_name = "RetrieveMeshFile" + app_service_plan_key = "MeshIntegrationService" + } + + GetParticipant = { + name_suffix = "get-participant" + function_endpoint_name = "GetParticipant" + app_service_plan_key = "ParticipantManagementService" + } + + UpdateParticipant = { + name_suffix = "update-participant" + function_endpoint_name = "UpdateParticipant" + app_service_plan_key = "ParticipantManagementService" + } + + GetOrganisationData = { + name_suffix = "get-organisation-data" + function_endpoint_name = "GetOrganisationData" + app_service_plan_key = "ReferenceDataService" + db_connection_string = "ServiceInsightsDbConnectionString" + } + } + +} + +function_app_slots = [] + +key_vault = { + disk_encryption = true + soft_del_ret_days = 7 + purge_prot = false + sku_name = "standard" +} + +sqlserver = { + sql_uai_name = "dtos-service-insight-sql-adm" + sql_admin_group_name = "sqlsvr_serins_dev_uks_admin" + ad_auth_only = true + + server = { + sqlversion = "12.0" + tlsversion = 1.2 + azure_services_access_enabled = true + } + + # serins database + dbs = { + serins = { + db_name_suffix = "ServiceInsightsDB" + collation = "SQL_Latin1_General_CP1_CI_AS" + licence_type = "LicenseIncluded" + max_gb = 5 + read_scale = false + sku = "S0" + } + } + + fw_rules = {} +} + +storage_accounts = { + fnapp = { + name_suffix = "fnappstor" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = { + config = { + container_name = "config" + } + sample-container = { + container_name = "sample-container" + } + inbound = { + container_name = "inbound" + } + inbound-poison = { + container_name = "inbound-poison" + } + } + } +} diff --git a/infrastructure/tf-core/environments/integration.tfvars b/infrastructure/tf-core/environments/integration.tfvars new file mode 100644 index 00000000..21576183 --- /dev/null +++ b/infrastructure/tf-core/environments/integration.tfvars @@ -0,0 +1,734 @@ +application = "cohman" +application_full_name = "cohort-manager" +environment = "INT" + +features = { + acr_enabled = false + api_management_enabled = false + event_grid_enabled = false + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Cohort-Manager" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.113.0.0/16" + connect_peering = true + subnets = { + apps = { + cidr_newbits = 8 + cidr_offset = 2 + delegation_name = "Microsoft.Web/serverFarms" + service_delegation_name = "Microsoft.Web/serverFarms" + service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + sql = { + cidr_newbits = 8 + cidr_offset = 3 + } + } + } +} + +routes = { + uksouth = { + firewall_policy_priority = 100 + application_rules = [] + nat_rules = [] + network_rules = [ + { + name = "AllowCohmanToAudit" + priority = 800 + action = "Allow" + rule_name = "SerinsToAudit" + source_addresses = ["10.105.0.0/16"] # will be populated with the cohort manager subnet address space + destination_addresses = ["10.106.0.0/16"] # will be populated with the audit subnet address space + protocols = ["TCP", "UDP"] + destination_ports = ["443"] + } + ] + route_table_routes = [ + { + name = "SerinsToAudit" + address_prefix = "" # will be populated with the cohort manager subnet address space + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "" # will be populated with the Firewall Private IP address + } + ] + } +} + +app_service_plan = { + os_type = "Linux" + sku_name = "P2v3" + vnet_integration_enabled = true + + autoscale = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "1" + capacity_max = "5" + capacity_def = "1" + + time_grain = "PT1M" + statistic = "Average" + time_window = "PT10M" + time_aggregation = "Average" + + inc_operator = "GreaterThan" + inc_threshold = 70 + inc_scale_direction = "Increase" + inc_scale_type = "ChangeCount" + inc_scale_value = 1 + inc_scale_cooldown = "PT5M" + + dec_operator = "LessThan" + dec_threshold = 25 + dec_scale_direction = "Decrease" + dec_scale_type = "ChangeCount" + dec_scale_value = 1 + dec_scale_cooldown = "PT5M" + } + } + + instances = { + CaasIntegration = { + autoscale_override = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "2" + capacity_max = "10" + capacity_def = "2" + } + } + } + CohortDistributionServices = {} + DemographicServices = {} + ExceptionHandling = {} + ParticipantManagementServices = {} + ScreeningValidationService = {} + screeningDataServices = {} + } +} + + +function_apps = { + acr_mi_name = "dtos-cohort-manager-acr-push" + acr_name = "acrukshubdevcohman" + acr_rg_name = "rg-hub-dev-uks-cohman" + + app_insights_name = "appi-int-uks-cohman" + app_insights_rg_name = "rg-cohman-int-uks-audit" + + always_on = true + + cont_registry_use_mi = true + + docker_CI_enable = "true" + docker_env_tag = "integration" + docker_img_prefix = "cohort-manager" + + enable_appsrv_storage = "false" + ftps_state = "Disabled" + https_only = true + remote_debugging_enabled = false + storage_uses_managed_identity = null + worker_32bit = false + + fa_config = { + ReceiveCaasFile = { + name_suffix = "receive-caas-file" + function_endpoint_name = "ReceiveCaasFile" + app_service_plan_key = "CaasIntegration" + db_connection_string = "DtOsDatabaseConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + RetrieveMeshFile = { + name_suffix = "retrieve-mesh-file" + function_endpoint_name = "RetrieveMeshFile" + app_service_plan_key = "CaasIntegration" + key_vault_url = "KeyVaultConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + ProcessCaasFile = { + name_suffix = "process-caas-file" + function_endpoint_name = "processCaasFile" + app_service_plan_key = "CaasIntegration" + app_urls = [ + { + env_var_name = "PMSAddParticipant" + function_app_key = "AddParticipant" + }, + { + env_var_name = "PMSRemoveParticipant" + function_app_key = "RemoveParticipant" + }, + { + env_var_name = "PMSUpdateParticipant" + function_app_key = "UpdateParticipant" + }, + { + env_var_name = "DemographicURI" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + } + ] + } + + AddParticipant = { + name_suffix = "add-participant" + function_endpoint_name = "addParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "DSaddParticipant" + function_app_key = "CreateParticipant" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + } + ] + } + + RemoveParticipant = { + name_suffix = "remove-participant" + function_endpoint_name = "RemoveParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + }, + { + env_var_name = "RemoveCohortDistributionURL" + function_app_key = "RemoveCohortDistributionData" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipant = { + name_suffix = "update-participant" + function_endpoint_name = "updateParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "UpdateParticipant" + function_app_key = "UpdateParticipantDetails" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + } + ] + } + + CreateParticipant = { + name_suffix = "create-participant" + function_endpoint_name = "CreateParticipant" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsEligible = { + name_suffix = "mark-participant-as-eligible" + function_endpoint_name = "markParticipantAsEligible" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsIneligible = { + name_suffix = "mark-participant-as-ineligible" + function_endpoint_name = "markParticipantAsIneligible" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipantDetails = { + name_suffix = "update-participant-details" + function_endpoint_name = "updateParticipantDetails" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + CreateException = { + name_suffix = "create-exception" + function_endpoint_name = "CreateException" + app_service_plan_key = "ExceptionHandling" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + GetValidationExceptions = { + name_suffix = "get-validation-exceptions" + function_endpoint_name = "GetValidationExceptions" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + DemographicDataService = { + name_suffix = "demographic-data-service" + function_endpoint_name = "DemographicDataService" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + FileValidation = { + name_suffix = "file-validation" + function_endpoint_name = "FileValidation" + app_service_plan_key = "ScreeningValidationService" + storage_account_env_var_name = "caasfolder_STORAGE" + storage_containers = [ + { + env_var_name = "inboundBlobName" + container_name = "inbound" + }, + { + env_var_name = "fileExceptions" + container_name = "inbound-poison" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + StaticValidation = { + name_suffix = "static-validation" + function_endpoint_name = "StaticValidation" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "RemoveOldValidationRecord" + function_app_key = "RemoveValidationExceptionData" + } + ] + } + + LookupValidation = { + name_suffix = "lookup-validation" + function_endpoint_name = "LookupValidation" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + DemographicDataManagement = { + name_suffix = "demographic-data-management" + function_endpoint_name = "DemographicDataFunction" + app_service_plan_key = "DemographicServices" + app_urls = [ + { + env_var_name = "DemographicDataServiceURI" + function_app_key = "DemographicDataService" + } + ] + } + + AddCohortDistributionData = { + name_suffix = "add-cohort-distribution-data" + function_endpoint_name = "AddCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortDistributionData = { + name_suffix = "retrieve-cohort-distribution-data" + function_endpoint_name = "RetrieveCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveCohortDistributionData = { + name_suffix = "remove-cohort-distribution-data" + function_endpoint_name = "RemoveCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + TransformDataService = { + name_suffix = "transform-data-service" + function_endpoint_name = "TransformDataService" + app_service_plan_key = "CohortDistributionServices" + app_urls = [] + } + + AllocateServiceProvider = { + name_suffix = "allocate-service-provider" + function_endpoint_name = "AllocateServiceProviderToParticipantByService" + app_service_plan_key = "CohortDistributionServices" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CreateValidationExceptionURL" + function_app_key = "LookupValidation" + } + ] + } + + CreateCohortDistribution = { + name_suffix = "create-cohort-distribution" + function_endpoint_name = "CreateCohortDistribution" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "RetrieveParticipantDataURL" + function_app_key = "RetrieveParticipantData" + }, + { + env_var_name = "AllocateScreeningProviderURL" + function_app_key = "AllocateServiceProvider" + }, + { + env_var_name = "TransformDataServiceURL" + function_app_key = "TransformDataService" + }, + { + env_var_name = "AddCohortDistributionURL" + function_app_key = "AddCohortDistributionData" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "ValidateCohortDistributionRecordURL" + function_app_key = "ValidateCohortDistributionRecord" + } + ] + } + + RetrieveParticipantData = { + name_suffix = "retrieve-participant-data" + function_endpoint_name = "RetrieveParticipantData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + ValidateCohortDistributionRecord = { + name_suffix = "validate-cohort-distribution-record" + function_endpoint_name = "ValidateCohortDistributionRecord" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + } + ] + } + + RetrieveCohortReplay = { + name_suffix = "retrieve-cohort-replay" + function_endpoint_name = "RetrieveCohortReplay" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveValidationExceptionData = { + name_suffix = "remove-validation-exception-data" + function_endpoint_name = "RemoveValidationExceptionData" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortRequestAudit = { + name_suffix = "retrieve-cohort-request-audit" + function_endpoint_name = "RetrieveCohortRequestAudit" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortRequestAudit = { + name_suffix = "retrieve-cohort-request-audit" + function_endpoint_name = "RetrieveCohortRequestAudit" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortRequestAudit = { + name_suffix = "retrieve-cohort-request-audit" + function_endpoint_name = "RetrieveCohortRequestAudit" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + } +} + +function_app_slots = [] + +key_vault = { + disk_encryption = true + soft_del_ret_days = 7 + purge_prot = false + sku_name = "standard" +} + +sqlserver = { + sql_uai_name = "dtos-cohort-manager-sql-adm" + sql_admin_group_name = "sqlsvr_cohman_int_uks_admin" + ad_auth_only = true + + server = { + sqlversion = "12.0" + tlsversion = 1.2 + azure_services_access_enabled = true + } + + # cohman database + dbs = { + cohman = { + db_name_suffix = "DToSDB" + collation = "SQL_Latin1_General_CP1_CI_AS" + licence_type = "LicenseIncluded" + max_gb = 5 + read_scale = false + sku = "S0" + } + } + + fw_rules = {} +} + +storage_accounts = { + fnapp = { + name_suffix = "fnappstor" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = {} + } + file_exceptions = { + name_suffix = "filexptns" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = { + file-exceptions = { + container_name = "file-exceptions" + container_access_type = "private" + } + config = { + container_name = "config" + } + inbound = { + container_name = "inbound" + } + inbound-poison = { + container_name = "inbound-poison" + } + } + } +} diff --git a/infrastructure/tf-core/environments/nft.tfvars b/infrastructure/tf-core/environments/nft.tfvars new file mode 100644 index 00000000..e2d297b6 --- /dev/null +++ b/infrastructure/tf-core/environments/nft.tfvars @@ -0,0 +1,657 @@ +application = "cohman" +environment = "NFT" + +features = { + acr_enabled = false + api_management_enabled = false + event_grid_enabled = false + private_endpoints_enabled = false +} + +tags = { + Project = "Cohort-Manager" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.103.0.0/16" + subnets = {} + } +} + +acr = { + resource_group_key = "cohman" + sku = "Premium" + admin_enabled = false + + uai_name = "dtos-cohort-manager-acr-push" +} + +app_insights = { + name_suffix = "cohman" + resource_group_key = "cohman" + appinsights_type = "web" + + audit_resource_group_key = "audit" +} + +app_service_plan = { + sku_name = "P2v3" + os_type = "Linux" + + autoscale = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "1" + capacity_max = "5" + capacity_def = "1" + + time_grain = "PT1M" + statistic = "Average" + time_window = "PT10M" + time_aggregation = "Average" + + inc_operator = "GreaterThan" + inc_threshold = 70 + inc_scale_direction = "Increase" + inc_scale_type = "ChangeCount" + inc_scale_value = 1 + inc_scale_cooldown = "PT5M" + + dec_operator = "LessThan" + dec_threshold = 25 + dec_scale_direction = "Decrease" + dec_scale_type = "ChangeCount" + dec_scale_value = 1 + dec_scale_cooldown = "PT5M" + } + } +} + +function_apps = { + acr_mi_name = "dtos-cohort-manager-acr-push" + acr_name = "acrukscohmandev" + acr_rg_name = "rg-cohort-manager-dev-uks" + + cont_registry_use_mi = true + + docker_CI_enable = "true" + docker_env_tag = "nft" + docker_img_prefix = "cohort-manager" + + enable_appsrv_storage = "false" + ftps_state = "Disabled" + https_only = true + remote_debugging_enabled = false + worker_32bit = false + + fa_config = { + + ReceiveCaasFile = { + name_suffix = "receive-caas-file" + function_endpoint_name = "ReceiveCaasFile" + db_connection_string = "DtOsDatabaseConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + RetrieveMeshFile = { + name_suffix = "retrieve-mesh-file" + function_endpoint_name = "RetrieveMeshFile" + key_vault_url = "KeyVaultConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + ProcessCaasFile = { + name_suffix = "process-caas-file" + function_endpoint_name = "processCaasFile" + app_urls = [ + { + env_var_name = "PMSAddParticipant" + function_app_key = "AddParticipant" + }, + { + env_var_name = "PMSRemoveParticipant" + function_app_key = "RemoveParticipant" + }, + { + env_var_name = "PMSUpdateParticipant" + function_app_key = "UpdateParticipant" + }, + { + env_var_name = "DemographicURI" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + } + ] + } + + AddParticipant = { + name_suffix = "add-participant" + function_endpoint_name = "addParticipant" + app_urls = [ + { + env_var_name = "DSaddParticipant" + function_app_key = "CreateParticipant" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + } + ] + } + + RemoveParticipant = { + name_suffix = "remove-participant" + function_endpoint_name = "RemoveParticipant" + app_urls = [ + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + }, + { + env_var_name = "RemoveCohortDistributionURL" + function_app_key = "RemoveCohortDistributionData" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipant = { + name_suffix = "update-participant" + function_endpoint_name = "updateParticipant" + app_urls = [ + { + env_var_name = "UpdateParticipant" + function_app_key = "UpdateParticipantDetails" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + } + ] + } + + CreateParticipant = { + name_suffix = "create-participant" + function_endpoint_name = "CreateParticipant" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsEligible = { + name_suffix = "mark-participant-as-eligible" + function_endpoint_name = "markParticipantAsEligible" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsIneligible = { + name_suffix = "mark-participant-as-ineligible" + function_endpoint_name = "markParticipantAsIneligible" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipantDetails = { + name_suffix = "update-participant-details" + function_endpoint_name = "updateParticipantDetails" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + CreateException = { + name_suffix = "create-exception" + function_endpoint_name = "CreateException" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + GetValidationExceptions = { + name_suffix = "get-validation-exceptions" + function_endpoint_name = "GetValidationExceptions" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + DemographicDataService = { + name_suffix = "demographic-data-service" + function_endpoint_name = "DemographicDataService" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + FileValidation = { + name_suffix = "file-validation" + function_endpoint_name = "FileValidation" + storage_account_env_var_name = "caasfolder_STORAGE" + storage_containers = [ + { + env_var_name = "inboundBlobName" + container_name = "inbound" + }, + { + env_var_name = "fileExceptions" + container_name = "inbound-poison" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + StaticValidation = { + name_suffix = "static-validation" + function_endpoint_name = "StaticValidation" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "RemoveOldValidationRecord" + function_app_key = "RemoveValidationExceptionData" + } + ] + } + + LookupValidation = { + name_suffix = "lookup-validation" + function_endpoint_name = "LookupValidation" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + DemographicDataManagement = { + name_suffix = "demographic-data-management" + function_endpoint_name = "DemographicDataFunction" + app_urls = [ + { + env_var_name = "DemographicDataServiceURI" + function_app_key = "DemographicDataService" + } + ] + } + + AddCohortDistributionData = { + name_suffix = "add-cohort-distribution-data" + function_endpoint_name = "AddCohortDistributionData" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortDistributionData = { + name_suffix = "retrieve-cohort-distribution-data" + function_endpoint_name = "RetrieveCohortDistributionData" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveCohortDistributionData = { + name_suffix = "remove-cohort-distribution-data" + function_endpoint_name = "RemoveCohortDistributionData" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + TransformDataService = { + name_suffix = "transform-data-service" + function_endpoint_name = "TransformDataService" + app_urls = [] + } + + AllocateServiceProvider = { + name_suffix = "allocate-service-provider" + function_endpoint_name = "AllocateServiceProviderToParticipantByService" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CreateValidationExceptionURL" + function_app_key = "LookupValidation" + } + ] + } + + CreateCohortDistribution = { + name_suffix = "create-cohort-distribution" + function_endpoint_name = "CreateCohortDistribution" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "RetrieveParticipantDataURL" + function_app_key = "RetrieveParticipantData" + }, + { + env_var_name = "AllocateScreeningProviderURL" + function_app_key = "AllocateServiceProvider" + }, + { + env_var_name = "TransformDataServiceURL" + function_app_key = "TransformDataService" + }, + { + env_var_name = "AddCohortDistributionURL" + function_app_key = "AddCohortDistributionData" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "ValidateCohortDistributionRecordURL" + function_app_key = "ValidateCohortDistributionRecord" + } + ] + } + + RetrieveParticipantData = { + name_suffix = "retrieve-participant-data" + function_endpoint_name = "RetrieveParticipantData" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + ValidateCohortDistributionRecord = { + name_suffix = "validate-cohort-distribution-record" + function_endpoint_name = "ValidateCohortDistributionRecord" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + } + ] + } + + RetrieveCohortReplay = { + name_suffix = "retrieve-cohort-replay" + function_endpoint_name = "RetrieveCohortReplay" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveValidationExceptionData = { + name_suffix = "remove-validation-exception-data" + function_endpoint_name = "RemoveValidationExceptionData" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortRequestAudit = { + name_suffix = "retrieve-cohort-request-audit" + function_endpoint_name = "RetrieveCohortRequestAudit" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + } +} + +key_vault = { + #name_suffix = "" + resource_group_key = "cohman" + disk_encryption = true + soft_del_ret_days = 7 + purge_prot = false + sku_name = "standard" +} + +law = { + name_suffix = "cohman" + resource_group_key = "cohman" + + law_sku = "PerGB2018" + retention_days = 30 + + audit_resource_group_key = "audit" +} + +sqlserver = { + sql_uai_name = "dtos-cohort-manager-sql-adm" + sql_adm_group_name = "sqlsvr_cohman_nft_uks_admin" + ad_auth_only = true + + server = { + resource_group_key = "cohman" + sqlversion = "12.0" + tlsversion = 1.2 + azure_services_access_enabled = true + } + + # cohman database + dbs = { + cohman = { + db_name_suffix = "DToSDB" + collation = "SQL_Latin1_General_CP1_CI_AS" + licence_type = "LicenseIncluded" + max_gb = 5 + read_scale = false + sku = "S0" + } + } + + fw_rules = { + passthrough = { + fw_rule_name = "AllowAccessFromAzure" + start_ip = "0.0.0.0" + end_ip = "0.0.0.0" + } + } +} + +storage_accounts = { + resource_group_key = "cohman" + sa_config = { + fnapp = { + name_suffix = "fnappstor" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = true + } + + file_exceptions = { + name_suffix = "filexptns" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = true + } + } + + cont_config = { + file-exceptions = { + sa_key = "file_exceptions" + cont_name = "file-exceptions" + cont_access_type = "private" + } + config = { + sa_key = "file_exceptions" + cont_name = "config" + cont_access_type = "private" + } + inbound = { + sa_key = "file_exceptions" + cont_name = "inbound" + cont_access_type = "private" + } + + inbound-poison = { + sa_key = "file_exceptions" + cont_name = "inbound-poison" + cont_access_type = "private" + } + } +} \ No newline at end of file diff --git a/infrastructure/tf-core/environments/preprod.tfvars b/infrastructure/tf-core/environments/preprod.tfvars new file mode 100644 index 00000000..0fd574ea --- /dev/null +++ b/infrastructure/tf-core/environments/preprod.tfvars @@ -0,0 +1,716 @@ +application = "cohman" +environment = "PRE" + +features = { + acr_enabled = false + api_management_enabled = false + event_grid_enabled = false + private_endpoints_enabled = true + private_service_connection_is_manual = false + public_network_access_enabled = false +} + +tags = { + Project = "Cohort-Manager" +} + +regions = { + uksouth = { + is_primary_region = true + address_space = "10.2.0.0/16" + connect_peering = true + subnets = { + apps = { + cidr_newbits = 8 + cidr_offset = 2 + delegation_name = "Microsoft.Web/serverFarms" + service_delegation_name = "Microsoft.Web/serverFarms" + service_delegation_actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + pep = { + cidr_newbits = 8 + cidr_offset = 1 + } + sql = { + cidr_newbits = 8 + cidr_offset = 3 + } + } + } +} + +routes = { + uksouth = { + application_rules = [] + nat_rules = [] + network_rules = [ + { + name = "AllowCohmanToAudit" + priority = 800 + action = "Allow" + rule_name = "CohmanToAudit" + source_addresses = ["10.2.0.0/16"] # will be populated with the cohort manager subnet address space + destination_addresses = ["10.3.0.0/16"] # will be populated with the audit subnet address space + protocols = ["TCP", "UDP"] + destination_ports = ["443"] + } + ] + route_table_routes = [ + { + name = "CohmanToAudit" + address_prefix = "" # will be populated with the cohort manager subnet address space + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = "" # will be populated with the Firewall Private IP address + } + ] + } +} + +app_service_plan = { + os_type = "Linux" + sku_name = "P2v3" + vnet_integration_enabled = true + + autoscale = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "1" + capacity_max = "5" + capacity_def = "1" + + time_grain = "PT1M" + statistic = "Average" + time_window = "PT10M" + time_aggregation = "Average" + + inc_operator = "GreaterThan" + inc_threshold = 70 + inc_scale_direction = "Increase" + inc_scale_type = "ChangeCount" + inc_scale_value = 1 + inc_scale_cooldown = "PT5M" + + dec_operator = "LessThan" + dec_threshold = 25 + dec_scale_direction = "Decrease" + dec_scale_type = "ChangeCount" + dec_scale_value = 1 + dec_scale_cooldown = "PT5M" + } + } + + instances = { + CaasIntegration = { + autoscale_override = { + memory_percentage = { + metric = "MemoryPercentage" + + capacity_min = "2" + capacity_max = "10" + capacity_def = "2" + } + } + } + CohortDistributionServices = {} + DemographicServices = {} + ExceptionHandling = {} + ParticipantManagementServices = {} + ScreeningValidationService = {} + screeningDataServices = {} + } +} + +function_apps = { + acr_mi_name = "dtos-cohort-manager-acr-push" + acr_name = "acrukshubprodcohman" + acr_rg_name = "rg-hub-prod-uks-cohman" + + app_insights_name = "appi-pre-uks-cohman" + app_insights_rg_name = "rg-cohman-pre-uks-audit" + + always_on = true + + cont_registry_use_mi = true + + docker_CI_enable = "true" + docker_env_tag = "preprod" + docker_img_prefix = "cohort-manager" + + enable_appsrv_storage = "false" + ftps_state = "Disabled" + https_only = true + remote_debugging_enabled = false + storage_uses_managed_identity = null + worker_32bit = false + + fa_config = { + ReceiveCaasFile = { + name_suffix = "receive-caas-file" + function_endpoint_name = "ReceiveCaasFile" + app_service_plan_key = "CaasIntegration" + db_connection_string = "DtOsDatabaseConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + RetrieveMeshFile = { + name_suffix = "retrieve-mesh-file" + function_endpoint_name = "RetrieveMeshFile" + app_service_plan_key = "CaasIntegration" + key_vault_url = "KeyVaultConnectionString" + storage_account_env_var_name = "caasfolder_STORAGE" + app_urls = [ + { + env_var_name = "targetFunction" + function_app_key = "ProcessCaasFile" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "FileValidationURL" + function_app_key = "FileValidation" + } + ] + } + + ProcessCaasFile = { + name_suffix = "process-caas-file" + function_endpoint_name = "processCaasFile" + app_service_plan_key = "CaasIntegration" + app_urls = [ + { + env_var_name = "PMSAddParticipant" + function_app_key = "AddParticipant" + }, + { + env_var_name = "PMSRemoveParticipant" + function_app_key = "RemoveParticipant" + }, + { + env_var_name = "PMSUpdateParticipant" + function_app_key = "UpdateParticipant" + }, + { + env_var_name = "DemographicURI" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + } + ] + } + + AddParticipant = { + name_suffix = "add-participant" + function_endpoint_name = "addParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "DSaddParticipant" + function_app_key = "CreateParticipant" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + } + ] + } + + RemoveParticipant = { + name_suffix = "remove-participant" + function_endpoint_name = "RemoveParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + }, + { + env_var_name = "RemoveCohortDistributionURL" + function_app_key = "RemoveCohortDistributionData" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipant = { + name_suffix = "update-participant" + function_endpoint_name = "updateParticipant" + app_service_plan_key = "ParticipantManagementServices" + app_urls = [ + { + env_var_name = "UpdateParticipant" + function_app_key = "UpdateParticipantDetails" + }, + { + env_var_name = "CohortDistributionServiceURL" + function_app_key = "CreateCohortDistribution" + }, + { + env_var_name = "DemographicURIGet" + function_app_key = "DemographicDataManagement" + }, + { + env_var_name = "StaticValidationURL" + function_app_key = "StaticValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "DSmarkParticipantAsEligible" + function_app_key = "MarkParticipantAsEligible" + }, + { + env_var_name = "markParticipantAsIneligible" + function_app_key = "MarkParticipantAsIneligible" + } + ] + } + + CreateParticipant = { + name_suffix = "create-participant" + function_endpoint_name = "CreateParticipant" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsEligible = { + name_suffix = "mark-participant-as-eligible" + function_endpoint_name = "markParticipantAsEligible" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + MarkParticipantAsIneligible = { + name_suffix = "mark-participant-as-ineligible" + function_endpoint_name = "markParticipantAsIneligible" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + UpdateParticipantDetails = { + name_suffix = "update-participant-details" + function_endpoint_name = "updateParticipantDetails" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + CreateException = { + name_suffix = "create-exception" + function_endpoint_name = "CreateException" + app_service_plan_key = "ExceptionHandling" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + GetValidationExceptions = { + name_suffix = "get-validation-exceptions" + function_endpoint_name = "GetValidationExceptions" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + DemographicDataService = { + name_suffix = "demographic-data-service" + function_endpoint_name = "DemographicDataService" + app_service_plan_key = "screeningDataServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + FileValidation = { + name_suffix = "file-validation" + function_endpoint_name = "FileValidation" + app_service_plan_key = "ScreeningValidationService" + storage_account_env_var_name = "caasfolder_STORAGE" + storage_containers = [ + { + env_var_name = "inboundBlobName" + container_name = "inbound" + }, + { + env_var_name = "fileExceptions" + container_name = "inbound-poison" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + StaticValidation = { + name_suffix = "static-validation" + function_endpoint_name = "StaticValidation" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "RemoveOldValidationRecord" + function_app_key = "RemoveValidationExceptionData" + } + ] + } + + LookupValidation = { + name_suffix = "lookup-validation" + function_endpoint_name = "LookupValidation" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + storage_containers = [ + { + env_var_name = "BlobContainerName" + container_name = "config" + } + ] + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + DemographicDataManagement = { + name_suffix = "demographic-data-management" + function_endpoint_name = "DemographicDataFunction" + app_service_plan_key = "DemographicServices" + app_urls = [ + { + env_var_name = "DemographicDataServiceURI" + function_app_key = "DemographicDataService" + } + ] + } + + AddCohortDistributionData = { + name_suffix = "add-cohort-distribution-data" + function_endpoint_name = "AddCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortDistributionData = { + name_suffix = "retrieve-cohort-distribution-data" + function_endpoint_name = "RetrieveCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveCohortDistributionData = { + name_suffix = "remove-cohort-distribution-data" + function_endpoint_name = "RemoveCohortDistributionData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + TransformDataService = { + name_suffix = "transform-data-service" + function_endpoint_name = "TransformDataService" + app_service_plan_key = "CohortDistributionServices" + app_urls = [] + } + + AllocateServiceProvider = { + name_suffix = "allocate-service-provider" + function_endpoint_name = "AllocateServiceProviderToParticipantByService" + app_service_plan_key = "CohortDistributionServices" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "CreateValidationExceptionURL" + function_app_key = "LookupValidation" + } + ] + } + + CreateCohortDistribution = { + name_suffix = "create-cohort-distribution" + function_endpoint_name = "CreateCohortDistribution" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "RetrieveParticipantDataURL" + function_app_key = "RetrieveParticipantData" + }, + { + env_var_name = "AllocateScreeningProviderURL" + function_app_key = "AllocateServiceProvider" + }, + { + env_var_name = "TransformDataServiceURL" + function_app_key = "TransformDataService" + }, + { + env_var_name = "AddCohortDistributionURL" + function_app_key = "AddCohortDistributionData" + }, + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + }, + { + env_var_name = "ValidateCohortDistributionRecordURL" + function_app_key = "ValidateCohortDistributionRecord" + } + ] + } + + RetrieveParticipantData = { + name_suffix = "retrieve-participant-data" + function_endpoint_name = "RetrieveParticipantData" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [] + } + + ValidateCohortDistributionRecord = { + name_suffix = "validate-cohort-distribution-record" + function_endpoint_name = "ValidateCohortDistributionRecord" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "LookupValidationURL" + function_app_key = "LookupValidation" + } + ] + } + + RetrieveCohortReplay = { + name_suffix = "retrieve-cohort-replay" + function_endpoint_name = "RetrieveCohortReplay" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RemoveValidationExceptionData = { + name_suffix = "remove-validation-exception-data" + function_endpoint_name = "RemoveValidationExceptionData" + app_service_plan_key = "ScreeningValidationService" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + + RetrieveCohortRequestAudit = { + name_suffix = "retrieve-cohort-request-audit" + function_endpoint_name = "RetrieveCohortRequestAudit" + app_service_plan_key = "CohortDistributionServices" + db_connection_string = "DtOsDatabaseConnectionString" + app_urls = [ + { + env_var_name = "ExceptionFunctionURL" + function_app_key = "CreateException" + } + ] + } + } +} + +function_app_slots = [ + { + function_app_slots_name = "staging" + function_app_slot_enabled = true + } +] + +key_vault = { + disk_encryption = true + soft_del_ret_days = 7 + purge_prot = false + sku_name = "standard" +} + +sqlserver = { + sql_uai_name = "dtos-cohort-manager-sql-adm" + sql_admin_group_name = "sqlsvr_cohman_preprod_uks_admin" + ad_auth_only = true + + server = { + sqlversion = "12.0" + tlsversion = 1.2 + azure_services_access_enabled = true + } + + # cohman database + dbs = { + cohman = { + db_name_suffix = "DToSDB" + collation = "SQL_Latin1_General_CP1_CI_AS" + licence_type = "LicenseIncluded" + max_gb = 5 + read_scale = false + sku = "S0" + } + } + + fw_rules = {} +} + +storage_accounts = { + fnapp = { + name_suffix = "fnappstor" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = {} + } + file_exceptions = { + name_suffix = "filexptns" + account_tier = "Standard" + replication_type = "LRS" + public_network_access_enabled = false + containers = { + file-exceptions = { + container_name = "file-exceptions" + container_access_type = "private" + } + config = { + container_name = "config" + } + inbound = { + container_name = "inbound" + } + inbound-poison = { + container_name = "inbound-poison" + } + } + } +} diff --git a/infrastructure/tf-core/function_app.tf b/infrastructure/tf-core/function_app.tf new file mode 100644 index 00000000..9006ab05 --- /dev/null +++ b/infrastructure/tf-core/function_app.tf @@ -0,0 +1,261 @@ +module "functionapp" { + for_each = local.function_app_map + + source = "../../../dtos-devops-templates/infrastructure/modules/function-app" + + function_app_name = "${module.regions_config[each.value.region_key].names.function-app}-si-${lower(each.value.function_config.name_suffix)}" + resource_group_name = azurerm_resource_group.core[each.value.region_key].name + location = each.value.region_key + + log_analytics_workspace_id = data.terraform_remote_state.audit.outputs.log_analytics_workspace_id[local.primary_region] + + app_settings = local.app_settings[each.value.region_key][each.value.function_key] + + public_network_access_enabled = var.features.public_network_access_enabled + vnet_integration_subnet_id = module.subnets["${module.regions_config[each.value.region_key].names.subnet}-apps"].id + + rbac_role_assignments = local.rbac_role_assignments[each.value.region_key] + + asp_id = module.app-service-plan["${each.value.function_config.app_service_plan_key}-${each.value.region_key}"].app_service_plan_id + + # Use the storage account assigned identity for the Function Apps: + storage_account_name = module.storage["fnapp-${each.value.region_key}"].storage_account_name + storage_account_access_key = var.function_apps.storage_uses_managed_identity == true ? null : module.storage["fnapp-${each.value.region_key}"].storage_account_primary_access_key + storage_uses_managed_identity = var.function_apps.storage_uses_managed_identity + + # Connection string for Application Insights: + ai_connstring = data.azurerm_application_insights.ai.connection_string + + # Use the ACR assigned identity for the Function Apps: + cont_registry_use_mi = var.function_apps.cont_registry_use_mi + + # Other Function App configuration settings: + always_on = var.function_apps.always_on + worker_32bit = var.function_apps.worker_32bit + + acr_mi_client_id = data.azurerm_user_assigned_identity.acr_mi.client_id + acr_login_server = data.azurerm_container_registry.acr.login_server + + # Use the ACR assigned identity for the Function Apps too: + assigned_identity_ids = var.function_apps.cont_registry_use_mi ? [data.azurerm_user_assigned_identity.acr_mi.id] : [] + + image_tag = var.function_apps.docker_env_tag + image_name = "${var.function_apps.docker_img_prefix}-${lower(each.value.function_config.name_suffix)}" + + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids = [data.terraform_remote_state.hub.outputs.private_dns_zone_app_services[each.value.region_key].private_dns_zone.id] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.value.region_key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.value.region_key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + + function_app_slots = var.function_app_slots + + tags = var.tags +} + + +/* -------------------------------------------------------------------------------------------------- + Function App Access Policies +-------------------------------------------------------------------------------------------------- */ + +# Loop through the Key Vault URLs for each region and create the Key Vault Access Policies for each Function App: +resource "azurerm_key_vault_access_policy" "functionapp" { + for_each = local.keyvault_function_app_object_ids_map + + key_vault_id = each.value.key_vault_id + object_id = each.value.function_app_sami_id + tenant_id = data.azurerm_client_config.current.tenant_id + + secret_permissions = [ + "Get", + "List" + ] + + certificate_permissions = [ + "Get", + "List" + ] +} + +/* -------------------------------------------------------------------------------------------------- + RBAC roles to assign to the Function Apps +-------------------------------------------------------------------------------------------------- */ +locals { + rbac_role_assignments = { + for region_key in keys(module.regions_config) : + region_key => concat( + [ + for _, role_value in local.rbac_roles_storage : { + role_definition_name = role_value + scope = module.storage["fnapp-${region_key}"].storage_account_id + } + ], + [ + for _, role_value in local.rbac_roles_database : { + role_definition_name = role_value + scope = module.azure_sql_server[region_key].sql_server_id + } + ] + ) + } +} + +/* -------------------------------------------------------------------------------------------------- + Local variables used to create the Environment Variables for the Function Apps +-------------------------------------------------------------------------------------------------- */ +locals { + + # Create a map of the function apps config per region + function_apps_config = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => value + } + } + + + # To Do - move these directly into the tfvars file as a map as this way limits adding extra values + # WEBSITE_PULL_IMAGE_OVER_VNET reuses the private_endpoints_enabled variable as these settings are implicitly coupled. + global_app_settings = { + DOCKER_ENABLE_CI = var.function_apps.docker_CI_enable + REMOTE_DEBUGGING_ENABLED = var.function_apps.remote_debugging_enabled + WEBSITES_ENABLE_APP_SERVICE_STORAGE = var.function_apps.enable_appsrv_storage + WEBSITE_PULL_IMAGE_OVER_VNET = var.features.private_endpoints_enabled + } + + # Create a map of the function app urls for each function app + env_vars_app_urls = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => { + for app_url_key, app_url_value in value.app_urls : + app_url_value.env_var_name => "https://${module.regions_config[region_key].names.function-app}-si-${var.function_apps.fa_config[app_url_value.function_app_key].name_suffix}.azurewebsites.net/api/${var.function_apps.fa_config[app_url_value.function_app_key].function_endpoint_name}" + + } + } + } + + # Create a map of the storage accounts for each function app as defined in the storage_account_env_var_name attribute + # Should not need the following entry if we are using managed identity for storage access, but the C# code is not yet + # ready to support this so we are using the storage account key for now. + env_vars_storage_accounts = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => length(value.storage_account_env_var_name) > 0 ? { + "${value.storage_account_env_var_name}" = module.storage["file_exceptions-${region_key}"].storage_account_primary_connection_string + } : null + } + } + + env_vars_storage_accounts_private_blob = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => length(value.storage_account_env_var_name) > 0 ? { + "${value.storage_account_env_var_name}__blobServiceUri" = "https://${module.storage["file_exceptions-${region_key}"].storage_account_name}.blob.core.windows.net" + } : null + } + if var.features.private_endpoints_enabled == true + } + + env_vars_storage_accounts_private_queue = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => length(value.storage_account_env_var_name) > 0 ? { + "${value.storage_account_env_var_name}__queueServiceUri" = "https://${module.storage["file_exceptions-${region_key}"].storage_account_name}.queue.core.windows.net" + } : null + } + if var.features.private_endpoints_enabled == true + } + + # Create a map of the storage containers for each function app as defined in the storage_containers attribute + env_vars_storage_containers = { + for key, value in var.function_apps.fa_config : + key => length(value.storage_containers) > 0 ? { + for container_key, container_value in value.storage_containers : + container_value.env_var_name => container_value.container_name + } : null + } + #sqlsvr-serins-dev-uks.database.windows.net + #Server=sqlsvr-serins-dev-uks.database.windows.net; Authentication=Active Directory Managed Identity; Database=DToSDB + # Create a map of the database connection strings for each function app that requires one + env_vars_database_connection_strings = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => length(value.db_connection_string) > 0 ? { + "${value.db_connection_string}" = "Server=${module.regions_config[region_key].names.sql-server}.database.windows.net; Authentication=Active Directory Managed Identity; Database=${var.sqlserver.dbs.serins.db_name_suffix}" } + : null + } + } + + # Create a map of the key vault urls for each function app that requires one + env_vars_key_vault_urls = { + for region_key, region_value in module.regions_config : + region_key => { + for key, value in var.function_apps.fa_config : + key => length(value.key_vault_url) > 0 ? { + "${value.key_vault_url}" = module.key_vault[region_key].key_vault_url } + : null + } + } + + # Merge the local maps into a single map taking care to remove any null values and to loop round each region and each function app where necessary: + app_settings = { + for region_key, region_value in module.regions_config : + region_key => { + for app_key, app_value in var.function_apps.fa_config : + app_key => merge( + local.global_app_settings, + try(local.env_vars_app_urls[region_key][app_key], {}), + try(local.env_vars_storage_accounts[region_key][app_key], {}), + try(local.env_vars_storage_accounts_private_blob[region_key][app_key], {}), + try(local.env_vars_storage_accounts_private_queue[region_key][app_key], {}), + try(local.env_vars_storage_containers[app_key], {}), + try(local.env_vars_database_connection_strings[region_key][app_key], {}), + try(local.env_vars_key_vault_urls[region_key][app_key], {}) + ) + } + } + + # Finaly build a "super map" of all the app settings for each function app in each region + function_app_map = { + for value in flatten([ + for region_key, region_functions in local.function_apps_config : [ + for function_key, function_config in region_functions : { + region_key = region_key + function_key = function_key + function_config = function_config + } + ] + ]) : "${value.function_key}-${value.region_key}" => value + } + + # Create a flat list for the key vault access policy resource contianing just the details + # for functions that have key vault urls + keyvault_function_app_object_ids = flatten([ + for region_key, region_value in module.regions_config : + [ + for function_key, function_value in local.env_vars_key_vault_urls[region_key] : + { + region_key = region_key + function_key = function_key + key_vault_id = module.key_vault[region_key].key_vault_id + function_app_sami_id = module.functionapp["${function_key}-${region_key}"].function_app_sami_id + } + if function_value != null + ] + ]) + # Project the above list into a map with unique keys for consumption in a for_each meta argument + # (although in this case we don't actually need the key as we will create everything from the list as-is) + keyvault_function_app_object_ids_map = { + for value in local.keyvault_function_app_object_ids : "${value.function_key}-${value.region_key}" => value + } +} diff --git a/infrastructure/tf-core/key_vault.tf b/infrastructure/tf-core/key_vault.tf new file mode 100644 index 00000000..5db7b02b --- /dev/null +++ b/infrastructure/tf-core/key_vault.tf @@ -0,0 +1,25 @@ +module "key_vault" { + for_each = var.key_vault != {} ? var.regions : {} + + source = "../../../dtos-devops-templates/infrastructure/modules/key-vault" + + name = module.regions_config[each.key].names.key-vault + resource_group_name = azurerm_resource_group.core[each.key].name + location = each.key + + disk_encryption = var.key_vault.disk_encryption + soft_delete_retention = var.key_vault.soft_del_ret_days + purge_protection_enabled = var.key_vault.purge_prot + sku_name = var.key_vault.sku_name + + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids_keyvault = [data.terraform_remote_state.hub.outputs.private_dns_zone_key_vault[each.key].private_dns_zone.id] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + + tags = var.tags +} diff --git a/infrastructure/tf-core/locals.tf b/infrastructure/tf-core/locals.tf new file mode 100644 index 00000000..5c469bd0 --- /dev/null +++ b/infrastructure/tf-core/locals.tf @@ -0,0 +1,3 @@ +locals { + primary_region = [for k, v in var.regions : k if v.is_primary_region][0] +} diff --git a/infrastructure/tf-core/network_routing.tf b/infrastructure/tf-core/network_routing.tf new file mode 100644 index 00000000..c6a346cf --- /dev/null +++ b/infrastructure/tf-core/network_routing.tf @@ -0,0 +1,82 @@ +module "firewall_policy_rule_collection_group" { + for_each = var.routes + + source = "../../../dtos-devops-templates/infrastructure/modules/firewall-rule-collection-group" + + name = "${module.regions_config[each.key].names.firewall}-policy-rule-collection-group" + firewall_policy_id = data.terraform_remote_state.hub.outputs.firewall_policy_id[each.key] + priority = each.value.firewall_policy_priority + + network_rule_collection = [ + for rule_key, rule_val in each.value.network_rules : { + name = rule_val.name + priority = rule_val.priority + action = rule_val.action + rule_name = rule_val.rule_name + source_addresses = rule_val.source_addresses + destination_addresses = rule_val.destination_addresses + protocols = rule_val.protocols + destination_ports = rule_val.destination_ports + } + ] + +} + +module "route_table" { + for_each = var.routes + + source = "../../../dtos-devops-templates/infrastructure/modules/route-table" + + name = module.regions_config[each.key].names.route-table + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + location = each.key + + bgp_route_propagation_enabled = each.value.bgp_route_propagation_enabled + + routes = [ + for route_key, route_val in each.value.route_table_routes_to_audit : { + name = route_val.name + address_prefix = route_val.address_prefix + next_hop_type = route_val.next_hop_type + next_hop_in_ip_address = route_val.next_hop_in_ip_address == "" ? data.terraform_remote_state.hub.outputs.firewall_private_ip_addresses[each.key] : route_val.next_hop_in_ip_address + } + ] + + subnet_ids = [ + module.subnets["${module.regions_config[each.key].names.subnet}-apps"].id, + module.subnets["${module.regions_config[each.key].names.subnet}-pep"].id + ] + + tags = var.tags +} + +module "route_table_audit" { + for_each = var.routes + + providers = { + azurerm = azurerm.audit + } + + source = "../../../dtos-devops-templates/infrastructure/modules/route-table" + + name = module.regions_config[each.key].names.route-table + resource_group_name = "${module.regions_config[each.key].names.resource-group}-audit-networking" + location = each.key + + bgp_route_propagation_enabled = each.value.bgp_route_propagation_enabled + + routes = [ + for route_key, route_val in each.value.route_table_routes_from_audit : { + name = route_val.name + address_prefix = route_val.address_prefix + next_hop_type = route_val.next_hop_type + next_hop_in_ip_address = route_val.next_hop_in_ip_address == "" ? data.terraform_remote_state.hub.outputs.firewall_private_ip_addresses[each.key] : route_val.next_hop_in_ip_address + } + ] + + subnet_ids = [ + data.azurerm_subnet.subnet_audit_pep[each.key].id + ] + + tags = var.tags +} diff --git a/infrastructure/tf-core/networking.tf b/infrastructure/tf-core/networking.tf new file mode 100644 index 00000000..f2741b3e --- /dev/null +++ b/infrastructure/tf-core/networking.tf @@ -0,0 +1,118 @@ +resource "azurerm_resource_group" "rg_vnet" { + for_each = var.regions + + name = "${module.regions_config[each.key].names.resource-group}-networking" + location = each.key +} + +resource "azurerm_resource_group" "rg_private_endpoints" { + for_each = var.features.private_endpoints_enabled ? var.regions : {} + + name = "${module.regions_config[each.key].names.resource-group}-private-endpoints" + location = each.key +} + +module "vnet" { + for_each = var.regions + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet" + + name = module.regions_config[each.key].names.virtual-network + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + location = each.key + vnet_address_space = each.value.address_space + + dns_servers = [data.terraform_remote_state.hub.outputs.private_dns_resolver_inbound_ips[each.key].private_dns_resolver_ip] + + tags = var.tags +} + +/*-------------------------------------------------------------------------------------------------- + Create Subnets +--------------------------------------------------------------------------------------------------*/ + +locals { + # Expand a flattened list of objects for all subnets (allows nested for loops) + subnets_flatlist = flatten([ + for key, val in var.regions : [ + for subnet_key, subnet in val.subnets : merge({ + vnet_key = key + subnet_name = coalesce(subnet.name, "${module.regions_config[key].names.subnet}-${subnet_key}") + nsg_name = "${module.regions_config[key].names.network-security-group}-${subnet_key}" + nsg_rules = lookup(var.network_security_group_rules, subnet_key, []) + create_nsg = coalesce(subnet.create_nsg, true) + address_prefixes = cidrsubnet(val.address_space, subnet.cidr_newbits, subnet.cidr_offset) + }, subnet) # include all the declared key/value pairs for a specific subnet + ] + ]) + # Project the above list into a map with unique keys for consumption in a for_each meta argument + subnets_map = { for subnet in local.subnets_flatlist : subnet.subnet_name => subnet } +} + +module "subnets" { + for_each = local.subnets_map + + source = "../../../dtos-devops-templates/infrastructure/modules/subnet" + + name = each.value.subnet_name + location = module.vnet[each.value.vnet_key].vnet.location + network_security_group_name = each.value.nsg_name + network_security_group_nsg_rules = each.value.nsg_rules + create_nsg = each.value.create_nsg + resource_group_name = module.vnet[each.value.vnet_key].vnet.resource_group_name + vnet_name = module.vnet[each.value.vnet_key].name + address_prefixes = [each.value.address_prefixes] + default_outbound_access_enabled = true + private_endpoint_network_policies = "Disabled" # Default as per compliance requirements + + delegation_name = each.value.delegation_name != null ? each.value.delegation_name : "" + service_delegation_name = each.value.service_delegation_name != null ? each.value.service_delegation_name : "" + service_delegation_actions = each.value.service_delegation_actions != null ? each.value.service_delegation_actions : [] + + tags = var.tags +} + + + +/*-------------------------------------------------------------------------------------------------- + Create peering +--------------------------------------------------------------------------------------------------*/ + +module "peering_spoke_hub" { + # loop through regions and only create peering if connect_peering is set to true + for_each = { for key, val in var.regions : key => val if val.connect_peering == true } + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet-peering" + + name = "${module.regions_config[each.key].names.virtual-network}-to-hub-peering" + resource_group_name = azurerm_resource_group.rg_vnet[each.key].name + vnet_name = module.vnet[each.key].vnet.name + remote_vnet_id = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].vnet.id + + allow_virtual_network_access = true + allow_forwarded_traffic = true + allow_gateway_transit = false + + use_remote_gateways = false +} + +module "peering_hub_spoke" { + for_each = { for key, val in var.regions : key => val if val.connect_peering == true } + + providers = { + azurerm = azurerm.hub + } + + source = "../../../dtos-devops-templates/infrastructure/modules/vnet-peering" + + name = "hub-to-${module.regions_config[each.key].names.virtual-network}-peering" + resource_group_name = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].vnet.resource_group_name + vnet_name = data.terraform_remote_state.hub.outputs.vnets_hub[each.key].name + remote_vnet_id = module.vnet[each.key].vnet.id + + allow_virtual_network_access = true + allow_forwarded_traffic = true + allow_gateway_transit = false + + use_remote_gateways = false +} diff --git a/infrastructure/tf-core/providers.tf b/infrastructure/tf-core/providers.tf new file mode 100644 index 00000000..53d860dd --- /dev/null +++ b/infrastructure/tf-core/providers.tf @@ -0,0 +1,35 @@ +terraform { + backend "azurerm" {} + required_version = ">= 1.9.2" + required_providers { + azurerm = { + source = "hashicorp/azurerm" + # version = ">= 4.2.0" + version = "= 3.112.0" + } + azuread = { + source = "hashicorp/azuread" + version = "2.53.1" + } + random = "~> 3.5.1" + } +} + +provider "azurerm" { + subscription_id = var.TARGET_SUBSCRIPTION_ID + features {} +} + +provider "azurerm" { + alias = "audit" + subscription_id = var.AUDIT_SUBSCRIPTION_ID + features {} +} + +provider "azurerm" { + alias = "hub" + subscription_id = var.HUB_SUBSCRIPTION_ID + features {} +} + +provider "azuread" {} diff --git a/infrastructure/tf-core/rbac.tf b/infrastructure/tf-core/rbac.tf new file mode 100644 index 00000000..cdbc7039 --- /dev/null +++ b/infrastructure/tf-core/rbac.tf @@ -0,0 +1,11 @@ +locals { + rbac_roles_storage = { + storage_account_contributor = "Storage Account Contributor" + storage_blob_data_owner = "Storage Blob Data Owner" + storage_queue_data_contributor = "Storage Queue Data Contributor" + } + + rbac_roles_database = { + sql_contributor = "Contributor" + } +} diff --git a/infrastructure/tf-core/sql_server.tf b/infrastructure/tf-core/sql_server.tf new file mode 100644 index 00000000..2f373698 --- /dev/null +++ b/infrastructure/tf-core/sql_server.tf @@ -0,0 +1,41 @@ +module "azure_sql_server" { + for_each = var.sqlserver != {} ? var.regions : {} + + source = "../../../dtos-devops-templates/infrastructure/modules/sql-server" + + # Azure SQL Server + name = module.regions_config[each.key].names.sql-server + resource_group_name = azurerm_resource_group.core[each.key].name + location = each.key + + sqlversion = var.sqlserver.server.sqlversion + tlsver = var.sqlserver.server.tlsversion + kv_id = module.key_vault[each.key].key_vault_id + + sql_uai_name = var.sqlserver.sql_uai_name + sql_admin_group_name = var.sqlserver.sql_admin_group_name + sql_admin_object_id = data.azuread_group.sql_admin_group.object_id + ad_auth_only = var.sqlserver.ad_auth_only + + # Default database + db_name_suffix = var.sqlserver.dbs.serins.db_name_suffix + collation = var.sqlserver.dbs.serins.collation + licence_type = var.sqlserver.dbs.serins.licence_type + max_gb = var.sqlserver.dbs.serins.max_gb + read_scale = var.sqlserver.dbs.serins.read_scale + sku = var.sqlserver.dbs.serins.sku + + # FW Rules + firewall_rules = var.sqlserver.fw_rules + + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids_sql = [data.terraform_remote_state.hub.outputs.private_dns_zone_azure_sql[each.key].private_dns_zone.id] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + + tags = var.tags +} diff --git a/infrastructure/tf-core/storage.tf b/infrastructure/tf-core/storage.tf new file mode 100644 index 00000000..ff9a6226 --- /dev/null +++ b/infrastructure/tf-core/storage.tf @@ -0,0 +1,48 @@ +module "storage" { + for_each = local.storage_accounts_map + + source = "../../../dtos-devops-templates/infrastructure/modules/storage" + + name = substr("${module.regions_config[each.value.region_key].names.storage-account}${lower(each.value.name_suffix)}", 0, 24) + resource_group_name = azurerm_resource_group.core[each.value.region_key].name + location = each.value.region_key + + containers = each.value.containers + + account_replication_type = each.value.replication_type + account_tier = each.value.account_tier + public_network_access_enabled = each.value.public_network_access_enabled + + rbac_roles = local.rbac_roles_storage + + # Private Endpoint Configuration if enabled + private_endpoint_properties = var.features.private_endpoints_enabled ? { + private_dns_zone_ids_blob = [data.terraform_remote_state.hub.outputs.private_dns_zone_storage_blob[each.value.region_key].private_dns_zone.id] + private_dns_zone_ids_queue = [data.terraform_remote_state.hub.outputs.private_dns_zone_storage_queue[each.value.region_key].private_dns_zone.id] + private_endpoint_enabled = var.features.private_endpoints_enabled + private_endpoint_subnet_id = module.subnets["${module.regions_config[each.value.region_key].names.subnet}-pep"].id + private_endpoint_resource_group_name = azurerm_resource_group.rg_private_endpoints[each.value.region_key].name + private_service_connection_is_manual = var.features.private_service_connection_is_manual + } : null + + tags = var.tags +} + +locals { + storage_accounts_flatlist = flatten([ + for region_key, region_val in var.regions : [ + for storage_key, storage_val in var.storage_accounts : { + name = "${storage_key}-${region_key}" + region_key = region_key + name_suffix = storage_val.name_suffix + replication_type = storage_val.replication_type + account_tier = storage_val.account_tier + public_network_access_enabled = storage_val.public_network_access_enabled + containers = storage_val.containers + } + ] + ]) + + # Project the above list into a map with unique keys for consumption in a for_each meta argument + storage_accounts_map = { for storage in local.storage_accounts_flatlist : storage.name => storage } +} diff --git a/infrastructure/tf-core/variables.tf b/infrastructure/tf-core/variables.tf new file mode 100644 index 00000000..ddb429cc --- /dev/null +++ b/infrastructure/tf-core/variables.tf @@ -0,0 +1,364 @@ +variable "AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_NAME" { + description = "The name of the Azure Storage Account for the audit backend" + type = string +} + +variable "AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME" { + description = "The name of the container in the Audit Azure Storage Account for the backend" + type = string +} + +variable "AUDIT_BACKEND_AZURE_RESOURCE_GROUP_NAME" { + description = "The name of the audit resource group for the Azure Storage Account" + type = string +} + +variable "AUDIT_BACKEND_AZURE_STORAGE_ACCOUNT_KEY" { + description = "The name of the audit resource group for the Azure Storage Account" + type = string +} + +variable "TARGET_SUBSCRIPTION_ID" { + description = "ID of a subscription to deploy infrastructure" + type = string +} + +variable "AUDIT_SUBSCRIPTION_ID" { + description = "ID of the Audit subscription to deploy infrastructure" + type = string +} + +variable "HUB_SUBSCRIPTION_ID" { + description = "ID of the subscription hosting the DevOps resources" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_NAME" { + description = "The name of the Azure Storage Account for the backend" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME" { + description = "The name of the container in the Azure Storage Account for the backend" + type = string +} + +variable "HUB_BACKEND_AZURE_STORAGE_ACCOUNT_KEY" { + description = "The name of the Statefile for the hub resources" + type = string +} + +variable "HUB_BACKEND_AZURE_RESOURCE_GROUP_NAME" { + description = "The name of the resource group for the Azure Storage Account" + type = string +} + +variable "application" { + description = "Project/Application code for deployment" + type = string + default = "DToS" +} + +variable "application_full_name" { + description = "Full name of the Project/Application code for deployment" + type = string + default = "DToS" +} + +variable "environment" { + description = "Environment code for deployments" + type = string + default = "DEV" +} + +variable "features" { + description = "Feature flags for the deployment" + type = map(bool) +} + +variable "regions" { + type = map(object({ + address_space = optional(string) + is_primary_region = bool + connect_peering = optional(bool, false) + subnets = optional(map(object({ + cidr_newbits = string + cidr_offset = string + create_nsg = optional(bool, true) # defaults to true + name = optional(string) # Optional name override + delegation_name = optional(string) + service_delegation_name = optional(string) + service_delegation_actions = optional(list(string)) + }))) + })) +} + +### Cohort Manager specific variables ### +variable "app_service_plan" { + description = "Configuration for the app service plan" + type = object({ + sku_name = optional(string, "P2v3") + os_type = optional(string, "Linux") + vnet_integration_enabled = optional(bool, false) + + autoscale = object({ + memory_percentage = object({ + metric = optional(string) + capacity_min = optional(string) + capacity_max = optional(string) + capacity_def = optional(string) + time_grain = optional(string) + statistic = optional(string) + time_window = optional(string) + time_aggregation = optional(string) + inc_operator = optional(string) + inc_threshold = optional(number) + inc_scale_direction = optional(string) + inc_scale_type = optional(string) + inc_scale_value = optional(number) + inc_scale_cooldown = optional(string) + dec_operator = optional(string) + dec_threshold = optional(number) + dec_scale_direction = optional(string) + dec_scale_type = optional(string) + dec_scale_value = optional(number) + dec_scale_cooldown = optional(string) + }) + }) + + instances = map(object({ + autoscale_override = optional(object({ + memory_percentage = object({ + metric = optional(string) + capacity_min = optional(string) + capacity_max = optional(string) + capacity_def = optional(string) + time_grain = optional(string) + statistic = optional(string) + time_window = optional(string) + time_aggregation = optional(string) + inc_operator = optional(string) + inc_threshold = optional(number) + inc_scale_direction = optional(string) + inc_scale_type = optional(string) + inc_scale_value = optional(number) + inc_scale_cooldown = optional(string) + dec_operator = optional(string) + dec_threshold = optional(number) + dec_scale_direction = optional(string) + dec_scale_type = optional(string) + dec_scale_value = optional(number) + dec_scale_cooldown = optional(string) + }) + })) + })) + }) +} + +variable "function_apps" { + description = "Configuration for function apps" + type = object({ + acr_mi_name = string + acr_name = string + acr_rg_name = string + always_on = bool + app_insights_name = string + app_insights_rg_name = string + cont_registry_use_mi = bool + docker_CI_enable = string + docker_env_tag = string + docker_img_prefix = string + enable_appsrv_storage = bool + ftps_state = string + https_only = bool + remote_debugging_enabled = bool + storage_uses_managed_identity = bool + worker_32bit = bool + slots = optional(map(object({ + name = string + slot_enabled = optional(bool, false) + }))) + fa_config = map(object({ + name_suffix = string + function_endpoint_name = string + app_service_plan_key = string + storage_account_env_var_name = optional(string, "") + storage_containers = optional(list(object + ({ + env_var_name = string + container_name = string + })), []) + db_connection_string = optional(string, "") + key_vault_url = optional(string, "") + app_urls = optional(list(object({ + env_var_name = string + function_app_key = string + })), []) + })) + }) +} + +variable "key_vault" { + description = "Configuration for the key vault" + type = object({ + disk_encryption = optional(bool, true) + soft_del_ret_days = optional(number, 7) + purge_prot = optional(bool, false) + sku_name = optional(string, "standard") + }) +} + +variable "network_security_group_rules" { + description = "The network security group rules." + default = {} + type = map(list(object({ + name = string + priority = number + direction = string + access = string + protocol = string + source_port_range = string + destination_port_range = string + source_address_prefix = string + destination_address_prefix = string + }))) +} + +/* + application_rule_collection = [ + { + name = "example-application-rule-collection-1" + priority = 600 + action = "Allow" + rule_name = "example-rule-1" + protocols = [ + { + type = "Http" + port = 80 + }, + { + type = "Https" + port = 443 + } + ] + source_addresses = ["0.0.0.0/0"] + destination_fqdns = ["example.com"] + }, +*/ + + +variable "routes" { + description = "Routes configuration for different regions" + type = map(object({ + bgp_route_propagation_enabled = optional(bool, false) + firewall_policy_priority = number + application_rules = list(object({ + name = optional(string) + priority = optional(number) + action = optional(string) + rule_name = optional(string) + protocols = list(object({ + type = optional(string) + port = optional(number) + })) + source_addresses = optional(list(string)) + destination_fqdns = list(string) + })) + nat_rules = list(object({ + name = optional(string) + priority = optional(number) + action = optional(string) + rule_name = optional(string) + protocols = list(string) + source_addresses = list(string) + destination_address = optional(string) + destination_ports = list(string) + translated_address = optional(string) + translated_port = optional(string) + })) + network_rules = list(object({ + name = optional(string) + priority = optional(number) + action = optional(string) + rule_name = optional(string) + source_addresses = optional(list(string)) + destination_addresses = optional(list(string)) + protocols = optional(list(string)) + destination_ports = optional(list(string)) + })) + route_table_routes_to_audit = list(object({ + name = optional(string) + address_prefix = optional(string) + next_hop_type = optional(string) + next_hop_in_ip_address = optional(string) + })) + route_table_routes_from_audit = list(object({ + name = optional(string) + address_prefix = optional(string) + next_hop_type = optional(string) + next_hop_in_ip_address = optional(string) + })) + })) + default = {} +} + +variable "sqlserver" { + description = "Configuration for the Azure MSSQL server instance and a default database " + type = object({ + + sql_uai_name = optional(string) + sql_admin_group_name = optional(string) + ad_auth_only = optional(bool) + + # Server Instance + server = optional(object({ + sqlversion = optional(string, "12.0") + tlsversion = optional(number, 1.2) + azure_services_access_enabled = optional(bool, true) + }), {}) + + # Database + dbs = optional(map(object({ + db_name_suffix = optional(string, "serins") + collation = optional(string, "SQL_Latin1_General_CP1_CI_AS") + licence_type = optional(string, "LicenseIncluded") + max_gb = optional(number, 5) + read_scale = optional(bool, false) + sku = optional(string, "S0") + })), {}) + + # FW Rules + fw_rules = optional(map(object({ + fw_rule_name = string + start_ip = string + end_ip = string + })), {}) + }) +} + +variable "storage_accounts" { + description = "Configuration for the Storage Account, currently used for Function Apps" + type = map(object({ + name_suffix = string + account_tier = optional(string, "Standard") + replication_type = optional(string, "LRS") + public_network_access_enabled = optional(bool, false) + containers = optional(map(object({ + container_name = string + container_access_type = optional(string, "private") + })), {}) + })) +} + +variable "tags" { + description = "Default tags to be applied to resources" + type = map(string) +} + +variable "function_app_slots" { + description = "function app slots" + type = list(object({ + function_app_slots_name = optional(string, "staging") + function_app_slot_enabled = optional(bool, false) + })) +} diff --git a/scripts/database/drop_tables.sql b/scripts/database/drop_tables.sql index 062569e7..78eb2254 100644 --- a/scripts/database/drop_tables.sql +++ b/scripts/database/drop_tables.sql @@ -42,3 +42,7 @@ GO IF OBJECT_ID('dbo.EPISODE', 'U') IS NOT NULL DROP TABLE dbo.EPISODE; GO + +IF OBJECT_ID('dbo.ORGANISATION_LKP', 'U') IS NOT NULL + DROP TABLE dbo.ORGANISATION_LKP; +GO diff --git a/scripts/database/permissions.sql b/scripts/database/permissions.sql new file mode 100644 index 00000000..bbf2d9b1 --- /dev/null +++ b/scripts/database/permissions.sql @@ -0,0 +1,59 @@ +CREATE USER [dev-uks-si-create-participant-screening-episode-data] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-participant-screening-episode-data]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-participant-screening-episode-data]; + +CREATE USER [dev-uks-si-create-participant-screening-profile-data] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-participant-screening-profile-data]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-participant-screening-profile-data]; + +CREATE USER [dev-uks-si-create-participant-screening-episode] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-participant-screening-episode]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-participant-screening-episode]; + +CREATE USER [dev-uks-si-create-participant-screening-profile] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-participant-screening-profile]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-participant-screening-profile]; + +CREATE USER [dev-uks-si-get-demographics-data] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-get-demographics-data]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-get-demographics-data]; + +CREATE USER [dev-uks-si-create-episode] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-episode]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-episode]; + +CREATE USER [dev-uks-si-get-episode] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-get-episode]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-get-episode]; + +CREATE USER [dev-uks-si-update-episode] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-update-episode]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-update-episode]; + +CREATE USER [dev-uks-si-receive-data] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-receive-data]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-receive-data]; + +CREATE USER [dev-uks-si-create-update-episode] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-create-update-episode]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-create-update-episode]; + +CREATE USER [dev-uks-si-get-episode-mgmt] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-get-episode-mgmt]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-get-episode-mgmt]; + +CREATE USER [dev-uks-si-retrieve-mesh-file-from-cm] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-retrieve-mesh-file-from-cm]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-retrieve-mesh-file-from-cm]; + +CREATE USER [dev-uks-si-get-participant] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-get-participant]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-get-participant]; + +CREATE USER [dev-uks-si-update-participant] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-update-participant]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-update-participant]; + +CREATE USER [dev-uks-si-get-organisation-data] FROM EXTERNAL PROVIDER; +ALTER ROLE [db_datareader] ADD MEMBER [dev-uks-si-get-organisation-data]; +ALTER ROLE [db_datawriter] ADD MEMBER [dev-uks-si-get-organisation-data]; diff --git a/scripts/deployment/get-docker-names.sh b/scripts/deployment/get-docker-names.sh index 502679ac..5f2fa2a8 100644 --- a/scripts/deployment/get-docker-names.sh +++ b/scripts/deployment/get-docker-names.sh @@ -1,13 +1,14 @@ #!/bin/bash declare -A docker_functions_map=( - ["BIAnalyticsDataService/CreateParticipantScreeningEpisode"]="create-participant-screening-episode" - ["BIAnalyticsDataService/CreateParticipantScreeningProfile"]="create-participant-screening-profile" - # ["BIAnalyticsService/CreateParticipantScreeningProfile"]="create-participant-screening-profile" # does not exist in the compose.yaml file + ["BIAnalyticsDataService/CreateParticipantScreeningEpisode"]="create-participant-screening-episode-data" + ["BIAnalyticsDataService/CreateParticipantScreeningProfile"]="create-participant-screening-profile-data" + ["BIAnalyticsService/CreateParticipantScreeningEpisode"]="create-participant-screening-episode" # does not exist in the compose.yaml file + ["BIAnalyticsService/CreateParticipantScreeningProfile"]="create-participant-screening-profile" # does not exist in the compose.yaml file # ["BIAnalyticsService/CreateDataAssets"]="create-data-assets" ["DemographicsService/GetDemographicsData"]="get-demographics-data" # does not exist in the compose.yaml file - ["EpisodeDataService/CreateEpisode"]="create-episode" ["EpisodeDataService/GetEpisode"]="get-episode" + ["EpisodeDataService/CreateEpisode"]="create-episode" ["EpisodeDataService/UpdateEpisode"]="update-episode" ["EpisodeIntegrationService/ReceiveData"]="receive-data" ["EpisodeManagementService/CreateUpdateEpisode"]="create-update-episode" diff --git a/infrastructure/environments/.gitkeep b/src/BIAnalyticsDataService/CreateParticipantScreeningProfile/delme similarity index 100% rename from infrastructure/environments/.gitkeep rename to src/BIAnalyticsDataService/CreateParticipantScreeningProfile/delme diff --git a/infrastructure/images/.gitkeep b/src/EpisodeDataService/GetEpisode/delme similarity index 100% rename from infrastructure/images/.gitkeep rename to src/EpisodeDataService/GetEpisode/delme diff --git a/infrastructure/modules/.gitkeep b/src/EpisodeManagementService/GetEpisode/delme similarity index 100% rename from infrastructure/modules/.gitkeep rename to src/EpisodeManagementService/GetEpisode/delme diff --git a/src/Shared/Common/delme b/src/Shared/Common/delme new file mode 100644 index 00000000..e69de29b diff --git a/src/Shared/Model/delme b/src/Shared/Model/delme new file mode 100644 index 00000000..e69de29b