diff --git a/.github/workflows/deploy-backoffice-app.yaml b/.github/workflows/deploy-backoffice-app.yaml index 16520deb..1b098cf4 100644 --- a/.github/workflows/deploy-backoffice-app.yaml +++ b/.github/workflows/deploy-backoffice-app.yaml @@ -4,10 +4,13 @@ on: workflow_dispatch: {} jobs: - deploy_workspace_to_azure: + deploy: name: Deploy - uses: pagopa/io-std/.github/workflows/deploy-workspace.yaml@main - with: - environment: io-p-sign-backoffice-app - workspace-name: io-sign-backoffice-app + uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main secrets: inherit + with: + workspace_name: io-sign-backoffice-app + environment: app-prod + resource_group_name: io-p-sign-backend-rg + web_app_name: io-p-sign-backoffice-app + use_staging_slot: true \ No newline at end of file diff --git a/.github/workflows/deploy-backoffice-func.yaml b/.github/workflows/deploy-backoffice-func.yaml index dc0e8e48..ea0b30a0 100644 --- a/.github/workflows/deploy-backoffice-func.yaml +++ b/.github/workflows/deploy-backoffice-func.yaml @@ -4,10 +4,13 @@ on: workflow_dispatch: {} jobs: - deploy_workspace_to_azure: + deploy: name: Deploy - uses: pagopa/io-std/.github/workflows/deploy-workspace.yaml@main - with: - environment: io-p-sign-backoffice-func - workspace-name: io-sign-backoffice-func + uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main secrets: inherit + with: + workspace_name: io-sign-backoffice-func + environment: app-prod + resource_group_name: io-p-sign-backend-rg + web_app_name: io-p-sign-backoffice-func + use_staging_slot: true \ No newline at end of file diff --git a/.github/workflows/deploy-issuer-func.yaml b/.github/workflows/deploy-issuer-func.yaml index b1cdb75c..2062dbee 100644 --- a/.github/workflows/deploy-issuer-func.yaml +++ b/.github/workflows/deploy-issuer-func.yaml @@ -6,11 +6,14 @@ on: jobs: deploy_workspace_to_azure: name: Deploy - uses: pagopa/io-std/.github/workflows/deploy-workspace.yaml@main - with: - environment: io-p-sign-issuer-func - workspace-name: io-func-sign-issuer + uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main secrets: inherit + with: + workspace_name: io-func-sign-issuer + environment: app-prod + resource_group_name: io-p-sign-backend-rg + web_app_name: io-p-sign-issuer-func + use_staging_slot: true # tag the current commit as "latest" in order to make the latest # released version easily referenceable in the documentation diff --git a/.github/workflows/deploy-support-func.yaml b/.github/workflows/deploy-support-func.yaml index e41ee8dc..c4722b0f 100644 --- a/.github/workflows/deploy-support-func.yaml +++ b/.github/workflows/deploy-support-func.yaml @@ -4,10 +4,13 @@ on: workflow_dispatch: {} jobs: - deploy_workspace_to_azure: + deploy: name: Deploy - uses: pagopa/io-std/.github/workflows/deploy-workspace.yaml@main - with: - environment: io-p-sign-support-func - workspace-name: io-func-sign-support + uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main secrets: inherit + with: + workspace_name: io-func-sign-support + environment: app-prod + resource_group_name: io-p-sign-backend-rg + web_app_name: io-p-sign-support-func + use_staging_slot: true \ No newline at end of file diff --git a/.github/workflows/deploy-user-func.yaml b/.github/workflows/deploy-user-func.yaml index 8e21ae66..6edcf425 100644 --- a/.github/workflows/deploy-user-func.yaml +++ b/.github/workflows/deploy-user-func.yaml @@ -4,10 +4,13 @@ on: workflow_dispatch: {} jobs: - deploy_workspace_to_azure: + deploy: name: Deploy - uses: pagopa/io-std/.github/workflows/deploy-workspace.yaml@main - with: - environment: io-p-sign-user-func - workspace-name: io-func-sign-user + uses: pagopa/dx/.github/workflows/web_app_deploy.yaml@main secrets: inherit + with: + workspace_name: io-func-sign-user + environment: app-prod + resource_group_name: io-p-sign-backend-rg + web_app_name: io-p-sign-user-func + use_staging_slot: true \ No newline at end of file diff --git a/.github/workflows/opex_api_issuer.yaml b/.github/workflows/opex_api_issuer.yaml index 2a7d5fba..5d90756d 100644 --- a/.github/workflows/opex_api_issuer.yaml +++ b/.github/workflows/opex_api_issuer.yaml @@ -40,8 +40,12 @@ jobs: environment: prod api-name: ${{ env.API_NAME }} config: .opex/${{ env.API_NAME }}/env/prod/config.yaml - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - client-id: ${{ secrets.AZURE_CLIENT_ID_CD }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant-id: ${{ env.ARM_TENANT_ID }} + client-id: ${{ env.ARM_CLIENT_ID }} + subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }} # from https://github.com/pagopa/opex-dashboard-azure-action/pkgs/container/opex-dashboard-azure-action docker-version: sha256:7e454c1892b2aaf3717782c07a2fa01504db5cc7d07979fae863dfc00a9f0173 + env: + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} diff --git a/.github/workflows/opex_api_support.yaml b/.github/workflows/opex_api_support.yaml index b7b6d483..49fe1200 100644 --- a/.github/workflows/opex_api_support.yaml +++ b/.github/workflows/opex_api_support.yaml @@ -38,8 +38,13 @@ jobs: environment: prod api-name: ${{ env.API_NAME }} config: .opex/${{ env.API_NAME }}/env/prod/config.yaml - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - client-id: ${{ secrets.AZURE_CLIENT_ID_CD }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant-id: ${{ env.ARM_TENANT_ID }} + client-id: ${{ env.ARM_CLIENT_ID }} + subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }} # from https://github.com/pagopa/opex-dashboard-azure-action/pkgs/container/opex-dashboard-azure-action docker-version: sha256:7e454c1892b2aaf3717782c07a2fa01504db5cc7d07979fae863dfc00a9f0173 + env: + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} + diff --git a/.github/workflows/opex_api_user.yaml b/.github/workflows/opex_api_user.yaml index 24c61b5e..8c190f40 100644 --- a/.github/workflows/opex_api_user.yaml +++ b/.github/workflows/opex_api_user.yaml @@ -38,8 +38,12 @@ jobs: environment: prod api-name: ${{ env.API_NAME }} config: .opex/${{ env.API_NAME }}/env/prod/config.yaml - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - client-id: ${{ secrets.AZURE_CLIENT_ID_CD }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant-id: ${{ env.ARM_TENANT_ID }} + client-id: ${{ env.ARM_CLIENT_ID }} + subscription-id: ${{ env.ARM_SUBSCRIPTION_ID }} # from https://github.com/pagopa/opex-dashboard-azure-action/pkgs/container/opex-dashboard-azure-action docker-version: sha256:04d8ead53c772d23b094c2a395292dc159e6f2905e1b13b5f828f31eac6eb27f + env: + ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }} + ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }} + ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }} diff --git a/.github/workflows/tf-code-review.yaml b/.github/workflows/tf-code-review.yaml new file mode 100644 index 00000000..8cebf00a --- /dev/null +++ b/.github/workflows/tf-code-review.yaml @@ -0,0 +1,23 @@ +name: PR - IO Sign TF Validation + +on: + workflow_dispatch: + pull_request: + types: + - opened + - edited + - synchronize + - reopened + - ready_for_review + paths: + - "infra/resources/prod/**" + - ".github/workflows/tf-code-review.yaml" + +jobs: + prod_itn_core_code_review: + uses: pagopa/dx/.github/workflows/infra_plan.yaml@main + name: Prod - Code Review + secrets: inherit + with: + environment: prod + base_path: infra/resources diff --git a/.github/workflows/tf-deploy.yaml b/.github/workflows/tf-deploy.yaml new file mode 100644 index 00000000..fa9d8254 --- /dev/null +++ b/.github/workflows/tf-deploy.yaml @@ -0,0 +1,19 @@ +name: PR - IO Sign TF Apply + +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "infra/resources/prod/**" + - ".github/workflows/tf-deploy.yaml" + +jobs: + prod_itn_core_deploy: + uses: pagopa/dx/.github/workflows/infra_apply.yaml@main + name: Prod - Code Deploy + secrets: inherit + with: + environment: prod + base_path: infra/resources diff --git a/.identity/.gitignore b/.identity/.gitignore deleted file mode 100644 index 45544ad9..00000000 --- a/.identity/.gitignore +++ /dev/null @@ -1,48 +0,0 @@ -# Local .terraform directories -**/.terraform/* - -# .tfstate files -*.tfstate -*.tfstate.* - -# Crash log files -crash.log - -# Ignore any .tfvars files that are generated automatically for each Terraform run. Most -# .tfvars files are managed as part of configuration and so should be included in -# version control. -# -# example.tfvars - -# Ignore override files as they are usually used to override resources locally and so -# are not checked in -override.tf -override.tf.json -*_override.tf -*_override.tf.json - -# Include override files you do wish to add to version control using negated pattern -# -# !example_override.tf - -# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan -# example: *tfplan* - -**/.tfsec/* -**/.ignore/* - -*.DS_Store -*.log -*.h2.db -settings.json -__TMP -.metals/ -*.log -*.h2.db -settings.json -__TMP -.metals/ -__azurite_* -/.idea - -**/modules/**/.terraform.lock.hcl diff --git a/.identity/.terraform.lock.hcl b/.identity/.terraform.lock.hcl deleted file mode 100644 index 854d0c44..00000000 --- a/.identity/.terraform.lock.hcl +++ /dev/null @@ -1,73 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/azuread" { - version = "2.33.0" - constraints = "2.33.0" - hashes = [ - "h1:PDiZA9QpXCkaSuWu6jiCRcjVtKJETqjcOZq4I434zfE=", - "h1:QAQe2+WSqGnHYAVoA+NN4Oeuoqg5sXq3U9Qmj6S1P5M=", - "h1:XIvCW3Nl4bW1bc9f8jyGhft+fQjaed4yy/LFzDAeVJ8=", - "h1:Z28tjly5UfKOE+HL/oALxCPhmCuBwUgZ4uaYt68VR3M=", - "zh:0602d03d7d7e38819f78dc377e64f365427496edf1065bfbb113e3921ab1c34e", - "zh:08843838f4fe146084592472648d4ea7191931eabe042a96c3b3c6eaf8ddfb43", - "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", - "zh:26a0d8a186e3b47ea0b7217a8e420b03fda59b7a680bb3ea52cf7d3e6d965ef3", - "zh:352a1cacaacd39e796de15a52d192ab0e6eb98dd36b5fbf8ebddd37e6dafa4ac", - "zh:3702ad4c534e67e2e07b060bfe5e6edc244c59c911906c8b15b96e7fecb0ff2c", - "zh:93b5248d26bdd44845b2ab051a2168c7edad788ae9836f62ea5fb632fd59d7ea", - "zh:a7b880155f4a67b52a5bfe78de33dc55254ef80006234f00e36aaf6533b1de4a", - "zh:a7cf0829364127c9bca26ec01ea3d66988b43987b2d26a3290487d1fc0da50eb", - "zh:b1f82b0d30af733b36a2f849799e0b1ed6a72888fa32a438c829c4e5cff88e20", - "zh:b6c2b23770852de8f56b549579c2f5a82afd84a9ca0616d53a25d48488f7aaf0", - "zh:d87dfbdfe8ab9d3a2e33f210333d40f211ea7d33bfa671063e6807c6ddd85a52", - ] -} - -provider "registry.terraform.io/hashicorp/azurerm" { - version = "3.84.0" - constraints = "3.84.0" - hashes = [ - "h1:1Ucponuagrx5kNeIlcZwG2urqoRXBCTddDKqL265+xM=", - "h1:3KYwbI62e6u2f7ob9Ps8yahnIaNHkE56UsF0130zRzE=", - "h1:aoqNC2sfLKyblgQh0SfQW0BHl3UP1mMAUJLYLGG3PxE=", - "h1:y/NWRLvnJmyJ5lf/AnLFy25jfyJqp6xwwxLxZnvovAs=", - "zh:14a96daf672541dbc27137d9cc0a96a737710597262ecaaa64a328eb1174e5df", - "zh:16d8e794fdd87ed8e64291fe8a617f420d8263f21672033333a020d06f4c9618", - "zh:64e5cd1bb6a81bccffff0d1f77790286ab46179cf12442134c3f3bca722afc1b", - "zh:7010ada67fbae971ac8b7204a30b1317aee7ccac7227afc6ac27277c642996a1", - "zh:77c2616ecd29685d2a4dc3ec3e9771e5ecf652e127946767d9b7ef19bbf58a21", - "zh:861922cfae724eacf1bd915efd5dbf6c23e4e762a2bbe60993099648e64aedb5", - "zh:8fb797c98bb08e7342995317810d28c41bb519fbc128adaa170896356b9eaebd", - "zh:982e85a4a9d282e3c8f7d7836037ccc98ff3ef50af246fad2e04684a81d16201", - "zh:a2ef29ff907cf6622e58afa0a27e23a3160ba3d70d531795b4d9a6c42c354630", - "zh:c46ccc4eecb79d096bcb652af0cffe300ec480d80a13a5b302c71b1aac9f9f1c", - "zh:cc6a06bf6d5e811fe8c0d9ad652e143b4e94bd16a03fb8a86f5086f0ae5abfa9", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - ] -} - -provider "registry.terraform.io/integrations/github" { - version = "5.39.0" - constraints = "5.39.0" - hashes = [ - "h1:AMn+8x91H4L0bNXgfGA4mEmjumfd7lslBRNsf+Z3J9Q=", - "h1:B2pPhXlLR5IHfoO6MQ70EgfavVLUlFd8FQw2zxy04RM=", - "h1:EsDsxcxdtvQ6D64PNbkGG6PbAC2l2ECeCryGPw6uKIw=", - "h1:mpO68ifHJruMIuSvOuCa4PQt841arZr50O+uCxjhVak=", - "zh:1bd8eae3b590ca0d765af0d316fe5500b6a55cc75b84997aed38549fef3f3e3e", - "zh:3fb718473bcf42ce48f71ad79f527988b5ad8bc744cbcf2e4ac84f389c17b32a", - "zh:43b6cdd6471478baddf4326f23a68ec5bce5a64aa4792cafa0d87708429e18de", - "zh:54d577bebc29cfb9a8f29c8335fe533b55c1e7bf79d7a7acb1d8dfcc305f2824", - "zh:6aa1b7cb45e7d9e116b3e6c234c92b9cc6598819d0d6b0cb330a707f8e6f007b", - "zh:8824653f311a4993b5ce355bbe5f74d8ed1eed2ed2fd25d6e902831263566b40", - "zh:8b9a256730cf48d3b8a91d86393a4787d119fc9990ab864c6069ec83bbaf0a17", - "zh:9e87d3cc56dfba33c9a8f41b1f39b8624d680c8e5498bbdccccca251825c9529", - "zh:9fa3ac69297416c619e812e79bf634064cc09fdea242088bbf65fb34ffd6db9f", - "zh:c0aa2b2e488eefd7a4739d9b6893b977b5249d7f2ccfa55b64ce529a50b64fd7", - "zh:cbe26313f9d2dcfcbaa91a0c4f064ed6e34c2e2eff05dfe092332f50530fc61b", - "zh:dc2ca63d044304352ac66d969c5acb0dc689a48d4d97ee5d0a28414bc5bc95ee", - "zh:f6351cb0ff7ff17771c415349f0b9828ec54f19f40c6c3a53a8092f2c78c6f65", - "zh:f8c06856928f55475e9ae63958f5f38a45ceee728f9ec0114ffebd5b6b56f44e", - ] -} diff --git a/.identity/01_data.tf b/.identity/01_data.tf deleted file mode 100644 index e47c59d7..00000000 --- a/.identity/01_data.tf +++ /dev/null @@ -1,18 +0,0 @@ -data "azurerm_resource_group" "rg_identity" { - name = "${local.prefix}-identity-rg" -} - -data "azurerm_resources" "web_apps" { - resource_group_name = local.resource_group_name - type = "Microsoft.Web/sites" -} - -data "azurerm_linux_web_app" "web_apps" { - for_each = local.web_apps_map - resource_group_name = local.resource_group_name - name = each.value.name -} - -data "github_team" "maintainers" { - slug = "io-sign-maintainers" -} diff --git a/.identity/99_locals.tf b/.identity/99_locals.tf deleted file mode 100644 index 395819da..00000000 --- a/.identity/99_locals.tf +++ /dev/null @@ -1,28 +0,0 @@ -locals { - prefix = "${var.prefix}-${var.env_short}" - project = "${var.prefix}-${var.env_short}-${var.domain}" - - resource_group_name = "${local.project}-backend-rg" - - web_apps_map = { for w in data.azurerm_resources.web_apps.resources : w.name => w } - - github_federations = tolist([ - for w in local.web_apps_map : { - repository = "io-sign" - subject = github_repository_environment.web_apps[w.name].environment - } - ]) - - repo_secrets = { - "AZURE_SUBSCRIPTION_ID" = data.azurerm_client_config.current.subscription_id - "AZURE_TENANT_ID" = data.azurerm_client_config.current.tenant_id - } - - opex_env_ci_secrets = { - "AZURE_CLIENT_ID_CI" = module.opex_identity_ci.identity_client_id - } - - opex_env_cd_secrets = { - "AZURE_CLIENT_ID_CD" = module.opex_identity_cd.identity_client_id - } -} diff --git a/.identity/99_main.tf b/.identity/99_main.tf deleted file mode 100644 index 4f778003..00000000 --- a/.identity/99_main.tf +++ /dev/null @@ -1,34 +0,0 @@ -terraform { - required_version = ">=1.6.0" - - required_providers { - azuread = { - source = "hashicorp/azuread" - version = "2.33.0" - } - azurerm = { - source = "hashicorp/azurerm" - version = "3.84.0" - } - github = { - source = "integrations/github" - version = "5.39.0" - } - } - - backend "azurerm" {} -} - -provider "azurerm" { - features {} -} - -provider "github" { - owner = var.github.org - write_delay_ms = "200" - read_delay_ms = "200" -} - -data "azurerm_subscription" "current" {} - -data "azurerm_client_config" "current" {} diff --git a/.identity/99_outputs.tf b/.identity/99_outputs.tf deleted file mode 100644 index ad02b586..00000000 --- a/.identity/99_outputs.tf +++ /dev/null @@ -1,28 +0,0 @@ -output "tenant_id" { - value = data.azurerm_client_config.current.tenant_id -} - -output "subscription_id" { - value = data.azurerm_subscription.current.subscription_id -} - -output "web_apps_managed_identities" { - value = { - app_name = module.web_apps_identity_cd.identity_app_name - client_id = module.web_apps_identity_cd.identity_client_id - } -} - -output "opex_ci_managed_identities" { - value = { - app_name = module.opex_identity_ci.identity_app_name - client_id = module.opex_identity_ci.identity_client_id - } -} - -output "opex_cd_managed_identities" { - value = { - app_name = module.opex_identity_cd.identity_app_name - client_id = module.opex_identity_cd.identity_client_id - } -} diff --git a/.identity/99_variables.tf b/.identity/99_variables.tf deleted file mode 100644 index a3e9e983..00000000 --- a/.identity/99_variables.tf +++ /dev/null @@ -1,80 +0,0 @@ -variable "tags" { - type = map(any) -} - -variable "prefix" { - type = string - validation { - condition = ( - length(var.prefix) <= 6 - ) - error_message = "Max length is 6 chars." - } -} - -variable "env" { - type = string - description = "Environment" -} - -variable "env_short" { - type = string - validation { - condition = ( - length(var.env_short) <= 1 - ) - error_message = "Max length is 1 chars." - } -} - -variable "domain" { - type = string - description = "The applicative domain" - validation { - condition = ( - length(var.domain) < 6 - ) - error_message = "Max length is 6 chars." - } -} - -variable "github" { - type = object({ - org = string - repository = string - }) - description = "GitHub Organization and repository name" - default = { - org = "pagopa" - repository = "io-sign" - } -} - -variable "opex_environment_ci_roles" { - type = object({ - subscription = list(string) - resource_groups = map(list(string)) - }) - description = "Continous Integration roles for opex managed identiy" -} - -variable "opex_environment_cd_roles" { - type = object({ - subscription = list(string) - resource_groups = map(list(string)) - }) - description = "Continous Delivery roles for opex managed identiy" -} - -variable "web_apps_environment_cd_roles" { - type = object({ - subscription = list(string) - resource_groups = map(list(string)) - }) - description = "GitHub Continous Delivery roles for web apps managed identity" -} - -variable "location" { - type = string - default = "westeurope" -} diff --git a/.identity/env/prod/backend.ini b/.identity/env/prod/backend.ini deleted file mode 100644 index cf83055f..00000000 --- a/.identity/env/prod/backend.ini +++ /dev/null @@ -1 +0,0 @@ -subscription=PROD-IO diff --git a/.identity/env/prod/backend.tfvars b/.identity/env/prod/backend.tfvars deleted file mode 100644 index 5941c328..00000000 --- a/.identity/env/prod/backend.tfvars +++ /dev/null @@ -1,4 +0,0 @@ -resource_group_name = "io-p-sign-infra-rg" -storage_account_name = "iopsigninfrast" -container_name = "azurermstate" -key = "identity.tfstate" diff --git a/.identity/env/prod/terraform.tfvars b/.identity/env/prod/terraform.tfvars deleted file mode 100644 index a3e51875..00000000 --- a/.identity/env/prod/terraform.tfvars +++ /dev/null @@ -1,49 +0,0 @@ -domain = "sign" -env = "prod" -env_short = "p" -prefix = "io" - -tags = { - CreatedBy = "Terraform" - Environment = "Prod" - Owner = "io" - Source = "https://github.com/pagopa/io-sign" - CostCenter = "TS310 - PAGAMENTI & SERVIZI" -} - -opex_environment_ci_roles = { - subscription = ["Reader"] - resource_groups = { - "dashboards" = [ - "Reader" - ], - "terraform-state-rg" = [ - "Storage Blob Data Reader" - ] - } -} - -opex_environment_cd_roles = { - subscription = ["Reader"] - resource_groups = { - "dashboards" = [ - "Contributor" - ], - "terraform-state-rg" = [ - "Storage Blob Data Contributor", - "Reader and Data Access" - ] - } -} - -web_apps_environment_cd_roles = { - subscription = [] - resource_groups = { - "io-p-github-runner-rg" = [ - "Contributor", - ], - "io-p-sign-backend-rg" = [ - "Contributor", - ] - } -} diff --git a/.identity/github_environment_opex.tf b/.identity/github_environment_opex.tf deleted file mode 100644 index cf8212b9..00000000 --- a/.identity/github_environment_opex.tf +++ /dev/null @@ -1,33 +0,0 @@ -resource "github_repository_environment" "github_repository_environment_opex_ci" { - environment = "opex-${var.env}-ci" - repository = var.github.repository - deployment_branch_policy { - protected_branches = false - custom_branch_policies = true - } -} - -resource "github_repository_environment" "github_repository_environment_opex_cd" { - environment = "opex-${var.env}-cd" - repository = var.github.repository - deployment_branch_policy { - protected_branches = false - custom_branch_policies = true - } -} - -resource "github_actions_environment_secret" "opex_env_ci_secrets" { - for_each = local.opex_env_ci_secrets - repository = var.github.repository - environment = github_repository_environment.github_repository_environment_opex_ci.environment - secret_name = each.key - plaintext_value = each.value -} - -resource "github_actions_environment_secret" "opex_env_cd_secrets" { - for_each = local.opex_env_cd_secrets - repository = var.github.repository - environment = github_repository_environment.github_repository_environment_opex_cd.environment - secret_name = each.key - plaintext_value = each.value -} diff --git a/.identity/github_environment_web_apps.tf b/.identity/github_environment_web_apps.tf deleted file mode 100644 index c34b75fe..00000000 --- a/.identity/github_environment_web_apps.tf +++ /dev/null @@ -1,40 +0,0 @@ -resource "github_repository_environment" "web_apps" { - for_each = local.web_apps_map - environment = each.value.name - repository = var.github.repository - reviewers { - teams = [data.github_team.maintainers.id] - } -} - -resource "github_actions_environment_secret" "web_app_client_id" { - for_each = local.web_apps_map - repository = var.github.repository - environment = github_repository_environment.web_apps[each.key].environment - secret_name = "AZURE_CLIENT_ID" - plaintext_value = module.web_apps_identity_cd.identity_client_id -} - -resource "github_actions_environment_variable" "web_app_resouce_group" { - for_each = local.web_apps_map - repository = var.github.repository - environment = github_repository_environment.web_apps[each.key].environment - variable_name = "AZURE_WEB_APP_RESOURCE_GROUP" - value = local.resource_group_name -} - -resource "github_actions_environment_variable" "web_app_names" { - for_each = local.web_apps_map - repository = var.github.repository - environment = github_repository_environment.web_apps[each.key].environment - variable_name = "AZURE_WEB_APP_NAME" - value = each.value.name -} - -resource "github_actions_environment_variable" "health_check_path" { - for_each = local.web_apps_map - repository = var.github.repository - environment = github_repository_environment.web_apps[each.key].environment - variable_name = "HEALTH_CHECK_PATH" - value = coalesce(data.azurerm_linux_web_app.web_apps[each.key].site_config[0].health_check_path, "/") -} diff --git a/.identity/identity_opex.tf b/.identity/identity_opex.tf deleted file mode 100644 index 6cc5808d..00000000 --- a/.identity/identity_opex.tf +++ /dev/null @@ -1,49 +0,0 @@ -module "opex_identity_ci" { - source = "github.com/pagopa/terraform-azurerm-v3//github_federated_identity?ref=v7.34.2" - - prefix = var.prefix - env_short = var.env_short - domain = var.domain - app_name = "opex" - - identity_role = "ci" - - github_federations = [ - { - repository = "io-sign" - subject = github_repository_environment.github_repository_environment_opex_ci.environment - } - ] - - ci_rbac_roles = { - subscription_roles = var.opex_environment_ci_roles.subscription - resource_groups = var.opex_environment_ci_roles.resource_groups - } - - tags = var.tags -} - -module "opex_identity_cd" { - source = "github.com/pagopa/terraform-azurerm-v3//github_federated_identity?ref=v7.34.2" - - prefix = var.prefix - env_short = var.env_short - domain = var.domain - app_name = "opex" - - identity_role = "cd" - - github_federations = [ - { - repository = "io-sign" - subject = github_repository_environment.github_repository_environment_opex_cd.environment - } - ] - - cd_rbac_roles = { - subscription_roles = var.opex_environment_cd_roles.subscription - resource_groups = var.opex_environment_cd_roles.resource_groups - } - - tags = var.tags -} diff --git a/.identity/identity_web_apps.tf b/.identity/identity_web_apps.tf deleted file mode 100644 index 671835ef..00000000 --- a/.identity/identity_web_apps.tf +++ /dev/null @@ -1,18 +0,0 @@ -module "web_apps_identity_cd" { - source = "github.com/pagopa/terraform-azurerm-v3//github_federated_identity?ref=v7.34.2" - - prefix = var.prefix - env_short = var.env_short - domain = var.domain - - identity_role = "cd" - - github_federations = local.github_federations - - cd_rbac_roles = { - subscription_roles = var.web_apps_environment_cd_roles.subscription - resource_groups = var.web_apps_environment_cd_roles.resource_groups - } - - tags = var.tags -} diff --git a/.identity/terraform.sh b/.identity/terraform.sh deleted file mode 100755 index 982f6a00..00000000 --- a/.identity/terraform.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e - -action=$1 -env=$2 -IFS=" " read -r -a other <<< "${@:3}" - -if [ -z "$action" ]; then - echo "Missed action: init, apply, plan" - exit 0 -fi - -if [ -z "$env" ]; then - echo "env should be: dev, uat or prod." - exit 0 -fi - -# shellcheck source=./env/prod/backend.ini -source "./env/$env/backend.ini" -az account set -s "${subscription}" - -export TF_VAR_github_token="${GITHUB_TOKEN}" - -if echo "init plan apply refresh import output state taint destroy" | grep -w "$action" > /dev/null; then - if [ "$action" = "init" ]; then - echo "🧭 terraform INIT in env: ${env}" - - terraform "$action" -reconfigure -backend-config="./env/$env/backend.tfvars" "${other[@]}" - elif [ "$action" = "output" ] || [ "$action" = "state" ] || [ "$action" = "taint" ]; then - echo "🧭 terraform (output|state|taint) launched with action: ${action} in env: ${env}" - - terraform init -reconfigure -backend-config="./env/$env/backend.tfvars" - terraform "$action" "${other[@]}" - else - echo "🧭 terraform launched with action: ${action} in env: ${env} into folder $(pwd)" - - terraform init -reconfigure -backend-config="./env/$env/backend.tfvars" - terraform "$action" -var-file="./env/$env/terraform.tfvars" "${other[@]}" - fi -else - echo "Action not allowed." - exit 1 -fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..c82e5ce7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: + - repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.86.0 + hooks: + - id: terraform_fmt + - id: terraform_docs + - id: terraform_validate + args: + - --args=-json + - --args=-no-color + exclude: '(\/_?modules\/.*)' \ No newline at end of file diff --git a/.identity/.terraform-version b/.terraform-version similarity index 100% rename from .identity/.terraform-version rename to .terraform-version diff --git a/CODEOWNERS b/CODEOWNERS index 6552397b..b80f78ba 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1,7 @@ -@lucacavallaro @grausof \ No newline at end of file +# see https://help.github.com/en/articles/about-code-owners#example-of-a-codeowners-file + +* @lucacavallaro @grausof + +# engineering-team-cloud-eng +/infra/ @pagopa/engineering-team-cloud-eng +/.github/ @pagopa/engineering-team-cloud-eng \ No newline at end of file diff --git a/infra/github-runner/prod/.terraform.lock.hcl b/infra/github-runner/prod/.terraform.lock.hcl new file mode 100644 index 00000000..cc7d59a2 --- /dev/null +++ b/infra/github-runner/prod/.terraform.lock.hcl @@ -0,0 +1,48 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/azure/azapi" { + version = "1.13.1" + constraints = "~> 1.12" + hashes = [ + "h1:2cnqo8u7YMuBexFZv8/lXGxIn1dXuEnC44LAL90GAa0=", + "h1:EHLgSlpuzCcWaDBCB3J1D5xkHKoEeX8TkeYNlnRA1qc=", + "h1:Q10vF78s0i71/CfRYbeoRLAWuJrat2kxIrHK9/yaEYk=", + "h1:xDZG4lbtQJeyJa3Gzo8qecYxyw+AIXYcdDRlkaSLNz8=", + "zh:1f2aceddd67ceeb82a75c2f15dc01e54781e9aed5968507dbc29590c165b2e2b", + "zh:397f0bfbac899d48e23cecf38d362c27562150aa20b19157b5bd370b8e6801ee", + "zh:652263b7d00623684e29ef7b8ff285a17c5bd7cc8ba7d22967c66d0b3a3c568a", + "zh:652c53320a41434942877515780296a1509be03f32d54e60178f39200f960a67", + "zh:666426faf686401e54ec09fe06e9d7c06a6455ec398764f70558440c73aeb7f9", + "zh:6aa91ae8ba78f2494f99b4c99e66d15ed0b14d735cd1f77adc12ff9dfa075807", + "zh:a529e5a13c37d1805c469227f08cdbe7527d04dd64d18709d26627c6a0b588b1", + "zh:a589c049205e8e5bf94a13d56b28f400d908ad27e13e16df64408ee82eb8a0ff", + "zh:a9a50defdee230f315f74be6c77ff104fe2610a1b3ad6b87326f555e80d13b18", + "zh:ba49ef70d96e13795e2dbffd6cb2ff976dfe84e0373a5971ebe3b4c9c9b7af60", + "zh:d3ed50efe5f8c80d3d7d464ab9a13ccf82440d871c9ce3032ce476845364c6b9", + "zh:e3eb48ee8c36ee4f81850d8a21fc59b81886c729d7c3b7adece4a25f355bed2f", + ] +} + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.108.0" + constraints = "~> 3.50, ~> 3.86, <= 3.108.0" + hashes = [ + "h1:36WHCMjguUKG15iS3WNqmk2/FQH2AwFL0mJl0VWCfps=", + "h1:Hi1qvx4ADlHsEArf05M5DAqN6PA9g1eYFVQhUFiyHnM=", + "h1:RIFBFTXz4X48JDHjbQHX4y400ax1/uEzMVFZgX3/z3w=", + "h1:pNUojXaVobFjmYvp9HViTZ9FKp6yFy+coPbn4/GR138=", + "zh:2afecf948fd702bc08c87d9114595809d011f99a70a12dbf6bc67a12d0bee5fc", + "zh:395b6d1384a579867064e62d49b0b91e15919c33b03ea8b5031c2779bfa16b3d", + "zh:3e5594c59b6b02bc6e0f4c3de71aa2ab992494c53725fb3c64d36745f3814ef3", + "zh:4613e190609377309f6a4ac44f631c9469efab3ae148dbb09e73718201dc4f42", + "zh:624f01cb7604d58100068401bd07ab09a141e7bd318f8214127838cf202e4868", + "zh:65709950c9933e38704e2075a2339951e1259a6e882f35d390be36e1844ebc72", + "zh:af82657fad4e3a177f2ebb8035b45bda40f8856eb999288533321028794d03e5", + "zh:c40b331eba08830d16c0e6795fa7cbf08231073df2cfdb0f34e9d908a915981a", + "zh:d6ccd533a0bd984ca7ed1ae860e057e9e2f88468745be9712236d2d240353de4", + "zh:f361fd398e8772f8554a010331d161d6f7284a43238fd28bfa7b41795a5538b8", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:f8c2132c77d35930203ec66f1bf9bbf633a2406e9f7b572ff425d65b8aa8c492", + ] +} diff --git a/infra/github-runner/prod/README.md b/infra/github-runner/prod/README.md new file mode 100644 index 00000000..c7edc2a2 --- /dev/null +++ b/infra/github-runner/prod/README.md @@ -0,0 +1,34 @@ +# IO SIGN - GitHub Self-Hosted runner + + + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | <= 3.108.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [container\_app\_job\_selfhosted\_runner](#module\_container\_app\_job\_selfhosted\_runner) | github.com/pagopa/dx//infra/modules/github_selfhosted_runner_on_container_app_jobs | main | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +| Name | Description | +|------|-------------| +| [container\_app\_job](#output\_container\_app\_job) | n/a | + diff --git a/infra/github-runner/prod/container_app_job.tf b/infra/github-runner/prod/container_app_job.tf new file mode 100644 index 00000000..66248c8b --- /dev/null +++ b/infra/github-runner/prod/container_app_job.tf @@ -0,0 +1,20 @@ +module "container_app_job_selfhosted_runner" { + source = "github.com/pagopa/dx//infra/modules/github_selfhosted_runner_on_container_app_jobs?ref=main" + + prefix = local.prefix + env_short = local.env_short + + repo_name = "io-sign" + + container_app_environment = { + name = "${local.prefix}-${local.env_short}-github-runner-cae" + resource_group_name = "${local.prefix}-${local.env_short}-github-runner-rg" + } + + key_vault = { + name = "${local.prefix}-${local.env_short}-kv-common" + resource_group_name = "${local.prefix}-${local.env_short}-rg-common" + } + + tags = local.tags +} diff --git a/infra/github-runner/prod/locals.tf b/infra/github-runner/prod/locals.tf new file mode 100644 index 00000000..76198cf5 --- /dev/null +++ b/infra/github-runner/prod/locals.tf @@ -0,0 +1,13 @@ +locals { + prefix = "io" + env_short = "p" + + tags = { + CostCenter = "TS310 - PAGAMENTI & SERVIZI" + CreatedBy = "Terraform" + Environment = "Prod" + Owner = "IO" + Source = "https://github.com/pagopa/io-sign/infra/github-runner/prod" + ManagementTeam = "IO Firma" + } +} diff --git a/infra/github-runner/prod/main.tf b/infra/github-runner/prod/main.tf new file mode 100644 index 00000000..20aaf9d2 --- /dev/null +++ b/infra/github-runner/prod/main.tf @@ -0,0 +1,20 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "<= 3.108.0" + } + } + + backend "azurerm" { + resource_group_name = "terraform-state-rg" + storage_account_name = "tfappprodio" + container_name = "terraform-state" + key = "io-sign.github-runner.tfstate" + } +} + +provider "azurerm" { + features { + } +} diff --git a/infra/github-runner/prod/outputs.tf b/infra/github-runner/prod/outputs.tf new file mode 100644 index 00000000..75a41704 --- /dev/null +++ b/infra/github-runner/prod/outputs.tf @@ -0,0 +1,7 @@ +output "container_app_job" { + value = { + id = module.container_app_job_selfhosted_runner.container_app_job.id + name = module.container_app_job_selfhosted_runner.container_app_job.name + resource_group_name = module.container_app_job_selfhosted_runner.container_app_job.resource_group_name + } +} diff --git a/infra/identity/prod/.terraform.lock.hcl b/infra/identity/prod/.terraform.lock.hcl new file mode 100644 index 00000000..0bf2312c --- /dev/null +++ b/infra/identity/prod/.terraform.lock.hcl @@ -0,0 +1,22 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.108.0" + constraints = "~> 3.30, ~> 3.86, ~> 3.106, <= 3.108.0" + hashes = [ + "h1:36WHCMjguUKG15iS3WNqmk2/FQH2AwFL0mJl0VWCfps=", + "zh:2afecf948fd702bc08c87d9114595809d011f99a70a12dbf6bc67a12d0bee5fc", + "zh:395b6d1384a579867064e62d49b0b91e15919c33b03ea8b5031c2779bfa16b3d", + "zh:3e5594c59b6b02bc6e0f4c3de71aa2ab992494c53725fb3c64d36745f3814ef3", + "zh:4613e190609377309f6a4ac44f631c9469efab3ae148dbb09e73718201dc4f42", + "zh:624f01cb7604d58100068401bd07ab09a141e7bd318f8214127838cf202e4868", + "zh:65709950c9933e38704e2075a2339951e1259a6e882f35d390be36e1844ebc72", + "zh:af82657fad4e3a177f2ebb8035b45bda40f8856eb999288533321028794d03e5", + "zh:c40b331eba08830d16c0e6795fa7cbf08231073df2cfdb0f34e9d908a915981a", + "zh:d6ccd533a0bd984ca7ed1ae860e057e9e2f88468745be9712236d2d240353de4", + "zh:f361fd398e8772f8554a010331d161d6f7284a43238fd28bfa7b41795a5538b8", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:f8c2132c77d35930203ec66f1bf9bbf633a2406e9f7b572ff425d65b8aa8c492", + ] +} diff --git a/infra/identity/prod/README.md b/infra/identity/prod/README.md new file mode 100644 index 00000000..837cc0dc --- /dev/null +++ b/infra/identity/prod/README.md @@ -0,0 +1,34 @@ +# IO Sign - GitHub federated Managed Identities + + + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | <= 3.108.0 | + +## Providers + +No providers. + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [federated\_identities](#module\_federated\_identities) | github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github | main | +| [federated\_identities\_opex](#module\_federated\_identities\_opex) | github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github | main | +| [federated\_identities\_web\_apps](#module\_federated\_identities\_web\_apps) | github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github | main | + +## Resources + +No resources. + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/infra/identity/prod/locals.tf b/infra/identity/prod/locals.tf new file mode 100644 index 00000000..ab5cfaa1 --- /dev/null +++ b/infra/identity/prod/locals.tf @@ -0,0 +1,18 @@ +locals { + prefix = "io" + env_short = "p" + env = "prod" + project = "${local.prefix}-${local.env_short}" + domain = "sign" + + repo_name = "io-sign" + + tags = { + CostCenter = "TS310 - PAGAMENTI & SERVIZI" + CreatedBy = "Terraform" + Environment = "Prod" + Owner = "IO" + Source = "https://github.com/pagopa/io-sign/blob/main/src/identity/prod" + ManagementTeam = "IO Firma" + } +} diff --git a/infra/identity/prod/main.tf b/infra/identity/prod/main.tf new file mode 100644 index 00000000..2f4db416 --- /dev/null +++ b/infra/identity/prod/main.tf @@ -0,0 +1,166 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "<= 3.108.0" + } + } + + backend "azurerm" { + resource_group_name = "terraform-state-rg" + storage_account_name = "tfappprodio" + container_name = "terraform-state" + key = "io-sign.identity.tfstate" + } +} + +provider "azurerm" { + features { + } +} + +module "federated_identities" { + source = "github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github?ref=main" + + prefix = local.prefix + env_short = local.env_short + env = local.env + domain = local.domain + + repositories = [local.repo_name] + + continuos_integration = { + enable = true + roles = { + subscription = [ + "Reader", + "Reader and Data Access", + "PagoPA IaC Reader", + "DocumentDB Account Contributor", + "Storage Blob Data Reader", + "Storage File Data SMB Share Reader", + "Storage Queue Data Reader", + "Storage Table Data Reader", + "Key Vault Reader", + ] + resource_groups = { + terraform-state-rg = [ + "Storage Blob Data Contributor" + ] + } + } + } + + continuos_delivery = { + enable = true + roles = { + subscription = [ + "Contributor", + "Storage Account Contributor", + "Storage Blob Data Contributor", + "Storage File Data SMB Share Contributor", + "Storage Queue Data Contributor", + "Storage Table Data Contributor", + "Key Vault Contributor", + ] + resource_groups = {} + } + } + + + tags = local.tags +} + +module "federated_identities_web_apps" { + source = "github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github?ref=main" + + prefix = local.prefix + env_short = local.env_short + env = "app-${local.env}" + domain = "${local.domain}-app" + + repositories = [local.repo_name] + continuos_integration = { + enable = false + } + + continuos_delivery = { + enable = true + + roles = { + subscription = [] + resource_groups = { + "io-p-github-runner-rg" = [ + "Contributor", + ], + "io-p-sign-backend-rg" = [ + "Contributor", + ] + } + } + } + + tags = local.tags +} + +module "federated_identities_opex" { + source = "github.com/pagopa/dx//infra/modules/azure_federated_identity_with_github?ref=main" + + prefix = local.prefix + env_short = local.env_short + env = "opex-${local.env}" + domain = "${local.domain}-opex" + + repositories = [local.repo_name] + continuos_integration = { + enable = true + + roles = { + subscription = [] + resource_groups = { + dashboards = [ + "Reader" + ] + terraform-state-rg = [ + "Storage Blob Data Reader", + "Reader and Data Access" + ] + } + } + } + + continuos_delivery = { + enable = true + + roles = { + subscription = [] + resource_groups = { + dashboards = [ + "Contributor" + ] + terraform-state-rg = [ + "Storage Blob Data Contributor", + "Reader and Data Access" + ] + } + } + } + + tags = local.tags +} + +# Access Policy +module "roles_ci" { + source = "github.com/pagopa/dx//infra/modules/azure_role_assignments?ref=main" + principal_id = module.federated_identities.federated_ci_identity.id + + key_vault = [ + { + name = "io-p-sign-kv" + resource_group_name = "io-p-sign-sec-rg" + roles = { + secrets = "reader" + } + } + ] +} \ No newline at end of file diff --git a/infra/repository/.terraform.lock.hcl b/infra/repository/.terraform.lock.hcl new file mode 100644 index 00000000..327b845a --- /dev/null +++ b/infra/repository/.terraform.lock.hcl @@ -0,0 +1,50 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.105.0" + constraints = "<= 3.105.0" + hashes = [ + "h1:MK83TecMdabDD+HjbxdTt3emXp8G6djLj7KvvUGstM0=", + "h1:OtWRTAMNOruOmwVB72QSGXC5IIGGQcHwEqnCCmsGbGM=", + "h1:SOC7EdvKd5YowghQvb6hu209F1PQqtb8LulbQkxOZQQ=", + "h1:zWkzhP2fx0WQIAUp6Amk/We3WNcbtiWagpKF5PJP5+M=", + "zh:2f81bca6a3bf3d37604bf99fdb2c77d6118520aa379ab65fd28e6b76bed399cd", + "zh:3578eb79d175af9544b0dc543124d551c0fed4c48f51773ee17e1dc62e22833a", + "zh:377dbb56caea3fa1e6a6599193b55c8594204c40c054fc2ace4f576fdfe750a2", + "zh:3d1cf01929cb213ff9a0f9753e35699bf13f60f7f0f15b38f1b216fa2cbb5f72", + "zh:481376d79224a0e4aebc6e39dee10de3cc14efd1c7c58b6d74c538e356cf4bb2", + "zh:625119aec0d24ff693c589d802b7983ffce3fcf1e9c3351396af02799dd176ca", + "zh:65981e62a6e9eb8a39dd332632617e8c929dcce6af48d3829f590f5c0f14362f", + "zh:72db82697f4e694c21defa8d0efb22f71d373676d078d71d567e8b4d9a134df7", + "zh:a0fa43cf78716ff98eccd7506b017c5c487034d9d9cce88c1accdba9314a4822", + "zh:b073f60b68b0102128815251dd895ec7f24bddec84a1b725fc0777de8a78dc7e", + "zh:b601e509eb9735756b6b7ccacc15d6333769a7bb2f8ac8c394e39b29cfc6ee55", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} + +provider "registry.terraform.io/integrations/github" { + version = "6.1.0" + constraints = "6.1.0" + hashes = [ + "h1:0BC1bA6irof4GXsbOCltW2f18OB/vp3kYhQ598IvOu0=", + "h1:LZeec2qr5cNz6MIVrQArl11E1hRnEdzkS7JUrc/8cus=", + "h1:MWD2GsKJ92kgyegYPGPjQKM0SqFaFbvOibMfDQdJsP0=", + "h1:Z1C0pLLJQF2fit8PKwc1e5Vm64q73RpayCmkDSMihqw=", + "zh:03c2a7d7fa334b5abb1ea4962bb2ffabfff96ec883b1a62445fe724d4a541313", + "zh:144f77865c87843635a3f6a0d52530ab3a6270b04dfa2da744a9fc0003b64900", + "zh:4cfa42e679be22e516b8e0294688d6cfc896c0e1456387fd9d10d09d84e99c6d", + "zh:5ff9e90b7bc9008f5b7fb0d9ef0c7c67eb8fb29439309620de1b0b1810b3e7f9", + "zh:7bfe85fcbef2b4b6ff5eff8bc82a590f2471e71297207616014c852e7385921b", + "zh:a105ec4828973821a9618c0e058f5a597de014edf7aa64d97b7f4fc528abbc36", + "zh:a495c5b3bc6ce3d6261e9d1ba7f285e7e463b5f6ad15e533d5b7037ab985530f", + "zh:a4d7e43b7b59f41022e9137115440df46aa9de62a187ae4a35fb9fc388fca4c3", + "zh:a75ab20f5032e2ebcfe288e06d0f4f8eafd8fed569be7ac7c384e55c294ada43", + "zh:cb6e9cde411355ad477a60fecb8ed9b665d8475761949e03aceed57851842385", + "zh:d833d63b5374841e667647fde74d2388d1249a097a633b4bba20ad175b7db681", + "zh:e4e5aab1a6e37fb8220621673384b62a3f2693ca1052487eb4ca38426a40bc8b", + "zh:f06a84ddf6723e880997c0f773b500b3fabcecb1230d9ed2d93943700802c876", + "zh:f9695f2ceddfc243834a10bd91cfb8aa1b0e7cdb9eee14d17d49b4f439440b86", + ] +} diff --git a/infra/repository/README.md b/infra/repository/README.md new file mode 100644 index 00000000..20d797a2 --- /dev/null +++ b/infra/repository/README.md @@ -0,0 +1,58 @@ +# IO Sign - Repository + + + +## Requirements + +| Name | Version | +|------|---------| +| [azurerm](#requirement\_azurerm) | <= 3.105.0 | +| [github](#requirement\_github) | 6.1.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | 3.105.0 | +| [github](#provider\_github) | 6.1.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [github_actions_environment_secret.env_opex_prod_cd_secrets](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource | +| [github_actions_environment_secret.env_opex_prod_ci_secrets](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource | +| [github_actions_environment_secret.env_prod_cd_secrets](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource | +| [github_actions_environment_secret.env_prod_ci_secrets](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource | +| [github_actions_environment_secret.web_app_client_id](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_environment_secret) | resource | +| [github_actions_secret.repo_secrets](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/actions_secret) | resource | +| [github_branch_default.default_main](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/branch_default) | resource | +| [github_branch_protection.main](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/branch_protection) | resource | +| [github_repository.this](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository) | resource | +| [github_repository_environment.github_repository_environment_opex_prod_cd](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource | +| [github_repository_environment.github_repository_environment_opex_prod_ci](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource | +| [github_repository_environment.github_repository_environment_prod_cd](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource | +| [github_repository_environment.github_repository_environment_prod_ci](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource | +| [github_repository_environment.github_repository_environment_web_apps_cd](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/resources/repository_environment) | resource | +| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source | +| [azurerm_resources.web_apps](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resources) | data source | +| [azurerm_subscription.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source | +| [azurerm_user_assigned_identity.identity_app_prod_cd](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/user_assigned_identity) | data source | +| [azurerm_user_assigned_identity.identity_opex_prod_cd](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/user_assigned_identity) | data source | +| [azurerm_user_assigned_identity.identity_opex_prod_ci](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/user_assigned_identity) | data source | +| [azurerm_user_assigned_identity.identity_prod_cd](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/user_assigned_identity) | data source | +| [azurerm_user_assigned_identity.identity_prod_ci](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/user_assigned_identity) | data source | +| [github_organization_teams.all](https://registry.terraform.io/providers/integrations/github/6.1.0/docs/data-sources/organization_teams) | data source | + +## Inputs + +No inputs. + +## Outputs + +No outputs. + diff --git a/infra/repository/data.tf b/infra/repository/data.tf new file mode 100644 index 00000000..5275cfc3 --- /dev/null +++ b/infra/repository/data.tf @@ -0,0 +1,41 @@ +data "azurerm_user_assigned_identity" "identity_prod_ci" { + name = "${local.project}-sign-github-ci-identity" + resource_group_name = local.identity_resource_group_name +} + +data "azurerm_user_assigned_identity" "identity_prod_cd" { + name = "${local.project}-sign-github-cd-identity" + resource_group_name = local.identity_resource_group_name +} + +data "azurerm_user_assigned_identity" "identity_opex_prod_ci" { + name = "${local.project}-sign-opex-github-ci-identity" + resource_group_name = local.identity_resource_group_name +} + +data "azurerm_user_assigned_identity" "identity_opex_prod_cd" { + name = "${local.project}-sign-opex-github-cd-identity" + resource_group_name = local.identity_resource_group_name +} + +data "azurerm_user_assigned_identity" "identity_app_prod_cd" { + name = "${local.project}-sign-app-github-cd-identity" + resource_group_name = local.identity_resource_group_name +} + +data "github_organization_teams" "all" { + root_teams_only = true + summary_only = true +} + +# Web app configuration +data "azurerm_resources" "web_apps" { + resource_group_name = local.backend_resource_group_name + type = "Microsoft.Web/sites" +} + +# data "azurerm_linux_web_app" "web_apps" { +# for_each = local.web_apps_map +# resource_group_name = local.backend_resource_group_name +# name = each.value.name +# } \ No newline at end of file diff --git a/infra/repository/github_branch_rules.tf b/infra/repository/github_branch_rules.tf new file mode 100644 index 00000000..c003676f --- /dev/null +++ b/infra/repository/github_branch_rules.tf @@ -0,0 +1,35 @@ +resource "github_branch_default" "default_main" { + repository = github_repository.this.name + branch = "main" +} + +resource "github_branch_protection" "main" { + repository_id = github_repository.this.name + pattern = "main" + + force_push_bypassers = [] + + required_status_checks { + strict = true + contexts = [] + } + + require_conversation_resolution = true + required_linear_history = true + + #tfsec:ignore:github-branch_protections-require_signed_commits + require_signed_commits = true #false + + required_pull_request_reviews { + dismiss_stale_reviews = false + require_code_owner_reviews = true + required_approving_review_count = 1 + dismissal_restrictions = [ + "/lucacavallaro", + ] + pull_request_bypassers = [] + restrict_dismissals = true + } + + allows_deletions = false +} diff --git a/infra/repository/github_environment_cd.tf b/infra/repository/github_environment_cd.tf new file mode 100644 index 00000000..f6210c26 --- /dev/null +++ b/infra/repository/github_environment_cd.tf @@ -0,0 +1,78 @@ +resource "github_repository_environment" "github_repository_environment_prod_cd" { + environment = "prod-cd" + repository = github_repository.this.name + + deployment_branch_policy { + protected_branches = false + custom_branch_policies = true + } + + reviewers { + teams = matchkeys( + data.github_organization_teams.all.teams[*].id, + data.github_organization_teams.all.teams[*].slug, + local.cd.reviewers_teams + ) + } +} + +resource "github_actions_environment_secret" "env_prod_cd_secrets" { + for_each = local.cd.secrets + + repository = github_repository.this.name + environment = github_repository_environment.github_repository_environment_prod_cd.environment + secret_name = each.key + plaintext_value = each.value +} + +# -------------- OPEX ---------------- + +resource "github_repository_environment" "github_repository_environment_opex_prod_cd" { + environment = "opex-prod-cd" + repository = github_repository.this.name + + deployment_branch_policy { + protected_branches = false + custom_branch_policies = true + } + reviewers { + teams = matchkeys( + data.github_organization_teams.all.teams[*].id, + data.github_organization_teams.all.teams[*].slug, + local.cd_opex.reviewers_teams + ) + } +} + +resource "github_actions_environment_secret" "env_opex_prod_cd_secrets" { + for_each = local.cd_opex.secrets + + repository = github_repository.this.name + environment = github_repository_environment.github_repository_environment_opex_prod_cd.environment + secret_name = each.key + plaintext_value = each.value +} + +# -------------- WEB-APP ---------------- + +resource "github_repository_environment" "github_repository_environment_web_apps_cd" { + environment = "app-prod-cd" + repository = github_repository.this.name + + reviewers { + teams = matchkeys( + data.github_organization_teams.all.teams[*].id, + data.github_organization_teams.all.teams[*].slug, + local.cd_web_apps.reviewers_teams + ) + } +} + +resource "github_actions_environment_secret" "web_app_client_id" { + for_each = local.cd_web_apps.secrets + + repository = github_repository.this.name + environment = github_repository_environment.github_repository_environment_web_apps_cd.environment + secret_name = each.key + plaintext_value = each.value +} \ No newline at end of file diff --git a/infra/repository/github_environment_ci.tf b/infra/repository/github_environment_ci.tf new file mode 100644 index 00000000..6f6c6064 --- /dev/null +++ b/infra/repository/github_environment_ci.tf @@ -0,0 +1,42 @@ +resource "github_repository_environment" "github_repository_environment_prod_ci" { + environment = "prod-ci" + repository = github_repository.this.name + + deployment_branch_policy { + protected_branches = false + custom_branch_policies = true + } + +} + +resource "github_actions_environment_secret" "env_prod_ci_secrets" { + for_each = local.ci.secrets + + repository = github_repository.this.name + environment = github_repository_environment.github_repository_environment_prod_ci.environment + secret_name = each.key + plaintext_value = each.value +} + +# -------------- OPEX ---------------- + +resource "github_repository_environment" "github_repository_environment_opex_prod_ci" { + environment = "opex-prod-ci" + repository = github_repository.this.name + + prevent_self_review = false + + deployment_branch_policy { + protected_branches = false + custom_branch_policies = true + } +} + +resource "github_actions_environment_secret" "env_opex_prod_ci_secrets" { + for_each = local.ci_opex.secrets + + repository = github_repository.this.name + environment = github_repository_environment.github_repository_environment_opex_prod_ci.environment + secret_name = each.key + plaintext_value = each.value +} diff --git a/.identity/github_repo_secrets.tf b/infra/repository/github_repo_secrets.tf similarity index 56% rename from .identity/github_repo_secrets.tf rename to infra/repository/github_repo_secrets.tf index 3494f189..3c47ba4b 100644 --- a/.identity/github_repo_secrets.tf +++ b/infra/repository/github_repo_secrets.tf @@ -1,6 +1,7 @@ resource "github_actions_secret" "repo_secrets" { - for_each = local.repo_secrets - repository = var.github.repository + for_each = local.repo_secrets + + repository = github_repository.this.name secret_name = each.key plaintext_value = each.value -} \ No newline at end of file +} diff --git a/infra/repository/github_repository.tf b/infra/repository/github_repository.tf new file mode 100644 index 00000000..2fabb3aa --- /dev/null +++ b/infra/repository/github_repository.tf @@ -0,0 +1,31 @@ +resource "github_repository" "this" { + name = "io-sign" + description = "IO platform feature that allows the signing of PDF documents" + + #tfsec:ignore:github-repositories-private + visibility = "public" + + allow_auto_merge = false + allow_update_branch = true + allow_rebase_merge = false + allow_merge_commit = false + allow_squash_merge = true + squash_merge_commit_title = "PR_TITLE" + squash_merge_commit_message = "BLANK" + + delete_branch_on_merge = true + + has_projects = false + has_wiki = false + has_discussions = false + has_issues = false + has_downloads = true + + homepage_url = "https://firma.io.italia.it/" + + topics = ["io-sign", "io"] + + vulnerability_alerts = true + + archive_on_destroy = false +} diff --git a/infra/repository/locals.tf b/infra/repository/locals.tf new file mode 100644 index 00000000..9e0eea7d --- /dev/null +++ b/infra/repository/locals.tf @@ -0,0 +1,48 @@ +locals { + project = "io-p" + + identity_resource_group_name = "${local.project}-identity-rg" + backend_resource_group_name = "${local.project}-sign-backend-rg" + + repo_secrets = { + "ARM_TENANT_ID" = data.azurerm_client_config.current.tenant_id, + "ARM_SUBSCRIPTION_ID" = data.azurerm_subscription.current.subscription_id + } + + ci = { + secrets = { + "ARM_CLIENT_ID" = data.azurerm_user_assigned_identity.identity_prod_ci.client_id + } + } + + cd = { + secrets = { + "ARM_CLIENT_ID" = data.azurerm_user_assigned_identity.identity_prod_cd.client_id + } + reviewers_teams = ["io-sign-admins", "io-sign-maintainers", "engineering-team-cloud-eng"] + } + + # OPEX + ci_opex = { + secrets = { + "ARM_CLIENT_ID" = data.azurerm_user_assigned_identity.identity_opex_prod_ci.client_id + } + } + + cd_opex = { + secrets = { + "ARM_CLIENT_ID" = data.azurerm_user_assigned_identity.identity_opex_prod_cd.client_id + } + reviewers_teams = ["io-sign-admins", "io-sign-maintainers", "engineering-team-cloud-eng"] + } + + # WEB-APP + # web_apps_map = { for w in data.azurerm_resources.web_apps.resources : w.name => w } + + cd_web_apps = { + secrets = { + "ARM_CLIENT_ID" = data.azurerm_user_assigned_identity.identity_app_prod_cd.client_id + } + reviewers_teams = ["io-sign-admins", "io-sign-maintainers", "engineering-team-cloud-eng"] + } +} diff --git a/infra/repository/main.tf b/infra/repository/main.tf new file mode 100644 index 00000000..ca89f279 --- /dev/null +++ b/infra/repository/main.tf @@ -0,0 +1,34 @@ +terraform { + + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "<= 3.105.0" + } + + github = { + source = "integrations/github" + version = "6.1.0" + } + } + + backend "azurerm" { + resource_group_name = "terraform-state-rg" + storage_account_name = "tfappprodio" + container_name = "terraform-state" + key = "io-sign.repository.tfstate" + } +} + +provider "azurerm" { + features { + } +} + +provider "github" { + owner = "pagopa" +} + +data "azurerm_client_config" "current" {} + +data "azurerm_subscription" "current" {} diff --git a/infra/resources/prod/.terraform.lock.hcl b/infra/resources/prod/.terraform.lock.hcl new file mode 100644 index 00000000..f02c34e7 --- /dev/null +++ b/infra/resources/prod/.terraform.lock.hcl @@ -0,0 +1,93 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azuread" { + version = "2.33.0" + constraints = "<= 2.33.0" + hashes = [ + "h1:PDiZA9QpXCkaSuWu6jiCRcjVtKJETqjcOZq4I434zfE=", + "h1:QAQe2+WSqGnHYAVoA+NN4Oeuoqg5sXq3U9Qmj6S1P5M=", + "h1:XIvCW3Nl4bW1bc9f8jyGhft+fQjaed4yy/LFzDAeVJ8=", + "h1:Z28tjly5UfKOE+HL/oALxCPhmCuBwUgZ4uaYt68VR3M=", + "zh:0602d03d7d7e38819f78dc377e64f365427496edf1065bfbb113e3921ab1c34e", + "zh:08843838f4fe146084592472648d4ea7191931eabe042a96c3b3c6eaf8ddfb43", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:26a0d8a186e3b47ea0b7217a8e420b03fda59b7a680bb3ea52cf7d3e6d965ef3", + "zh:352a1cacaacd39e796de15a52d192ab0e6eb98dd36b5fbf8ebddd37e6dafa4ac", + "zh:3702ad4c534e67e2e07b060bfe5e6edc244c59c911906c8b15b96e7fecb0ff2c", + "zh:93b5248d26bdd44845b2ab051a2168c7edad788ae9836f62ea5fb632fd59d7ea", + "zh:a7b880155f4a67b52a5bfe78de33dc55254ef80006234f00e36aaf6533b1de4a", + "zh:a7cf0829364127c9bca26ec01ea3d66988b43987b2d26a3290487d1fc0da50eb", + "zh:b1f82b0d30af733b36a2f849799e0b1ed6a72888fa32a438c829c4e5cff88e20", + "zh:b6c2b23770852de8f56b549579c2f5a82afd84a9ca0616d53a25d48488f7aaf0", + "zh:d87dfbdfe8ab9d3a2e33f210333d40f211ea7d33bfa671063e6807c6ddd85a52", + ] +} + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.113.0" + constraints = "~> 3.30, ~> 3.76, ~> 3.95, != 3.97.0, != 3.97.1, <= 3.113.0" + hashes = [ + "h1:32/ivdUXEkPBOQE9aVmuUafSbJNjvptDLHtOk/PXtvQ=", + "h1:SMgp1in4oxxVeICJMy8qg3WG00jDmLwUfgd9jlO3Ysw=", + "h1:SbNQLapCxbTbhM37LaRALPizAZMiA5sTRC09sUWgZOo=", + "h1:eEUtt0lrLdpVaF6FiDq8BGQPgEcykmhj0aNIL7hTOGw=", + "zh:12479f5664288943400447b55e50df675c28ae82ad8d373cc2e5682f3a3411f0", + "zh:1b42a14e80e568429d3b55fed753ca3ef0df9dcdfa107890d7264599c020940f", + "zh:381be6ca617f848de3baa3985a6e1788e91a803afe04a3c5c727453528b6310d", + "zh:3e70e2e07b6db1c363de3e5d0ca47f27fc956473df03329c7d2e54d3ac29176b", + "zh:87c7633aeaa828098c6055da9e67d4acaf4b46748b6b3f0267e105e55f05de25", + "zh:8d0d98226901f874770dd5220d4701a12ae8bd586994615aa7dcba12b9736bec", + "zh:9fd913acd42a60c3a90a18ce803567ef861db8779a59aacced91f2cbd86de9d9", + "zh:b6f3f7ae0a055437fb36c139af9bb3135e7f4dad172157ae1eb0177dc74d703f", + "zh:b927027ba2bf40d34e03d742fd2b6c5299023b5ab8e6f05e50aac76a46ad1094", + "zh:ceb5187b9d2a439f4e48944f3ffeeeaf47a03dbe6f3325ea1775bf659ce0aa88", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + "zh:fb9d78dfeca7489bffca9b1a1f3abee7f16dbbcba31388aea1102062c1d6dce8", + ] +} + +provider "registry.terraform.io/hashicorp/http" { + version = "3.4.4" + hashes = [ + "h1:EqpXf0aaUCTyYzlNP9XhjzAMMImD1T5bAy1pgLjWx4s=", + "h1:WSgOdMqgN48NIZy0CTKY2hMTNdc4rChYPUDEWYXeaTQ=", + "h1:dDGRXAVxwKgjVzA7VsO7MpYxt+eHnJosFV7rPZ4842o=", + "h1:rkI0TvsuQLwWFwbYG0+X/u726CVj6C6TwUebmMMlOMU=", + "zh:28910c348aff60df15cb70c2838c5dac463de5d52fe41a511f122b0b5fa6032d", + "zh:61ddcdb703900b01a8d38c67bd68304e87e05aa82c2d6636a5c49813b0cee8bf", + "zh:6d7ba9fcebff1079b9cbad066874d83680a4aedc997baa597927f59b29a69186", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:82caa166f57808dd8421e9edf51bca0692135ca06ab548d5a2e3fe612bdd45a6", + "zh:95cb8ece59966d8f4020660879728dabaa158b3d188f22c0b92229347e740346", + "zh:ae56558b4262a4de250eec83e200ea4647badde10d1a14ed273f4daff650336f", + "zh:c1c5051eab9d9759fdb31bca6d7575a693558887a1156fa5f268963e05be4d92", + "zh:c90234ce3877e54be5b43493f51b582c6f9cb09138844cb048f63e9cd9f230fa", + "zh:cb237c6c47f085bf15149d6d2727b8bf108267582a30e7e2cd7393115896d003", + "zh:e7d782985f8b422cf265a856541ddb14f0d3ab0b54eb1aad6087ccfedacc7335", + "zh:ed0cc12d15226499fc7d173ad2b156c1934efae718cf254e79ca7f0ccd686b6d", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.2" + constraints = "~> 3.2" + hashes = [ + "h1:IMVAUHKoydFrlPrl9OzasDnw/8ntZFerCC9iXw1rXQY=", + "h1:m467k2tZ9cdFFgHW7LPBK2GLPH43LC6wc3ppxr8yvoE=", + "h1:vWAsYRd7MjYr3adj8BVKRohVfHpWQdvkIwUQ2Jf5FVM=", + "h1:zT1ZbegaAYHwQa+QwIFugArWikRJI9dqohj8xb0GY88=", + "zh:3248aae6a2198f3ec8394218d05bd5e42be59f43a3a7c0b71c66ec0df08b69e7", + "zh:32b1aaa1c3013d33c245493f4a65465eab9436b454d250102729321a44c8ab9a", + "zh:38eff7e470acb48f66380a73a5c7cdd76cc9b9c9ba9a7249c7991488abe22fe3", + "zh:4c2f1faee67af104f5f9e711c4574ff4d298afaa8a420680b0cb55d7bbc65606", + "zh:544b33b757c0b954dbb87db83a5ad921edd61f02f1dc86c6186a5ea86465b546", + "zh:696cf785090e1e8cf1587499516b0494f47413b43cb99877ad97f5d0de3dc539", + "zh:6e301f34757b5d265ae44467d95306d61bef5e41930be1365f5a8dcf80f59452", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:913a929070c819e59e94bb37a2a253c228f83921136ff4a7aa1a178c7cce5422", + "zh:aa9015926cd152425dbf86d1abdbc74bfe0e1ba3d26b3db35051d7b9ca9f72ae", + "zh:bb04798b016e1e1d49bcc76d62c53b56c88c63d6f2dfe38821afef17c416a0e1", + "zh:c23084e1b23577de22603cff752e59128d83cfecc2e6819edadd8cf7a10af11e", + ] +} diff --git a/infra/resources/prod/README.md b/infra/resources/prod/README.md new file mode 100644 index 00000000..6cceb081 --- /dev/null +++ b/infra/resources/prod/README.md @@ -0,0 +1,181 @@ + + +## Requirements + +| Name | Version | +|------|---------| +| [azuread](#requirement\_azuread) | >= 2.33.0 | +| [azurerm](#requirement\_azurerm) | >= 3.40.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azuread](#provider\_azuread) | 2.33.0 | +| [azurerm](#provider\_azurerm) | 3.40.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [apim\_io\_sign\_issuer\_api\_v1](#module\_apim\_io\_sign\_issuer\_api\_v1) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_api | v4.1.3 | +| [apim\_io\_sign\_product](#module\_apim\_io\_sign\_product) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_product | v4.1.3 | +| [apim\_io\_sign\_support\_api\_v1](#module\_apim\_io\_sign\_support\_api\_v1) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_api | v4.1.3 | +| [apim\_io\_sign\_support\_product](#module\_apim\_io\_sign\_support\_product) | git::https://github.com/pagopa/terraform-azurerm-v3.git//api_management_product | v4.1.3 | +| [cosmosdb\_account](#module\_cosmosdb\_account) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_account | v4.1.8 | +| [cosmosdb\_sql\_container\_issuer-dossiers](#module\_cosmosdb\_sql\_container\_issuer-dossiers) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_issuer-issuers](#module\_cosmosdb\_sql\_container\_issuer-issuers) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_issuer-issuers-by-vat-number](#module\_cosmosdb\_sql\_container\_issuer-issuers-by-vat-number) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_issuer-issuers-whitelist](#module\_cosmosdb\_sql\_container\_issuer-issuers-whitelist) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_issuer-signature-requests](#module\_cosmosdb\_sql\_container\_issuer-signature-requests) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_issuer-uploads](#module\_cosmosdb\_sql\_container\_issuer-uploads) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_user-signature-requests](#module\_cosmosdb\_sql\_container\_user-signature-requests) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_container\_user-signatures](#module\_cosmosdb\_sql\_container\_user-signatures) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_container | v4.1.11 | +| [cosmosdb\_sql\_database\_issuer](#module\_cosmosdb\_sql\_database\_issuer) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_database | v4.1.3 | +| [cosmosdb\_sql\_database\_user](#module\_cosmosdb\_sql\_database\_user) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cosmosdb_sql_database | v4.1.3 | +| [event\_hub](#module\_event\_hub) | git::https://github.com/pagopa/terraform-azurerm-v3.git//eventhub | v4.1.7 | +| [io\_sign\_eventhub\_snet](#module\_io\_sign\_eventhub\_snet) | git::https://github.com/pagopa/terraform-azurerm-v3.git//subnet | v4.1.4 | +| [io\_sign\_issuer\_func](#module\_io\_sign\_issuer\_func) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app | v6.2.1 | +| [io\_sign\_issuer\_func\_staging\_slot](#module\_io\_sign\_issuer\_func\_staging\_slot) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app_slot | v6.0.1 | +| [io\_sign\_snet](#module\_io\_sign\_snet) | git::https://github.com/pagopa/terraform-azurerm-v3.git//subnet | v4.1.4 | +| [io\_sign\_storage](#module\_io\_sign\_storage) | git::https://github.com/pagopa/terraform-azurerm-v3.git//storage_account | v4.1.5 | +| [io\_sign\_support\_func](#module\_io\_sign\_support\_func) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app | v6.1.0 | +| [io\_sign\_support\_func\_staging\_slot](#module\_io\_sign\_support\_func\_staging\_slot) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app_slot | v6.1.0 | +| [io\_sign\_support\_snet](#module\_io\_sign\_support\_snet) | git::https://github.com/pagopa/terraform-azurerm-v3.git//subnet | v4.1.4 | +| [io\_sign\_user\_func](#module\_io\_sign\_user\_func) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app | v6.2.1 | +| [io\_sign\_user\_func\_staging\_slot](#module\_io\_sign\_user\_func\_staging\_slot) | git::https://github.com/pagopa/terraform-azurerm-v3.git//function_app_slot | v6.0.1 | +| [io\_sign\_user\_snet](#module\_io\_sign\_user\_snet) | git::https://github.com/pagopa/terraform-azurerm-v3.git//subnet | v4.1.4 | +| [key\_vault](#module\_key\_vault) | git::https://github.com/pagopa/terraform-azurerm-v3.git//key_vault | v6.2.2 | +| [key\_vault\_secrets](#module\_key\_vault\_secrets) | git::https://github.com/pagopa/terraform-azurerm-v3.git//key_vault_secrets_query | v4.1.3 | +| [landing\_cdn](#module\_landing\_cdn) | git::https://github.com/pagopa/terraform-azurerm-v3.git//cdn | v6.3.1 | + +## Resources + +| Name | Type | +|------|------| +| [azurerm_api_management_api_operation_policy.get_signer_by_fiscal_code_policy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_api_operation_policy) | resource | +| [azurerm_api_management_named_value.io_fn_sign_issuer_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_fn_sign_issuer_url](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_fn_sign_support_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_fn_sign_support_url](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_sign_cosmosdb_issuer_container_name](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_sign_cosmosdb_issuer_issuers_collection_name](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_sign_cosmosdb_issuer_whitelist_collection_name_new](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_sign_cosmosdb_key](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_api_management_named_value.io_sign_cosmosdb_name](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_named_value) | resource | +| [azurerm_dns_cname_record.dkim1_mailup_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_cname_record) | resource | +| [azurerm_dns_cname_record.dkim2_mailup_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_cname_record) | resource | +| [azurerm_dns_cname_record.ses_validation_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_cname_record) | resource | +| [azurerm_dns_mx_record.ses_mx_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_mx_record) | resource | +| [azurerm_dns_txt_record.dmarc_mailup_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_txt_record) | resource | +| [azurerm_dns_txt_record.spf1_mailup_firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_txt_record) | resource | +| [azurerm_dns_zone.firma_io_pagopa_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/dns_zone) | resource | +| [azurerm_key_vault_access_policy.adgroup_admin](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_key_vault_access_policy.adgroup_contributors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_key_vault_access_policy.adgroup_developers](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_key_vault_access_policy.adgroup_sign](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_key_vault_access_policy.azdevops_platform_iac_policy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_access_policy) | resource | +| [azurerm_key_vault_secret.integration_event_hub_jaas_connection_string](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | +| [azurerm_key_vault_secret.integration_event_hub_secrets](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/key_vault_secret) | resource | +| [azurerm_monitor_action_group.email_fci_tech](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_action_group) | resource | +| [azurerm_monitor_action_group.slack_fci_tech](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_action_group) | resource | +| [azurerm_monitor_autoscale_setting.io_sign_issuer_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_autoscale_setting) | resource | +| [azurerm_monitor_autoscale_setting.io_sign_support_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_autoscale_setting) | resource | +| [azurerm_monitor_autoscale_setting.io_sign_user_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_autoscale_setting) | resource | +| [azurerm_monitor_metric_alert.io_sign_issuer_http_server_errors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_issuer_response_time](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_support_http_server_errors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_support_response_time](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_user_helathcheck](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_user_http_server_errors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.io_sign_user_response_time](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_scheduled_query_rules_alert.io_sign_qtsp_avg_async_time](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_scheduled_query_rules_alert) | resource | +| [azurerm_network_security_group.io_sign_eventhub_nsg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group) | resource | +| [azurerm_network_security_group.io_sign_issuer_nsg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group) | resource | +| [azurerm_network_security_group.io_sign_support_nsg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group) | resource | +| [azurerm_network_security_group.io_sign_user_nsg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_security_group) | resource | +| [azurerm_portal_dashboard.io_sign_user_dashboard](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/portal_dashboard) | resource | +| [azurerm_private_endpoint.blob](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_issuer_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_issuer_func_staging](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_support_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_support_func_staging](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_user_func](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.io_sign_user_func_staging](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_private_endpoint.queue](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_endpoint) | resource | +| [azurerm_resource_group.backend_rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_resource_group.data_rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_resource_group.integration_rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_resource_group.sec_rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_storage_container.filled_modules](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | +| [azurerm_storage_container.signed_documents](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | +| [azurerm_storage_container.uploaded_documents](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | +| [azurerm_storage_container.validated_documents](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | +| [azurerm_storage_management_policy.io_sign_storage_management_policy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_management_policy) | resource | +| [azurerm_storage_queue.on_signature_request_ready](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.on_signature_request_rejected](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.on_signature_request_signed](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.on_signature_request_wait_for_signature](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.waiting_for_documents_to_fill](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.waiting_for_qtsp](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_storage_queue.waiting_for_signature_request_updates](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_queue) | resource | +| [azurerm_subnet_nat_gateway_association.io_sign_issuer_snet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association) | resource | +| [azurerm_subnet_nat_gateway_association.io_sign_support_snet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association) | resource | +| [azurerm_subnet_nat_gateway_association.io_sign_user_snet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association) | resource | +| [azuread_group.adgroup_admin](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_group.adgroup_contributors](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_group.adgroup_developers](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_group.adgroup_externals](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_group.adgroup_security](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_group.adgroup_sign](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) | data source | +| [azuread_service_principal.platform_iac_sp](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/service_principal) | data source | +| [azurerm_api_management.apim_api](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/api_management) | data source | +| [azurerm_application_insights.application_insights](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/application_insights) | data source | +| [azurerm_client_config.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/client_config) | data source | +| [azurerm_dns_zone.io_italia_it](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/dns_zone) | data source | +| [azurerm_key_vault.core_kv_common](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault) | data source | +| [azurerm_key_vault_secret.monitor_fci_tech_email](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secret) | data source | +| [azurerm_key_vault_secret.monitor_fci_tech_slack_email](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secret) | data source | +| [azurerm_monitor_action_group.email](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/monitor_action_group) | data source | +| [azurerm_monitor_action_group.slack](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/monitor_action_group) | data source | +| [azurerm_nat_gateway.nat_gateway](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/nat_gateway) | data source | +| [azurerm_private_dns_zone.privatelink_azurewebsites_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_blob_core_windows_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_documents_azure_com](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_file_core_windows_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_queue_core_windows_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_servicebus_windows_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_private_dns_zone.privatelink_table_core_windows_net](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/private_dns_zone) | data source | +| [azurerm_resource_group.core_ext](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source | +| [azurerm_resource_group.core_rg_common](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group) | data source | +| [azurerm_subnet.apim](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subnet) | data source | +| [azurerm_subnet.private_endpoints_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subnet) | data source | +| [azurerm_subscription.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source | +| [azurerm_virtual_network.vnet_common](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/virtual_network) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cosmos](#input\_cosmos) | n/a |
object({
zone_redundant = bool
additional_geo_locations = list(object({
location = string
failover_priority = number
zone_redundant = bool
}))
})
| n/a | yes | +| [dns\_default\_ttl\_sec](#input\_dns\_default\_ttl\_sec) | Default TTL for DNS | `number` | `3600` | no | +| [dns\_ses\_validation](#input\_dns\_ses\_validation) | CNAME records to validate SES domain identity |
list(object({
name = string
record = string
}))
| n/a | yes | +| [dns\_zone\_name](#input\_dns\_zone\_name) | The name for the DNS zone | `string` | n/a | yes | +| [domain](#input\_domain) | n/a | `string` | n/a | yes | +| [env\_short](#input\_env\_short) | n/a | `string` | n/a | yes | +| [integration\_hub](#input\_integration\_hub) | The configuration, hubs and keys of the event hub relative to external integration |
object({
auto_inflate_enabled = bool
sku_name = string
capacity = number
maximum_throughput_units = number
zone_redundant = bool
alerts_enabled = bool
ip_rules = list(object({
ip_mask = string
action = string
}))
hubs = list(object({
name = string
partitions = number
message_retention = number
consumers = list(string)
keys = list(object({
name = string
listen = bool
send = bool
manage = bool
}))
}))
})
| n/a | yes | +| [io\_sign\_database\_issuer](#input\_io\_sign\_database\_issuer) | n/a |
map(
object({
max_throughput = number
ttl = number
})
)
| n/a | yes | +| [io\_sign\_database\_user](#input\_io\_sign\_database\_user) | n/a |
map(
object({
max_throughput = number
ttl = number
})
)
| n/a | yes | +| [io\_sign\_issuer\_func](#input\_io\_sign\_issuer\_func) | n/a |
object({
sku_tier = string
sku_size = string
autoscale_default = number
autoscale_minimum = number
autoscale_maximum = number
})
| n/a | yes | +| [io\_sign\_support\_func](#input\_io\_sign\_support\_func) | n/a |
object({
sku_tier = string
sku_size = string
autoscale_default = number
autoscale_minimum = number
autoscale_maximum = number
})
| n/a | yes | +| [io\_sign\_user\_func](#input\_io\_sign\_user\_func) | n/a |
object({
sku_tier = string
sku_size = string
autoscale_default = number
autoscale_minimum = number
autoscale_maximum = number
})
| n/a | yes | +| [location](#input\_location) | n/a | `string` | n/a | yes | +| [prefix](#input\_prefix) | n/a | `string` | n/a | yes | +| [storage\_account](#input\_storage\_account) | The configuration of the storage account storing documents |
object({
enable_versioning = bool
delete_after_days = number
replication_type = string
enable_low_availability_alert = bool
})
| n/a | yes | +| [subnets\_cidrs](#input\_subnets\_cidrs) | The CIDR address prefixes of the subnets |
map(
list(string)
)
| n/a | yes | +| [tags](#input\_tags) | n/a | `map(any)` |
{
"CreatedBy": "Terraform"
}
| no | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/infra/resources/prod/api/backoffice/v1/base_policy.xml b/infra/resources/prod/api/backoffice/v1/base_policy.xml new file mode 100644 index 00000000..b84876a9 --- /dev/null +++ b/infra/resources/prod/api/backoffice/v1/base_policy.xml @@ -0,0 +1,32 @@ + + + + + + {{io-fn-sign-backoffice-key}} + + + + * + + + * + + +
*
+
+ +
*
+
+
+
+ + + + + + + + + +
diff --git a/infra/resources/prod/api/base_policy.xml b/infra/resources/prod/api/base_policy.xml new file mode 100644 index 00000000..ce1df461 --- /dev/null +++ b/infra/resources/prod/api/base_policy.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + diff --git a/infra/resources/prod/api/issuer/v1/base_policy.xml b/infra/resources/prod/api/issuer/v1/base_policy.xml new file mode 100644 index 00000000..2e0887de --- /dev/null +++ b/infra/resources/prod/api/issuer/v1/base_policy.xml @@ -0,0 +1,35 @@ + + + + + + {{io-fn-sign-issuer-key}} + + + @(context.Subscription.Id) + + + + * + + + * + + +
*
+
+ +
*
+
+
+
+ + + + + + + + + +
diff --git a/infra/resources/prod/api/issuer/v1/get_signer_by_fiscal_code_policy/policy.xml b/infra/resources/prod/api/issuer/v1/get_signer_by_fiscal_code_policy/policy.xml new file mode 100644 index 00000000..72194021 --- /dev/null +++ b/infra/resources/prod/api/issuer/v1/get_signer_by_fiscal_code_policy/policy.xml @@ -0,0 +1,184 @@ + + + + + + + + https://{{io-sign-cosmosdb-name}}.documents.azure.com/dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}/docs + POST + + @{ + var verb = "post"; + var resourceType = "docs"; + var resourceLink = "dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}"; + var key = "{{io-sign-cosmosdb-key}}"; + var keyType = "master"; + var tokenVersion = "1.0"; + var date = context.Variables.GetValueOrDefault("requestDateString"); + + var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) }; + + string payLoad = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", + verb.ToLowerInvariant(), + resourceType.ToLowerInvariant(), + resourceLink, + date.ToLowerInvariant(), + "" + ); + + byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad)); + string signature = Convert.ToBase64String(hashPayLoad); + + return System.Uri.EscapeDataString(String.Format("type={0}&ver={1}&sig={2}", + keyType, + tokenVersion, + signature)); + } + + + application/query+json + + + True + + + True + + + @(context.Variables.GetValueOrDefault("requestDateString")) + + + 2018-12-31 + + @("{\"query\": \"SELECT w.testers FROM whitelist w WHERE w.id = @id\", " + + "\"parameters\": [{ \"name\": \"@id\", \"value\": \"" + context.Subscription.Id + "\"}]}") + + + ())" /> + + + + + + + + + + https://{{io-sign-cosmosdb-name}}.documents.azure.com/dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}/docs + POST + + @{ + var verb = "post"; + var resourceType = "docs"; + var resourceLink = "dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}"; + var key = "{{io-sign-cosmosdb-key}}"; + var keyType = "master"; + var tokenVersion = "1.0"; + var date = context.Variables.GetValueOrDefault("requestDateString"); + + var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) }; + + string payLoad = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", + verb.ToLowerInvariant(), + resourceType.ToLowerInvariant(), + resourceLink, + date.ToLowerInvariant(), + "" + ); + + byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad)); + string signature = Convert.ToBase64String(hashPayLoad); + + return System.Uri.EscapeDataString(String.Format("type={0}&ver={1}&sig={2}", + keyType, + tokenVersion, + signature)); + } + + + application/query+json + + + True + + + True + + + @(context.Variables.GetValueOrDefault("requestDateString")) + + + 2018-12-31 + + @("{\"query\": \"SELECT i.environment FROM issuers i WHERE i.id = @subscriptionId\", " + + "\"parameters\": [{ \"name\": \"@subscriptionId\", \"value\": \"" + context.Subscription.Id + "\"}]}") + + + ())" /> + + + + + + + @{ + var environmentQueryResponse = context.Variables.GetValueOrDefault("issuerEnvironment"); + if (environmentQueryResponse != null && environmentQueryResponse.ContainsKey("Documents")){ + JArray envDocuments = (JArray) environmentQueryResponse["Documents"]; + if (envDocuments.Count > 0){ + JObject envFirstDocument = (JObject) envDocuments[0]; + if(envFirstDocument.ContainsKey("environment")){ + string issuerEnvironment = (string)envFirstDocument["environment"]; + + //If the issuer is not in the test environment then I do not perform any checks + if (issuerEnvironment!="test"){ + return "true"; + } + } + } + } + + var whitelistFiscalCodeQueryResponse = context.Variables.GetValueOrDefault("issuerWhitelistFiscalCodes"); + JObject requestBody = context.Request.Body.As(preserveContent: true); + if(requestBody.ContainsKey("fiscal_code")){ + string requestFiscalCodeString = (string)requestBody["fiscal_code"]; + if (whitelistFiscalCodeQueryResponse != null && whitelistFiscalCodeQueryResponse.ContainsKey("Documents")){ + JArray documents = (JArray) whitelistFiscalCodeQueryResponse["Documents"]; + if (documents.Count > 0){ + JObject firstDocument = (JObject) documents[0]; + if(firstDocument.ContainsKey("testers")){ + JArray whiteListFiscalCodes = (JArray)firstDocument["testers"]; + foreach (var fiscalCode in whiteListFiscalCodes) { + string fiscalCodeString = (string)fiscalCode; + if(fiscalCodeString==requestFiscalCodeString){ + return "true"; + } + } + } + } + } + } + return "false"; + } + + + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/infra/resources/prod/api/issuer/v1/openapi.yaml b/infra/resources/prod/api/issuer/v1/openapi.yaml new file mode 100644 index 00000000..d61feee2 --- /dev/null +++ b/infra/resources/prod/api/issuer/v1/openapi.yaml @@ -0,0 +1,841 @@ +openapi: 3.0.3 +info: + title: Firma con IO - Issuer API + version: 1.2.1 +servers: + - url: https://api.io.pagopa.it/api/v1/sign + description: production +security: + - SubscriptionKey: [] +paths: + /dossiers: + post: + operationId: createDossier + tags: + - Dossier + summary: Create a Dossier + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateDossierBody" + responses: + "201": + description: The Dossier detail + content: + application/json: + schema: + $ref: "#/components/schemas/DossierDetailView" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /dossiers/{id}: + get: + operationId: getDossier + tags: + - Dossier + summary: Get a Dossier by Id + parameters: + - in: path + name: id + required: true + schema: + $ref: "#/components/schemas/Id" + responses: + "200": + description: The Dossier detail + content: + application/json: + schema: + $ref: "#/components/schemas/DossierDetailView" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /dossiers/{id}/signature-requests: + get: + operationId: getRequestsByDossier + tags: + - Dossier + summary: Get the Signature Requests created from a Dossier + parameters: + - in: path + name: id + required: true + schema: + $ref: "#/components/schemas/Id" + - in: query + name: continuationToken + required: false + schema: + type: string + - in: query + name: limit + required: false + schema: + type: integer + minimum: 25 + maximum: 100 + responses: + "200": + description: The Dossier detail + content: + application/json: + schema: + $ref: "#/components/schemas/SignatureRequestList" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signers: + post: + operationId: getSignerByFiscalCode + tags: + - Signer + summary: Get Signer By Fiscal COde + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/GetSignerByFiscalCodeBody" + responses: + "201": + description: The Signer detail + content: + application/json: + schema: + $ref: "#/components/schemas/SignerDetailView" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signature-requests: + post: + operationId: createSignatureRequest + tags: + - Signature Request + summary: Create a Signature Request + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateSignatureRequestBody" + responses: + "201": + description: The Signature Request detail + content: + application/json: + schema: + $ref: "#/components/schemas/SignatureRequestDetailView" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "422": + $ref: "#/components/responses/UnprocessableContent" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signature-requests/{id}: + get: + operationId: getSignatureRequest + tags: + - Signature Request + summary: Get a Signature Request by Id + parameters: + - in: path + name: id + required: true + schema: + $ref: "#/components/schemas/Id" + responses: + "200": + description: The Signature Request detail + content: + application/json: + schema: + $ref: "#/components/schemas/SignatureRequestDetailView" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signature-requests/{id}/status: + put: + operationId: setSignatureRequestStatus + tags: + - Signature Request + summary: Set the status of a Signature Request + parameters: + - in: path + name: id + required: true + schema: + $ref: "#/components/schemas/Id" + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SetSignatureRequestStatusBody" + responses: + "204": + description: Signature Request status successfully set + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "422": + $ref: "#/components/responses/UnprocessableContent" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signature-requests/{req_id}/documents/{doc_id}/upload_url: + get: + operationId: getDocumentUploadUrl + tags: + - Signature Request + summary: Get the Upload Url for the specified document + parameters: + - in: path + name: req_id + required: true + schema: + $ref: "#/components/schemas/Id" + - in: path + name: doc_id + required: true + schema: + $ref: "#/components/schemas/Id" + responses: + "200": + description: The Upload Url + content: + application/json: + schema: + $ref: "#/components/schemas/UploadUrl" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "404": + $ref: "#/components/responses/NotFound" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /signature-requests/{req_id}/notification: + put: + operationId: sendNotification + tags: + - Signature Request + summary: Send a signature request notification to user via IO message + parameters: + - in: path + name: req_id + required: true + schema: + $ref: "#/components/schemas/Id" + responses: + "201": + description: The notification detail + content: + application/json: + schema: + $ref: "#/components/schemas/NotificationDetailView" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + + /validate-document: + post: + operationId: validateDocument + tags: + - Utility + summary: Validate a PDF document against a list of signature fields + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + document: + type: string + format: binary + signature_fields: + type: array + items: + $ref: "#/components/schemas/SignatureField" + required: + - document + encoding: + document: + contentType: application/pdf + responses: + "200": + description: the validation result + content: + application/json: + schema: + $ref: "#/components/schemas/DocumentValidationResult" + "400": + $ref: "#/components/responses/BadRequest" + "401": + $ref: "#/components/responses/Unauthorized" + "403": + $ref: "#/components/responses/Forbidden" + "429": + $ref: "#/components/responses/TooManyRequests" + default: + $ref: "#/components/responses/Unexpected" + +components: + securitySchemes: + SubscriptionKey: + type: apiKey + name: Ocp-Apim-Subscription-Key + in: header + description: The API key obtained through the developer portal + + responses: + NotFound: + description: The specified resource was not found + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + BadRequest: + description: Validation error on body + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + Forbidden: + description: You don't have enough privileges to perform this action + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + UnprocessableContent: + description: Unprocessable Content + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + TooManyRequests: + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + Unexpected: + description: Unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/ProblemDetail" + + schemas: + ProblemDetail: + type: object + properties: + type: + type: string + format: uri + description: |- + An absolute URI that identifies the problem type. When dereferenced, + it SHOULD provide human-readable documentation for the problem type + (e.g., using HTML). + default: about:blank + example: https://example.com/problem/constraint-violation + title: + type: string + description: >- + A short, summary of the problem type. Written in english and + readable + + for engineers (usually not suited for non technical stakeholders and + + not localized); example: Service Unavailable + status: + type: integer + format: int32 + description: >- + The HTTP status code generated by the origin server for this + occurrence of the problem. + minimum: 100 + maximum: 600 + exclusiveMaximum: true + example: 200 + detail: + type: string + description: |- + A human readable explanation specific to this occurrence of the + problem. + example: There was an error processing the request + instance: + type: string + format: uri + description: >- + An absolute URI that identifies the specific occurrence of the + problem. It may or may not yield further information if + dereferenced. + + Clause: + type: object + properties: + title: + type: string + minLength: 5 + maxLength: 80 + type: + type: string + enum: ["REQUIRED", "UNFAIR", "OPTIONAL"] + required: + - title + - type + + ExistingSignatureFieldAttrs: + type: object + properties: + unique_name: + type: string + required: + - unique_name + + SignatureFieldToBeCreatedAttrs: + type: object + properties: + coordinates: + type: object + properties: + x: { type: number } + y: { type: number } + required: [x, y] + page: + type: number + minimum: 0 + size: + type: object + properties: + w: { type: number, minimum: 0 } + h: { type: number, minimum: 0 } + required: [w, h] + required: + - coordinates + - page + - size + + SignatureFieldAttrs: + oneOf: + - $ref: "#/components/schemas/ExistingSignatureFieldAttrs" + - $ref: "#/components/schemas/SignatureFieldToBeCreatedAttrs" + + SignatureField: + type: object + properties: + attrs: + $ref: "#/components/schemas/SignatureFieldAttrs" + clause: + $ref: "#/components/schemas/Clause" + required: + - attrs + - clause + + DocumentMetadata: + type: object + properties: + title: + type: string + minLength: 3 + maxLength: 60 + signature_fields: + type: array + items: + $ref: "#/components/schemas/SignatureField" + required: + - title + + DocumentMetadataList: + type: array + items: + $ref: "#/components/schemas/DocumentMetadata" + minLength: 1 + + DossierSupportEmail: + type: string + description: Issuer's support email for a specific dossier. + format: email + example: demo@example.com + + DossierTitle: + type: string + minLength: 3 + + CreateDossierBody: + type: object + properties: + title: + $ref: "#/components/schemas/DossierTitle" + support_email: + $ref: "#/components/schemas/DossierSupportEmail" + documents_metadata: + $ref: "#/components/schemas/DocumentMetadataList" + required: + - title + - documents_metadata + + SetSignatureRequestStatusBody: + type: string + x-extensible-enum: + - READY + - CANCELLED + + DossierDetailView: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + title: + $ref: "#/components/schemas/DossierTitle" + support_email: + $ref: "#/components/schemas/DossierSupportEmail" + documents_metadata: + $ref: "#/components/schemas/DocumentMetadataList" + created_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + required: + - id + - title + - documents_metadata + - created_at + - updated_at + + Timestamp: + type: string + format: UTCISODateFromString + description: A date-time field in ISO-8601 format and UTC timezone. + x-import: "@pagopa/ts-commons/lib/dates" + example: "2018-10-13T00:00:00.000Z" + + Id: + type: string + description: Entity Id + format: NonEmptyString + example: 01ARZ3NDEKTSV4RRFFQ69G5FAV + x-import: "@pagopa/ts-commons/lib/strings" + + FiscalCode: + type: string + description: User's fiscal code. + format: FiscalCode + x-import: "@pagopa/ts-commons/lib/strings" + example: SPNDNL80R13C555X + + GetSignerByFiscalCodeBody: + type: object + properties: + fiscal_code: + $ref: "#/components/schemas/FiscalCode" + required: + - fiscal_code + + SignerDetailView: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + required: + - id + + CreateSignatureRequestBody: + type: object + properties: + dossier_id: + $ref: "#/components/schemas/Id" + signer_id: + $ref: "#/components/schemas/Id" + expires_at: + $ref: "#/components/schemas/Timestamp" + required: + - dossier_id + - signer_id + + Document: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + metadata: + $ref: "#/components/schemas/DocumentMetadata" + created_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + required: + - id + - metadata + - created_at + - updated_at + + DocumentToBeUploaded: + allOf: + - $ref: "#/components/schemas/Document" + - type: object + properties: + status: + type: string + enum: + - WAIT_FOR_UPLOAD + required: + - status + + DocumentToBeValidated: + allOf: + - $ref: "#/components/schemas/Document" + - type: object + properties: + status: + type: string + enum: + - WAIT_FOR_VALIDATION + uploaded_at: + $ref: "#/components/schemas/Timestamp" + required: + - status + - uploaded_at + + DocumentReady: + allOf: + - $ref: "#/components/schemas/Document" + - type: object + properties: + status: + type: string + enum: + - READY + uploaded_at: + $ref: "#/components/schemas/Timestamp" + url: + type: string + format: uri + required: + - status + - uploaded_at + - url + + DocumentRejected: + allOf: + - $ref: "#/components/schemas/Document" + - type: object + properties: + status: + type: string + enum: + - REJECTED + uploaded_at: + $ref: "#/components/schemas/Timestamp" + rejected_at: + $ref: "#/components/schemas/Timestamp" + reject_reason: + type: string + required: + - status + - uploaded_at + - rejected_at + - reject_reason + + DocumentDetailView: + oneOf: + - $ref: "#/components/schemas/DocumentToBeUploaded" + - $ref: "#/components/schemas/DocumentToBeValidated" + - $ref: "#/components/schemas/DocumentReady" + - $ref: "#/components/schemas/DocumentRejected" + + SignatureRequestStatus: + type: string + x-extensible-enum: + - DRAFT + - READY + - WAIT_FOR_SIGNATURE + - SIGNED + - REJECTED + - CANCELLED + + SignatureRequestDetailView: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + status: + $ref: "#/components/schemas/SignatureRequestStatus" + dossier_id: + $ref: "#/components/schemas/Id" + signer_id: + $ref: "#/components/schemas/Id" + expires_at: + $ref: "#/components/schemas/Timestamp" + documents: + type: array + items: + $ref: "#/components/schemas/DocumentDetailView" + notification: + $ref: "#/components/schemas/NotificationDetailView" + created_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + signed_at: + $ref: "#/components/schemas/Timestamp" + rejected_at: + $ref: "#/components/schemas/Timestamp" + reject_reason: + type: string + qr_code_url: + type: string + format: uri + required: + - id + - status + - dossier_id + - signer_id + - expires_at + - documents + - created_at + - updated_at + + SignatureRequestListView: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + signer_id: + $ref: "#/components/schemas/Id" + dossier_id: + $ref: "#/components/schemas/Id" + status: + $ref: "#/components/schemas/SignatureRequestStatus" + created_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + expires_at: + $ref: "#/components/schemas/Timestamp" + required: + - id + - signer_id + - dossier_id + - status + - created_at + - updated_at + - expires_at + + SignatureRequestList: + type: object + properties: + items: + type: array + items: + $ref: "#/components/schemas/SignatureRequestListView" + continuation_token: + type: string + required: + - items + + UploadUrl: + type: string + format: uri + + NotificationDetailView: + type: object + properties: + io_message_id: + type: string + required: + - io_message_id + + DocumentValidationResult: + type: object + properties: + is_valid: + type: boolean + violations: + type: array + items: + type: string + required: + - is_valid diff --git a/infra/resources/prod/api/support/v1/base_policy.xml b/infra/resources/prod/api/support/v1/base_policy.xml new file mode 100644 index 00000000..dd8b65dc --- /dev/null +++ b/infra/resources/prod/api/support/v1/base_policy.xml @@ -0,0 +1,35 @@ + + + + + + {{io-fn-sign-support-key}} + + + @(context.Subscription.Id) + + + + * + + + * + + +
*
+
+ +
*
+
+
+
+ + + + + + + + + +
diff --git a/infra/resources/prod/api/support/v1/openapi.yaml b/infra/resources/prod/api/support/v1/openapi.yaml new file mode 100644 index 00000000..fa78fd98 --- /dev/null +++ b/infra/resources/prod/api/support/v1/openapi.yaml @@ -0,0 +1,336 @@ +openapi: 3.0.3 +info: + title: Firma con IO - Support API + version: 1.0.0 +servers: + - url: https://api.io.pagopa.it/api/v1/sign/support + description: production + - url: http://localhost:7071/api/v1/sign/support + description: local +paths: + /signature-requests/{id}: + post: + operationId: getSignatureRequestById + tags: + - signature request + summary: Find Signature Request by Id + requestBody: + $ref: "#/components/requestBodies/GetSignatureRequestById" + parameters: + - in: path + name: id + description: the signature request id + required: true + schema: + $ref: "#/components/schemas/Id" + responses: + 200: + description: the signature request + content: + application/json: + schema: + $ref: "#/components/schemas/SignatureRequest" + 400: + $ref: "#/components/responses/ValidationProblem" + 401: + $ref: "#/components/responses/HttpUnauthorized" + 404: + $ref: "#/components/responses/HttpNotFound" + 500: + $ref: "#/components/responses/HttpInternalServerError" + +components: + requestBodies: + GetSignatureRequestById: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequireFiscalCodeOrVatNumber" + + responses: + HttpNotFound: + description: The specified resource was not found + content: + application/json: + schema: + type: object + properties: + title: + type: string + enum: ["Not found"] + status: + type: number + enum: [404] + required: + - title + - status + + HttpInternalServerError: + description: Internal Server Error + content: + application/json: + schema: + type: object + properties: + title: + type: string + enum: ["Internal Server Error"] + status: + type: number + enum: [500] + detail: + type: string + required: + - title + - status + + HttpUnauthorized: + description: Internal Server Error + content: + application/json: + schema: + type: object + properties: + title: + type: string + enum: ["Unauthorized"] + status: + type: number + enum: [401] + detail: + type: string + enum: + ["You must provide a valid API key to access this resource."] + required: + - title + - status + - detail + + ValidationProblem: + description: Error on request validation + content: + application/json: + schema: + type: object + properties: + type: + type: string + enum: ["/problems/validation-error"] + title: + type: string + detail: + type: string + status: + type: number + enum: [400] + violations: + type: array + items: + type: string + required: + - type + - title + - detail + - status + + schemas: + FiscalCode: + type: string + description: User's fiscal code. + format: FiscalCode + x-import: "@pagopa/ts-commons/lib/strings" + example: SPNDNL80R13C555X + + Id: + type: string + description: Entity Id + format: NonEmptyString + example: 01ARZ3NDEKTSV4RRFFQ69G5FAV + x-import: "@pagopa/ts-commons/lib/strings" + + Timestamp: + type: string + format: UTCISODateFromString + description: A date-time field in ISO-8601 format and UTC timezone. + x-import: "@pagopa/ts-commons/lib/dates" + example: "2018-10-13T00:00:00.000Z" + + RequireFiscalCodeOrVatNumber: + oneOf: + - type: object + properties: + fiscal_code: + $ref: "#/components/schemas/FiscalCode" + required: + - fiscal_code + - type: object + properties: + vat_number: + type: string + required: + - vat_number + + Clause: + type: object + properties: + title: + type: string + minLength: 5 + maxLength: 80 + type: + type: string + enum: ["REQUIRED", "UNFAIR", "OPTIONAL"] + required: + - title + - type + + ExistingSignatureFieldAttrs: + type: object + properties: + unique_name: + type: string + required: + - unique_name + + SignatureFieldToBeCreatedAttrs: + type: object + properties: + coordinates: + type: object + properties: + x: { type: number } + y: { type: number } + required: [x, y] + page: + type: number + minimum: 0 + size: + type: object + properties: + w: { type: number, minimum: 0 } + h: { type: number, minimum: 0 } + required: [w, h] + required: + - coordinates + - page + - size + + SignatureFieldAttrs: + oneOf: + - $ref: "#/components/schemas/ExistingSignatureFieldAttrs" + - $ref: "#/components/schemas/SignatureFieldToBeCreatedAttrs" + + SignatureField: + type: object + properties: + attrs: + $ref: "#/components/schemas/SignatureFieldAttrs" + clause: + $ref: "#/components/schemas/Clause" + required: + - attrs + - clause + + DocumentMetadata: + type: object + properties: + title: + type: string + minLength: 3 + maxLength: 60 + signature_fields: + type: array + items: + $ref: "#/components/schemas/SignatureField" + required: + - title + + Document: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + metadata: + $ref: "#/components/schemas/DocumentMetadata" + status: + type: string + enum: + - WAIT_FOR_UPLOAD + - WAIT_FOR_VALIDATION + - READY + - REJECTED + created_at: + $ref: "#/components/schemas/Timestamp" + url: + type: string + format: url + uploaded_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + rejected_at: + $ref: "#/components/schemas/Timestamp" + reject_reason: + type: string + required: + - id + - metadata + - created_at + - status + + Notification: + type: object + properties: + io_message_id: + $ref: "#/components/schemas/Id" + required: + - io_message_id + + SignatureRequest: + type: object + properties: + id: + $ref: "#/components/schemas/Id" + issuer_id: + $ref: "#/components/schemas/Id" + signer_id: + $ref: "#/components/schemas/Id" + dossier_id: + $ref: "#/components/schemas/Id" + status: + type: string + enum: + - DRAFT + - READY + - WAIT_FOR_SIGNATURE + - WAIT_FOR_QTSP + - SIGNED + - REJECTED + created_at: + $ref: "#/components/schemas/Timestamp" + updated_at: + $ref: "#/components/schemas/Timestamp" + expires_at: + $ref: "#/components/schemas/Timestamp" + rejected_at: + $ref: "#/components/schemas/Timestamp" + signed_at: + $ref: "#/components/schemas/Timestamp" + reject_reason: + type: string + documents: + type: array + items: + $ref: "#/components/schemas/Document" + notification: + $ref: "#/components/schemas/Notification" + required: + - id + - issuer_id + - signer_id + - dossier_id + - status + - created_at + - updated_at + - expires_at diff --git a/infra/resources/prod/api_product/backoffice/_base_policy.xml b/infra/resources/prod/api_product/backoffice/_base_policy.xml new file mode 100644 index 00000000..85cf608b --- /dev/null +++ b/infra/resources/prod/api_product/backoffice/_base_policy.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/infra/resources/prod/api_product/sign/_base_policy.xml b/infra/resources/prod/api_product/sign/_base_policy.xml new file mode 100644 index 00000000..69064bb4 --- /dev/null +++ b/infra/resources/prod/api_product/sign/_base_policy.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + https://{{io-sign-cosmosdb-name}}.documents.azure.com/dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}/docs + POST + + @{ + var verb = "post"; + var resourceType = "docs"; + var resourceLink = "dbs/{{io-sign-backoffice-database-name}}/colls/{{io-sign-backoffice-api-keys-collection-name}}"; + var key = "{{io-sign-cosmosdb-key}}"; + var keyType = "master"; + var tokenVersion = "1.0"; + var date = context.Variables.GetValueOrDefault("requestDateString"); + + var hmacSha256 = new System.Security.Cryptography.HMACSHA256 { Key = Convert.FromBase64String(key) }; + + string payLoad = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n", + verb.ToLowerInvariant(), + resourceType.ToLowerInvariant(), + resourceLink, + date.ToLowerInvariant(), + "" + ); + + byte[] hashPayLoad = hmacSha256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(payLoad)); + string signature = Convert.ToBase64String(hashPayLoad); + + return System.Uri.EscapeDataString(String.Format("type={0}&ver={1}&sig={2}", + keyType, + tokenVersion, + signature)); + } + + + application/query+json + + + True + + + True + + + @(context.Variables.GetValueOrDefault("requestDateString")) + + + 2018-12-31 + + @("{\"query\": \"SELECT w.cidrs FROM whitelist w WHERE w.id = @id\", " + + "\"parameters\": [{ \"name\": \"@id\", \"value\": \"" + context.Subscription.Id + "\"}]}") + + + ())" /> + + + + + + + + @{ + int HostToNetworkOrder(int host) { + return (((int)HostToNetworkOrderShort((short)host) & 0xFFFF) << 16) + | ((int)HostToNetworkOrderShort((short)(host >> 16)) & 0xFFFF); + } + short HostToNetworkOrderShort(short host) { + return (short)((((int)host & 0xFF) << 8) | (int)((host >> 8) & 0xFF)); + } + + bool IsPrivate(string ipAddress) + { + int[] ipParts = ipAddress.Split(new String[] { "." }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => int.Parse(s)).ToArray(); + // in private ip range + if (ipParts[0] == 127 || + ipParts[0] == 10 || + (ipParts[0] == 192 && ipParts[1] == 168) || + (ipParts[0] == 172 && (ipParts[1] >= 16 && ipParts[1] <= 31))) { + return true; + } + + // IP Address is probably public. + // This doesn't catch some VPN ranges like OpenVPN and Hamachi. + return false; + } + + /* + * If a request goes through multiple proxies, the IP addresses of each successive proxy is listed. + * This means that, given well-behaved client and proxies, the rightmost IP address is the IP address of the most recent proxy. + * For this reason we take the last IP in the list that is not a reserved IP + */ + string GetRightmostIp(string[] ipAddressList){ + for(int i = ipAddressList.Count()-1; i>=0; i--) + { + string ip = ipAddressList[i]; + string ipAddress = String.Concat(ip.Where(c => !Char.IsWhiteSpace(c))); + if(!IsPrivate(ipAddress)){ + return ipAddress; + } + } + return "127.0.0.1"; + } + + string forwardedFor = context.Request.Headers.GetValueOrDefault("x-forwarded-for",""); + if (!string.IsNullOrEmpty(forwardedFor)) { + string[] ipAddressList = forwardedFor.Split(','); + string ipAddress = GetRightmostIp(ipAddressList); + + string[] tokens = ipAddress.Split(':'); + if(tokens.Length == 2) { + ipAddress = tokens[0]; + } + + var queryResponse = context.Variables.GetValueOrDefault("issuerWhitelist"); + if (queryResponse != null && queryResponse.ContainsKey("Documents")) + { + JArray documents = (JArray) queryResponse["Documents"]; + if (documents.Count > 0){ + JObject firstDocument = (JObject) documents[0]; + if(firstDocument.ContainsKey("cidrs")){ + JArray whiteListCidrs = (JArray)firstDocument["cidrs"]; + foreach (var cidr in whiteListCidrs) { + string cidrAddress = (string)cidr; + // Avoid checking in this case because all IPs are enabled + if(cidrAddress=="0.0.0.0/0"){ + return "true"; + } + + string[] cidrParts = cidrAddress.Split('/'); + string[] inputIPParts = ipAddress.Split('.'); + string[] cidrIPArray = cidrParts[0].Split('.'); + + if (inputIPParts.Length == 4 && cidrIPArray.Length == 4) { + byte[] inputIPBytes = new byte[] {Convert.ToByte(int.Parse(inputIPParts[0])), + Convert.ToByte(int.Parse(inputIPParts[1])), + Convert.ToByte(int.Parse(inputIPParts[2])), + Convert.ToByte(int.Parse(inputIPParts[3])), }; + byte[] cidrIPBytes = new byte[] {Convert.ToByte(int.Parse(cidrIPArray[0])), + Convert.ToByte(int.Parse(cidrIPArray[1])), + Convert.ToByte(int.Parse(cidrIPArray[2])), + Convert.ToByte(int.Parse(cidrIPArray[3])), }; + + int cidrAddr = BitConverter.ToInt32(inputIPBytes,0); + int ipAddr = BitConverter.ToInt32(cidrIPBytes,0); + + var host = int.Parse(cidrParts[1]); + host = -1 << (32-host); + var mask = HostToNetworkOrder(host); + + if (((ipAddr & mask) == (cidrAddr & mask))) { + return "true"; + } + } + } + } + } + } + } + return "false"; + } + + + true + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/infra/resources/prod/api_product/support/_base_policy.xml b/infra/resources/prod/api_product/support/_base_policy.xml new file mode 100644 index 00000000..85cf608b --- /dev/null +++ b/infra/resources/prod/api_product/support/_base_policy.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/infra/resources/prod/apim_v2.tf b/infra/resources/prod/apim_v2.tf new file mode 100644 index 00000000..78917360 --- /dev/null +++ b/infra/resources/prod/apim_v2.tf @@ -0,0 +1,247 @@ +data "azurerm_api_management" "apim_v2_api" { + name = "io-p-apim-v2-api" + resource_group_name = "io-p-rg-internal" +} + +resource "azurerm_api_management_named_value" "io_fn_sign_issuer_url_v2" { + name = "io-fn-sign-issuer-url" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-issuer-url" + value = format("https://%s-sign-issuer-func.azurewebsites.net", local.product) +} + +resource "azurerm_api_management_named_value" "io_fn_sign_issuer_key_v2" { + name = "io-fn-sign-issuer-key" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-issuer-key" + value = module.key_vault_secrets.values["io-fn-sign-issuer-key"].value + secret = true +} + +resource "azurerm_api_management_named_value" "io_fn_sign_support_url_v2" { + name = "io-fn-sign-support-url" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-support-url" + value = format("https://%s-sign-support-func.azurewebsites.net", local.product) +} + +resource "azurerm_api_management_named_value" "io_fn_sign_support_key_v2" { + name = "io-fn-sign-support-key" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-support-key" + value = module.key_vault_secrets.values["io-fn-sign-support-key"].value + secret = true +} + + +resource "azurerm_api_management_named_value" "io_sign_cosmosdb_name_v2" { + name = "io-sign-cosmosdb-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-cosmosdb-name" + value = module.cosmosdb_account.name + secret = false +} + +resource "azurerm_api_management_named_value" "io_sign_cosmosdb_key_v2" { + name = "io-sign-cosmosdb-key" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-cosmosdb-key" + value = module.cosmosdb_account.primary_readonly_key + secret = true +} + +# legacy, it can be removed once the backoffice is released +resource "azurerm_api_management_named_value" "io_sign_cosmosdb_issuer_container_name_v2" { + name = "io-sign-cosmosdb-issuer-container-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-cosmosdb-issuer-container-name" + value = module.cosmosdb_sql_database_issuer.name + secret = false +} +resource "azurerm_api_management_named_value" "io_sign_cosmosdb_issuer_whitelist_collection_name_new_v2" { + name = "io-sign-cosmosdb-issuer-whitelist-collection-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-cosmosdb-issuer-whitelist-collection-name" + value = module.cosmosdb_sql_container_issuer-issuers-whitelist.name + secret = false +} +resource "azurerm_api_management_named_value" "io_sign_cosmosdb_issuer_issuers_collection_name_v2" { + name = "io-sign-cosmosdb-issuer-issuers-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-cosmosdb-issuer-issuers-name" + value = module.cosmosdb_sql_container_issuer-issuers.name + secret = false +} +# end legacy + +resource "azurerm_api_management_named_value" "backoffice-database-name" { + name = "io-sign-backoffice-database-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-backoffice-database-name" + value = module.cosmosdb_sql_database_backoffice.name + secret = false +} + +resource "azurerm_api_management_named_value" "backoffice-api-keys-collection-name" { + name = "io-sign-backoffice-api-keys-collection-name" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-sign-backoffice-api-keys-collection-name" + value = module.cosmosdb_sql_container_backoffice-api-keys.name + secret = false +} + +module "apim_v2_io_sign_product" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_product?ref=v8.35.0" + + product_id = "io-sign-api" + display_name = "IO SIGN API" + description = "Product for IO sign" + + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + + published = true + subscription_required = true + approval_required = false + + policy_xml = file("./api_product/sign/_base_policy.xml") +} + +resource "azurerm_api_management_api_operation_policy" "get_signer_by_fiscal_code_policy_v2" { + api_name = module.apim_v2_io_sign_issuer_api_v1.name + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + operation_id = "getSignerByFiscalCode" + + xml_content = file("./api/issuer/v1/get_signer_by_fiscal_code_policy/policy.xml") +} + +module "apim_v2_io_sign_issuer_api_v1" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_api?ref=v8.35.0" + + name = format("%s-sign-issuer-api", local.product) + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + product_ids = [module.apim_v2_io_sign_product.product_id] + subscription_required = true + service_url = null + + description = "IO Sign - Issuer API" + display_name = "IO Sign - Issuer API" + path = "api/v1/sign" + protocols = ["https"] + + content_format = "openapi" + + content_value = file("./api/issuer/v1/openapi.yaml") + + xml_content = file("./api/issuer/v1/base_policy.xml") +} + +module "apim_v2_io_sign_support_product" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_product?ref=v8.35.0" + + product_id = "io-sign-support-api" + display_name = "IO SIGN SUPPORT Product" + description = "Support Product for IO SIGN" + + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + + published = true + subscription_required = true + approval_required = false + + policy_xml = file("./api_product/support/_base_policy.xml") +} + +module "apim_v2_io_sign_support_api_v1" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_api?ref=v8.35.0" + + name = format("%s-sign-support-api", local.product) + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + product_ids = [module.apim_v2_io_sign_support_product.product_id] + subscription_required = true + service_url = null + + description = "IO Sign - Support API" + display_name = "IO Sign - Support API" + path = "api/v1/sign/support" + protocols = ["https"] + + content_format = "openapi" + + content_value = file("./api/support/v1/openapi.yaml") + + xml_content = file("./api/support/v1/base_policy.xml") +} + +# BACK OFFICE + +resource "azurerm_api_management_named_value" "io_fn_sign_backoffice_url_v2" { + name = "io-fn-sign-backoffice-url" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-backoffice-url" + value = format("https://%s-sign-backoffice-func.azurewebsites.net", local.product) +} + +resource "azurerm_api_management_named_value" "io_fn_sign_backoffice_key_v2" { + name = "io-fn-sign-backoffice-key" + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + display_name = "io-fn-sign-backoffice-key" + value = module.key_vault_secrets.values["io-sign-backoffice-func-key"].value + secret = true +} + +module "apim_v2_io_sign_backoffice_product" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_product?ref=v8.35.0" + + product_id = format("%s-sign-backoffice-apim-product", local.product) + display_name = "IO SIGN BACKOFFICE" + description = "Api Management product for io-sign-backoffice REST APIs" + + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + + published = true + subscription_required = true + approval_required = false + + policy_xml = file("./api_product/backoffice/_base_policy.xml") +} + +module "apim_v2_io_sign_backoffice_api_v1" { + source = "github.com/pagopa/terraform-azurerm-v3//api_management_api?ref=v8.35.0" + + name = format("%s-sign-backoffice-apim-api", local.product) + api_management_name = data.azurerm_api_management.apim_v2_api.name + resource_group_name = data.azurerm_api_management.apim_v2_api.resource_group_name + product_ids = [module.apim_v2_io_sign_backoffice_product.product_id] + subscription_required = true + service_url = null + + display_name = "IO SIGN BACKOFFICE API" + description = "io-sign-backoffice REST APIs" + + path = "api/v1/sign/backoffice" + protocols = ["https"] + + content_format = "openapi" + content_value = file("../../../apps/io-sign-backoffice-func/openapi.yaml") + + xml_content = file("./api/backoffice/v1/base_policy.xml") +} diff --git a/infra/resources/prod/azuread.tf b/infra/resources/prod/azuread.tf new file mode 100644 index 00000000..099ca18d --- /dev/null +++ b/infra/resources/prod/azuread.tf @@ -0,0 +1,22 @@ +# Azure AD +data "azuread_group" "adgroup_admin" { + display_name = format("%s-adgroup-admin", local.product) +} + +data "azuread_group" "adgroup_developers" { + display_name = format("%s-adgroup-developers", local.product) +} + +# tflint-ignore: terraform_unused_declarations +data "azuread_group" "adgroup_externals" { + display_name = format("%s-adgroup-externals", local.product) +} + +# tflint-ignore: terraform_unused_declarations +data "azuread_group" "adgroup_security" { + display_name = format("%s-adgroup-security", local.product) +} + +data "azuread_group" "adgroup_sign" { + display_name = format("%s-adgroup-sign", local.product) +} diff --git a/infra/resources/prod/cosmos.tf b/infra/resources/prod/cosmos.tf new file mode 100644 index 00000000..623db840 --- /dev/null +++ b/infra/resources/prod/cosmos.tf @@ -0,0 +1,33 @@ +module "cosmosdb_account" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_account?ref=v8.35.0" + name = format("%s-cosmos", local.project) + domain = var.domain + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + kind = "GlobalDocumentDB" + main_geo_location_location = azurerm_resource_group.data_rg.location + main_geo_location_zone_redundant = var.cosmos.zone_redundant + + additional_geo_locations = var.cosmos.additional_geo_locations + + # Having multiple region requires + # maximum lag must be between 5 minutes and 1 day + # and between 100000 operations and 1000000 operations + consistency_policy = { + consistency_level = "BoundedStaleness" + max_interval_in_seconds = 300 + max_staleness_prefix = 100000 + } + + public_network_access_enabled = false + + is_virtual_network_filter_enabled = true + + private_endpoint_enabled = true + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + private_dns_zone_sql_ids = [data.azurerm_private_dns_zone.privatelink_documents_azure_com.id] + private_endpoint_sql_name = "${local.project}-cosmos" + private_service_connection_sql_name = "${local.project}-cosmos-private-endpoint" + + tags = var.tags +} diff --git a/infra/resources/prod/cosmos_backoffice.tf b/infra/resources/prod/cosmos_backoffice.tf new file mode 100644 index 00000000..c8e742d4 --- /dev/null +++ b/infra/resources/prod/cosmos_backoffice.tf @@ -0,0 +1,68 @@ +module "cosmosdb_sql_database_backoffice" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_database?ref=v8.35.0" + name = "backoffice" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name +} + +module "cosmosdb_sql_container_backoffice-api-keys" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "api-keys" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_backoffice.name + partition_key_path = "/institutionId" + + autoscale_settings = { + max_throughput = var.io_sign_database_backoffice.api_keys.max_throughput + } + + default_ttl = var.io_sign_database_backoffice.api_keys.ttl +} + +module "cosmosdb_sql_container_backoffice-api-keys-by-id" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "api-keys-by-id" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_backoffice.name + partition_key_path = "/id" + + autoscale_settings = { + max_throughput = var.io_sign_database_backoffice.api_keys_by_id.max_throughput + } + + default_ttl = var.io_sign_database_backoffice.api_keys_by_id.ttl +} + +module "cosmosdb_sql_container_backoffice-issuers" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "issuers" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_backoffice.name + partition_key_path = "/institutionId" + + autoscale_settings = { + max_throughput = var.io_sign_database_backoffice.issuers.max_throughput + } + + default_ttl = var.io_sign_database_backoffice.issuers.ttl +} + +module "cosmosdb_sql_container_backoffice-consents" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "consents" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_backoffice.name + partition_key_path = "/institutionId" + + autoscale_settings = { + max_throughput = var.io_sign_database_backoffice.consents.max_throughput + } + + default_ttl = var.io_sign_database_backoffice.consents.ttl +} + + diff --git a/infra/resources/prod/cosmos_issuer.tf b/infra/resources/prod/cosmos_issuer.tf new file mode 100644 index 00000000..218e6ffe --- /dev/null +++ b/infra/resources/prod/cosmos_issuer.tf @@ -0,0 +1,111 @@ +module "cosmosdb_sql_database_issuer" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_database?ref=v8.35.0" + name = "issuer" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name +} + +module "cosmosdb_sql_container_issuer-dossiers" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "dossiers" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/issuerId" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.dossiers.max_throughput + } + + default_ttl = var.io_sign_database_issuer.dossiers.ttl +} + +module "cosmosdb_sql_container_issuer-signature-requests" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "signature-requests" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/issuerId" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.signature_requests.max_throughput + } + + default_ttl = var.io_sign_database_issuer.signature_requests.ttl +} + +module "cosmosdb_sql_container_issuer-uploads" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "uploads" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/id" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.uploads.max_throughput + } + + default_ttl = var.io_sign_database_issuer.uploads.ttl +} + +module "cosmosdb_sql_container_issuer-issuers" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "issuers" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/subscriptionId" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.uploads.max_throughput + } + + default_ttl = var.io_sign_database_issuer.issuers.ttl +} + +module "cosmosdb_sql_container_issuer-issuers-by-vat-number" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "issuers-by-vat-number" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/id" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.uploads.max_throughput + } + + default_ttl = var.io_sign_database_issuer.issuers.ttl +} + +module "cosmosdb_sql_container_issuer-issuers-by-subscription-id" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "issuers-by-subscription-id" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/id" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.uploads.max_throughput + } + + default_ttl = var.io_sign_database_issuer.issuers.ttl +} + +module "cosmosdb_sql_container_issuer-issuers-whitelist" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "issuers-whitelist" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_issuer.name + partition_key_path = "/id" + + autoscale_settings = { + max_throughput = var.io_sign_database_issuer.uploads.max_throughput + } + + default_ttl = var.io_sign_database_issuer.issuers.ttl +} diff --git a/infra/resources/prod/cosmos_user.tf b/infra/resources/prod/cosmos_user.tf new file mode 100644 index 00000000..865974a2 --- /dev/null +++ b/infra/resources/prod/cosmos_user.tf @@ -0,0 +1,36 @@ +module "cosmosdb_sql_database_user" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_database?ref=v8.35.0" + name = "user" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name +} + +module "cosmosdb_sql_container_user-signature-requests" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "signature-requests" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_user.name + partition_key_path = "/signerId" + + autoscale_settings = { + max_throughput = var.io_sign_database_user.signature_requests.max_throughput + } + + default_ttl = var.io_sign_database_user.signature_requests.ttl +} + +module "cosmosdb_sql_container_user-signatures" { + source = "github.com/pagopa/terraform-azurerm-v3//cosmosdb_sql_container?ref=v8.35.0" + name = "signatures" + resource_group_name = azurerm_resource_group.data_rg.name + account_name = module.cosmosdb_account.name + database_name = module.cosmosdb_sql_database_user.name + partition_key_path = "/signerId" + + autoscale_settings = { + max_throughput = var.io_sign_database_user.signatures.max_throughput + } + + default_ttl = var.io_sign_database_user.signatures.ttl +} diff --git a/infra/resources/prod/dns.tf b/infra/resources/prod/dns.tf new file mode 100644 index 00000000..7ad38363 --- /dev/null +++ b/infra/resources/prod/dns.tf @@ -0,0 +1,68 @@ +resource "azurerm_dns_zone" "firma_io_pagopa_it" { + count = var.env_short == "p" ? 1 : 0 + + name = var.dns_zone_names.website + resource_group_name = azurerm_resource_group.integration_rg.name + + tags = var.tags +} + +resource "azurerm_dns_mx_record" "ses_mx_firma_io_pagopa_it" { + name = "@" + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec + + record { + preference = 10 + exchange = "inbound-smtp.eu-west-1.amazonaws.com." + } +} + +resource "azurerm_dns_cname_record" "ses_validation_firma_io_pagopa_it" { + for_each = { for v in var.dns_ses_validation : v.name => v } + + name = each.value.name + record = each.value.record + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec +} + +resource "azurerm_dns_txt_record" "spf1_mailup_firma_io_pagopa_it" { + name = "@" + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec + + record { + value = "v=spf1 include:musvc.com -all" + } +} + +resource "azurerm_dns_cname_record" "dkim1_mailup_firma_io_pagopa_it" { + name = "ml01._domainkey" + record = "ml01.dkim.musvc.com." + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec +} + +resource "azurerm_dns_cname_record" "dkim2_mailup_firma_io_pagopa_it" { + name = "ml02._domainkey" + record = "ml02.dkim.musvc.com." + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec +} + +resource "azurerm_dns_txt_record" "dmarc_mailup_firma_io_pagopa_it" { + name = "_dmarc" + zone_name = azurerm_dns_zone.firma_io_pagopa_it[0].name + resource_group_name = azurerm_resource_group.integration_rg.name + ttl = var.dns_default_ttl_sec + + record { + value = "v=DMARC1; p=reject; pct=100; adkim=s; aspf=s" + } +} \ No newline at end of file diff --git a/infra/resources/prod/integration.tf b/infra/resources/prod/integration.tf new file mode 100644 index 00000000..f29c9def --- /dev/null +++ b/infra/resources/prod/integration.tf @@ -0,0 +1,125 @@ +# Needed to integrate Firma con IO with external domains, products or platforms (ie. eventhub for billing, ...) +module "event_hub" { + source = "github.com/pagopa/terraform-azurerm-v3//eventhub?ref=v8.35.0" + name = format("%s-eventhub-ns", local.project) + location = azurerm_resource_group.integration_rg.location + resource_group_name = azurerm_resource_group.integration_rg.name + auto_inflate_enabled = var.integration_hub.auto_inflate_enabled + sku = var.integration_hub.sku_name + capacity = var.integration_hub.capacity + maximum_throughput_units = var.integration_hub.maximum_throughput_units + zone_redundant = var.integration_hub.zone_redundant + + virtual_network_ids = [data.azurerm_virtual_network.vnet_common.id] + + private_dns_zone_record_A_name = null + + eventhubs = var.integration_hub.hubs + + # Configuration in accordance to the inttegration defined at + # https://pagopa.atlassian.net/wiki/search?text=firma%20con%20io%20fatturazione + public_network_access_enabled = true + + network_rulesets = [ + { + default_action = "Deny", + virtual_network_rule = [ + { + subnet_id = module.io_sign_eventhub_snet.id, + ignore_missing_virtual_network_service_endpoint = false + } + ], + ip_rule = var.integration_hub.ip_rules + trusted_service_access_enabled = false + } + ] + + private_endpoint_created = true + private_dns_zones = { + id = [data.azurerm_private_dns_zone.privatelink_servicebus_windows_net.id] + name = [data.azurerm_private_dns_zone.privatelink_servicebus_windows_net.name] + resource_group_name = data.azurerm_private_dns_zone.privatelink_servicebus_windows_net.resource_group_name + } + private_endpoint_resource_group_name = azurerm_resource_group.integration_rg.name + private_endpoint_subnet_id = module.io_sign_eventhub_snet.id + + alerts_enabled = var.integration_hub.alerts_enabled + metric_alerts = { + no_trx = { + aggregation = "Total" + metric_name = "IncomingMessages" + description = "No transactions received from acquirer in the last 24h" + operator = "LessThanOrEqual" + threshold = 1000 + frequency = "PT1H" + window_size = "P1D" + dimension = [ + { + name = "EntityName" + operator = "Include" + values = ["rtd-trx"] + } + ], + }, + active_connections = { + aggregation = "Average" + metric_name = "ActiveConnections" + description = null + operator = "LessThanOrEqual" + threshold = 0 + frequency = "PT5M" + window_size = "PT15M" + dimension = [], + }, + error_trx = { + aggregation = "Total" + metric_name = "IncomingMessages" + description = "Transactions rejected from one acquirer file received. trx write on eventhub. check immediately" + operator = "GreaterThan" + threshold = 0 + frequency = "PT5M" + window_size = "PT30M" + dimension = [ + { + name = "EntityName" + operator = "Include" + values = [ + "bpd-trx-error", + "rtd-trx-error" + ] + } + ], + }, + } + + action = [ + { + action_group_id = data.azurerm_monitor_action_group.error_action_group.id + webhook_properties = null + } + ] + + tags = var.tags +} + +#tfsec:ignore:AZU023 +resource "azurerm_key_vault_secret" "integration_event_hub_secrets" { + for_each = module.event_hub.key_ids + + name = format("%s-integration-evthub-key", replace(each.key, ".", "-")) + value = module.event_hub.keys[each.key].primary_key + content_type = "text/plain" + + key_vault_id = module.key_vault.id +} + +#tfsec:ignore:AZU023 +resource "azurerm_key_vault_secret" "integration_event_hub_jaas_connection_string" { + for_each = module.event_hub.key_ids + + name = format("%s-integration-evthub-conn-string", replace(each.key, ".", "-")) + value = "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"$ConnectionString\" password=\"${module.event_hub.keys[each.key].primary_connection_string}\";" + content_type = "text/plain" + + key_vault_id = module.key_vault.id +} diff --git a/infra/resources/prod/io_sign_backoffice_app.tf b/infra/resources/prod/io_sign_backoffice_app.tf new file mode 100644 index 00000000..8a6d97c0 --- /dev/null +++ b/infra/resources/prod/io_sign_backoffice_app.tf @@ -0,0 +1,192 @@ +locals { + backoffice_app_settings = merge({ + AZURE_SUBSCRIPTION_ID = data.azurerm_subscription.current.subscription_id + COSMOS_DB_CONNECTION_STRING = module.cosmosdb_account.connection_strings[0], + COSMOS_DB_NAME = module.cosmosdb_sql_database_backoffice.name + APIM_RESOURCE_GROUP_NAME = data.azurerm_api_management.apim_v2_api.resource_group_name, + APIM_SERVICE_NAME = data.azurerm_api_management.apim_v2_api.name, + APIM_PRODUCT_NAME = module.apim_v2_io_sign_product.product_id + APPINSIGHTS_INSTRUMENTATIONKEY = sensitive(data.azurerm_application_insights.application_insights.instrumentation_key) + }, + { + for s in var.io_sign_backoffice_app.app_settings : + s.name => s.key_vault_secret_name != null ? "@Microsoft.KeyVault(VaultName=${module.key_vault.name};SecretName=${s.key_vault_secret_name})" : s.value + }) +} + +module "io_sign_backoffice_snet" { + source = "github.com/pagopa/terraform-azurerm-v3//subnet?ref=v8.35.0" + name = format("%s-backoffice-snet", local.project) + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + virtual_network_name = data.azurerm_virtual_network.vnet_common.name + address_prefixes = var.subnets_cidrs.backoffice + + private_endpoint_network_policies_enabled = false + + service_endpoints = [ + "Microsoft.Web", + "Microsoft.AzureCosmosDB", + ] + + delegation = { + name = "default" + service_delegation = { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +data "azurerm_subnet" "appgateway_snet" { + name = var.io_common.appgateway_snet_name + virtual_network_name = var.io_common.vnet_common_name + resource_group_name = var.io_common.resource_group_name +} + +module "io_sign_backoffice_app" { + source = "github.com/pagopa/terraform-azurerm-v3//app_service?ref=v8.35.0" + + name = format("%s-backoffice-app", local.project) + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + plan_name = format("%s-backoffice-plan", local.project) + sku_name = var.io_sign_backoffice_app.sku_name + + node_version = "18-lts" + health_check_path = "/health" + app_command_line = "node server.js" + + app_settings = local.backoffice_app_settings + + always_on = true + vnet_integration = true + + subnet_id = module.io_sign_backoffice_snet.id + + allowed_subnets = [ + data.azurerm_subnet.appgateway_snet.id, + data.azurerm_subnet.apim_v2.id + ] + + ip_restriction_default_action = "Deny" + + tags = var.tags +} + +resource "azurerm_key_vault_access_policy" "backoffice_key_vault_access_policy" { + key_vault_id = module.key_vault.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = module.io_sign_backoffice_app.principal_id + + secret_permissions = ["Get"] + storage_permissions = [] + certificate_permissions = [] +} + +resource "azurerm_role_assignment" "firmaconio_selfcare_apim_contributor_role" { + scope = data.azurerm_api_management.apim_v2_api.id + role_definition_name = "API Management Service Contributor" + principal_id = module.io_sign_backoffice_app.principal_id +} + +resource "azurerm_role_assignment" "backoffice_app_api_keys_queue_sender_role" { + scope = azurerm_storage_queue.api_keys.resource_manager_id + role_definition_name = "Storage Queue Data Message Sender" + principal_id = module.io_sign_backoffice_app.principal_id +} + +resource "azurerm_private_endpoint" "io_sign_backoffice_app" { + name = format("%s-backoffice-endpoint", local.project) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-backoffice-endpoint", local.project) + private_connection_resource_id = module.io_sign_backoffice_app.id + is_manual_connection = false + subresource_names = ["sites"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +module "io_sign_backoffice_app_staging_slot" { + source = "github.com/pagopa/terraform-azurerm-v3//app_service_slot?ref=v8.35.0" + + name = "staging" + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + app_service_id = module.io_sign_backoffice_app.id + app_service_name = module.io_sign_backoffice_app.name + + node_version = "18-lts" + health_check_path = "/health" + app_command_line = "node server.js" + + app_settings = local.backoffice_app_settings + + always_on = true + vnet_integration = true + + subnet_id = module.io_sign_backoffice_snet.id + + allowed_subnets = [ + data.azurerm_subnet.appgateway_snet.id, + data.azurerm_subnet.apim_v2.id + ] + + ip_restriction_default_action = "Deny" + + tags = var.tags +} + +resource "azurerm_key_vault_access_policy" "backoffice_staging_key_vault_access_policy" { + key_vault_id = module.key_vault.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = module.io_sign_backoffice_app_staging_slot.principal_id + + secret_permissions = ["Get"] + storage_permissions = [] + certificate_permissions = [] +} + +resource "azurerm_role_assignment" "firmaconio_selfcare_staging_apim_contributor_role" { + scope = data.azurerm_api_management.apim_v2_api.id + role_definition_name = "API Management Service Contributor" + principal_id = module.io_sign_backoffice_app_staging_slot.principal_id +} + +resource "azurerm_role_assignment" "backoffice_app_staging_api_keys_queue_sender_role" { + scope = azurerm_storage_queue.api_keys.resource_manager_id + role_definition_name = "Storage Queue Data Message Sender" + principal_id = module.io_sign_backoffice_app_staging_slot.principal_id +} + +resource "azurerm_private_endpoint" "io_sign_backoffice_app_staging_slot" { + name = format("%s-backoffice-staging-endpoint", local.project) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-backoffice-staging-endpoint", local.project) + private_connection_resource_id = module.io_sign_backoffice_app.id + is_manual_connection = false + subresource_names = ["sites-staging"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} diff --git a/infra/resources/prod/io_sign_backoffice_func.tf b/infra/resources/prod/io_sign_backoffice_func.tf new file mode 100644 index 00000000..ef70de8d --- /dev/null +++ b/infra/resources/prod/io_sign_backoffice_func.tf @@ -0,0 +1,117 @@ +locals { + backoffice_func_settings = merge({ + COSMOS_DB_CONNECTION_STRING = module.cosmosdb_account.connection_strings[0], + COSMOS_DB_NAME = module.cosmosdb_sql_database_backoffice.name + }, { + for s in var.io_sign_backoffice_func.app_settings : + s.name => s.key_vault_secret_name != null ? "@Microsoft.KeyVault(VaultName=${module.key_vault.name};SecretName=${s.key_vault_secret_name})" : s.value + }) + io_sign_backoffice_func = { + staging_disabled = ["onSelfcareContractsMessage"] + } +} + +module "io_sign_backoffice_func" { + source = "github.com/pagopa/terraform-azurerm-v3//function_app?ref=v8.35.0" + + name = format("%s-backoffice-func", local.project) + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + health_check_path = "/health" + health_check_maxpingfailures = 2 + + node_version = "18" + runtime_version = "~4" + always_on = true + + app_settings = local.backoffice_func_settings + + subnet_id = module.io_sign_backoffice_snet.id + + sticky_app_setting_names = [ + for to_disable in local.io_sign_backoffice_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) + ] + + allowed_subnets = [ + module.io_sign_snet.id + ] + + app_service_plan_id = module.io_sign_backoffice_app.plan_id + + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + system_identity_enabled = true + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_backoffice_func" { + name = format("%s-backoffice-func-endpoint", local.project) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-backoffice-endpoint", local.project) + private_connection_resource_id = module.io_sign_backoffice_func.id + is_manual_connection = false + subresource_names = ["sites"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_key_vault_access_policy" "backoffice_func_key_vault_access_policy" { + key_vault_id = module.key_vault.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = module.io_sign_backoffice_func.system_identity_principal + + secret_permissions = ["Get"] + storage_permissions = [] + certificate_permissions = [] +} + +module "io_sign_backoffice_func_staging_slot" { + source = "github.com/pagopa/terraform-azurerm-v3//function_app_slot?ref=v8.35.0" + + name = "staging" + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + function_app_id = module.io_sign_backoffice_func.id + app_service_plan_id = module.io_sign_backoffice_func.app_service_plan_id + + health_check_path = "/health" + health_check_maxpingfailures = 2 + + storage_account_name = module.io_sign_backoffice_func.storage_account.name + storage_account_access_key = module.io_sign_backoffice_func.storage_account.primary_access_key + + node_version = "18" + runtime_version = "~4" + always_on = true + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + + app_settings = merge( + local.backoffice_func_settings, + { + # Disabled functions on slot triggered by queue and timer + for to_disable in local.io_sign_backoffice_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) => "true" + } + ) + + subnet_id = module.io_sign_backoffice_snet.id + + allowed_subnets = [ + module.io_sign_snet.id + ] + + tags = var.tags +} diff --git a/infra/resources/prod/io_sign_issuer_func.tf b/infra/resources/prod/io_sign_issuer_func.tf new file mode 100644 index 00000000..67665544 --- /dev/null +++ b/infra/resources/prod/io_sign_issuer_func.tf @@ -0,0 +1,240 @@ +locals { + io_sign_issuer_func = { + staging_disabled = [ + "MarkAsRejected", + "MarkAsSigned", + "MarkAsWaitForSignature", + "ValidateUpload", + "CreateIssuer", + "CreateIssuerByVatNumberView" + ] + disabled = [ + "CreateIssuer" + ] + app_settings = { + FUNCTIONS_WORKER_PROCESS_COUNT = 4 + AzureWebJobsDisableHomepage = "true" + NODE_ENV = "production" + CosmosDbConnectionString = module.cosmosdb_account.connection_strings[0] + CosmosDbDatabaseName = module.cosmosdb_sql_database_issuer.name + StorageAccountConnectionString = module.io_sign_storage.primary_connection_string + IssuerUploadedBlobContainerName = azurerm_storage_container.uploaded_documents.name + IssuerValidatedBlobContainerName = azurerm_storage_container.validated_documents.name + IoServicesApiBasePath = "https://api.io.pagopa.it" + IoServicesSubscriptionKey = module.key_vault_secrets.values["IoServicesSubscriptionKey"].value + IoServicesConfigurationId = module.key_vault_secrets.values["io-services-configuration-id"].value + PdvTokenizerApiBasePath = "https://api.tokenizer.pdv.pagopa.it" + PdvTokenizerApiKey = module.key_vault_secrets.values["PdvTokenizerApiKey"].value + AnalyticsEventHubConnectionString = module.event_hub.keys["analytics.io-sign-func-issuer"].primary_connection_string + BillingEventHubConnectionString = module.event_hub.keys["billing.io-sign-func-issuer"].primary_connection_string + SelfCareEventHubConnectionString = module.key_vault_secrets.values["SelfCareEventHubConnectionString"].value + SelfCareApiBasePath = "https://api.selfcare.pagopa.it" + SelfCareApiKey = module.key_vault_secrets.values["SelfCareApiKey"].value + SlackWebhookUrl = module.key_vault_secrets.values["SlackWebhookUrl"].value + BackOfficeApiBasePath = "https://api.io.pagopa.it/api/v1/sign/backoffice" + BackOfficeApiKey = module.key_vault_secrets.values["BackOfficeApiKey"].value + } + } +} + +module "io_sign_issuer_func" { + source = "github.com/pagopa/terraform-azurerm-v3//function_app?ref=v8.35.0" + + name = format("%s-issuer-func", local.project) + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + health_check_path = "/api/v1/sign/info" + health_check_maxpingfailures = 2 + + node_version = "18" + runtime_version = "~4" + always_on = true + + app_service_plan_info = { + kind = "Linux" + sku_size = var.io_sign_issuer_func.sku_size + maximum_elastic_worker_count = 0 + worker_count = 1 + zone_balancing_enabled = false + } + + app_settings = merge( + local.io_sign_issuer_func.app_settings, + { + # Enable functions on production triggered by queue and timer + for to_disable in local.io_sign_issuer_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) => contains(local.io_sign_issuer_func.disabled, to_disable) ? "true" : "false" + } + ) + + sticky_app_setting_names = [ + # Sticky the settings enabling triggered by queue and timer + for to_disable in local.io_sign_issuer_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) + ] + + subnet_id = module.io_sign_snet.id + allowed_subnets = [ + module.io_sign_snet.id, + data.azurerm_subnet.apim_v2.id, + ] + + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + system_identity_enabled = true + + tags = var.tags +} + +resource "azurerm_role_assignment" "issuer_func_api_keys_queue_processor_role" { + scope = azurerm_storage_queue.api_keys.resource_manager_id + role_definition_name = "Storage Queue Data Message Processor" + principal_id = module.io_sign_issuer_func.system_identity_principal +} + +module "io_sign_issuer_func_staging_slot" { + count = var.io_sign_issuer_func.sku_tier == "PremiumV3" ? 1 : 0 + source = "github.com/pagopa/terraform-azurerm-v3//function_app_slot?ref=v8.35.0" + + name = "staging" + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + function_app_id = module.io_sign_issuer_func.id + app_service_plan_id = module.io_sign_issuer_func.app_service_plan_id + + health_check_path = "/api/v1/sign/info" + health_check_maxpingfailures = 2 + + storage_account_name = module.io_sign_issuer_func.storage_account.name + storage_account_access_key = module.io_sign_issuer_func.storage_account.primary_access_key + + node_version = "18" + runtime_version = "~4" + always_on = true + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + + app_settings = merge( + local.io_sign_issuer_func.app_settings, + { + # Disabled functions on slot triggered by queue and timer + for to_disable in local.io_sign_issuer_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) => "true" + } + ) + + subnet_id = module.io_sign_snet.id + allowed_subnets = [ + module.io_sign_snet.id, + data.azurerm_subnet.apim_v2.id, + ] + + tags = var.tags +} + +resource "azurerm_monitor_autoscale_setting" "io_sign_issuer_func" { + count = var.io_sign_issuer_func.sku_tier == "PremiumV3" ? 1 : 0 + name = format("%s-autoscale", module.io_sign_issuer_func.name) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + target_resource_id = module.io_sign_issuer_func.app_service_plan_id + + profile { + name = "default" + + capacity { + default = var.io_sign_issuer_func.autoscale_default + minimum = var.io_sign_issuer_func.autoscale_minimum + maximum = var.io_sign_issuer_func.autoscale_maximum + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_issuer_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 3500 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_issuer_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 60 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_issuer_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 2500 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_issuer_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 30 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + } + + tags = var.tags +} diff --git a/infra/resources/prod/io_sign_support_func.tf b/infra/resources/prod/io_sign_support_func.tf new file mode 100644 index 00000000..01e8dbaa --- /dev/null +++ b/infra/resources/prod/io_sign_support_func.tf @@ -0,0 +1,192 @@ +locals { + io_sign_support_func = { + app_settings = { + FUNCTIONS_WORKER_PROCESS_COUNT = 4 + AzureWebJobsDisableHomepage = "true" + NODE_ENV = "production" + CosmosDbConnectionString = module.cosmosdb_account.connection_strings[0] + CosmosDbIssuerDatabaseName = module.cosmosdb_sql_database_issuer.name + CosmosDbUserDatabaseName = module.cosmosdb_sql_database_user.name + PdvTokenizerApiBasePath = "https://api.tokenizer.pdv.pagopa.it" + PdvTokenizerApiKey = module.key_vault_secrets.values["PdvTokenizerApiKey"].value + } + } +} + +module "io_sign_support_func" { + source = "github.com/pagopa/terraform-azurerm-v3//function_app?ref=v8.35.0" + + name = format("%s-support-func", local.project) + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + # TODO Activate when in production + # health_check_path = "/api/v1/sign/support/info" + + always_on = true + + runtime_version = "~4" + node_version = "18" + + app_service_plan_info = { + kind = "Linux" + sku_tier = var.io_sign_support_func.sku_tier + sku_size = var.io_sign_support_func.sku_size + maximum_elastic_worker_count = 0 + worker_count = 1 + zone_balancing_enabled = false + } + + app_settings = local.io_sign_support_func.app_settings + + subnet_id = module.io_sign_support_snet.id + allowed_subnets = [ + module.io_sign_support_snet.id, + data.azurerm_subnet.apim_v2.id, + ] + + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + system_identity_enabled = true + + tags = var.tags +} + +module "io_sign_support_func_staging_slot" { + count = var.io_sign_support_func.sku_tier == "PremiumV3" ? 1 : 0 + source = "github.com/pagopa/terraform-azurerm-v3//function_app_slot?ref=v8.35.0" + + name = "staging" + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + function_app_id = module.io_sign_support_func.id + app_service_plan_id = module.io_sign_support_func.app_service_plan_id + + # TODO Activate when in production + # health_check_path = "/api/v1/sign/support/info" + + storage_account_name = module.io_sign_support_func.storage_account.name + storage_account_access_key = module.io_sign_support_func.storage_account.primary_access_key + + runtime_version = "~4" + always_on = true + node_version = "18" + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + + app_settings = local.io_sign_support_func.app_settings + + subnet_id = module.io_sign_support_snet.id + allowed_subnets = [ + module.io_sign_support_snet.id, + data.azurerm_subnet.apim_v2.id, + ] + + tags = var.tags +} + +resource "azurerm_monitor_autoscale_setting" "io_sign_support_func" { + count = var.io_sign_support_func.sku_tier == "PremiumV3" ? 1 : 0 + name = format("%s-autoscale", module.io_sign_support_func.name) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + target_resource_id = module.io_sign_support_func.app_service_plan_id + + profile { + name = "default" + + capacity { + default = var.io_sign_support_func.autoscale_default + minimum = var.io_sign_support_func.autoscale_minimum + maximum = var.io_sign_support_func.autoscale_maximum + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_support_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 3500 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_support_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 60 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_support_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 2500 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_support_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 30 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + } + + tags = var.tags +} diff --git a/infra/resources/prod/io_sign_user_func.tf b/infra/resources/prod/io_sign_user_func.tf new file mode 100644 index 00000000..ba2166fc --- /dev/null +++ b/infra/resources/prod/io_sign_user_func.tf @@ -0,0 +1,234 @@ +locals { + io_sign_user_func = { + staging_disabled = [ + "CreateSignatureRequest", + "FillDocument", + "UpdateSignatureRequest", + "ValidateQtspSignature", + ] + app_settings = { + FUNCTIONS_WORKER_PROCESS_COUNT = 4 + AzureWebJobsDisableHomepage = "true" + NODE_ENV = "production" + NODE_TLS_REJECT_UNAUTHORIZED = 0 + CosmosDbConnectionString = module.cosmosdb_account.connection_strings[0] + CosmosDbDatabaseName = module.cosmosdb_sql_database_user.name + StorageAccountConnectionString = module.io_sign_storage.primary_connection_string + userUploadedBlobContainerName = azurerm_storage_container.uploaded_documents.name + userValidatedBlobContainerName = azurerm_storage_container.validated_documents.name + IoServicesApiBasePath = "https://api.io.pagopa.it" + IoServicesSubscriptionKey = module.key_vault_secrets.values["IoServicesSubscriptionKey"].value + IoServicesConfigurationId = module.key_vault_secrets.values["io-services-configuration-id"].value + PdvTokenizerApiBasePath = "https://api.tokenizer.pdv.pagopa.it" + PdvTokenizerApiKey = module.key_vault_secrets.values["PdvTokenizerApiKey"].value + NamirialApiBasePath = "https://pagopa.namirial.com" + NamirialUsername = "api" + NamirialPassword = module.key_vault_secrets.values["NamirialPassword"].value + NamirialTestApiBasePath = "https://pagopa-test.namirial.com" + NamirialTestUsername = "api" + NamirialTestPassword = module.key_vault_secrets.values["NamirialTestPassword"].value + AnalyticsEventHubConnectionString = module.event_hub.keys["analytics.io-sign-func-user"].primary_connection_string + BillingEventHubConnectionString = module.event_hub.keys["billing.io-sign-func-issuer"].primary_connection_string + SelfCareEventHubConnectionString = module.key_vault_secrets.values["SelfCareEventHubConnectionString"].value + SelfCareApiBasePath = "https://api.selfcare.pagopa.it" + SelfCareApiKey = module.key_vault_secrets.values["SelfCareApiKey"].value + LollipopApiBasePath = "https://api.io.pagopa.it" + LollipopApiKey = module.key_vault_secrets.values["LollipopPrimaryApiKey"].value + SlackWebhookUrl = module.key_vault_secrets.values["SlackWebhookUrl"].value + IoLinkBaseUrl = "https://continua.io.pagopa.it" + } + } +} + +module "io_sign_user_func" { + source = "github.com/pagopa/terraform-azurerm-v3//function_app?ref=v8.35.0" + + name = format("%s-user-func", local.project) + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + + health_check_path = "/api/v1/sign/info" + + node_version = "18" + runtime_version = "~4" + always_on = true + + app_service_plan_info = { + kind = "Linux" + sku_size = var.io_sign_user_func.sku_size + maximum_elastic_worker_count = 0 + worker_count = 1 + zone_balancing_enabled = false + } + + app_settings = merge( + local.io_sign_user_func.app_settings, + { + # Enable functions on production triggered by queue and timer + for to_disable in local.io_sign_user_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) => "false" + } + ) + + sticky_app_setting_names = [ + # Sticky the settings enabling triggered by queue and timer + for to_disable in local.io_sign_user_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) + ] + + subnet_id = module.io_sign_user_snet.id + allowed_subnets = [ + module.io_sign_user_snet.id, + ] + + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + system_identity_enabled = true + + tags = var.tags +} + +module "io_sign_user_func_staging_slot" { + count = var.io_sign_user_func.sku_tier == "PremiumV3" ? 1 : 0 + source = "github.com/pagopa/terraform-azurerm-v3//function_app_slot?ref=v8.35.0" + + name = "staging" + location = azurerm_resource_group.backend_rg.location + resource_group_name = azurerm_resource_group.backend_rg.name + function_app_id = module.io_sign_user_func.id + app_service_plan_id = module.io_sign_user_func.app_service_plan_id + + health_check_path = "/api/v1/sign/info" + health_check_maxpingfailures = 2 + + storage_account_name = module.io_sign_user_func.storage_account.name + storage_account_access_key = module.io_sign_user_func.storage_account.primary_access_key + + node_version = "18" + runtime_version = "~4" + always_on = true + application_insights_instrumentation_key = data.azurerm_application_insights.application_insights.instrumentation_key + + app_settings = merge( + local.io_sign_user_func.app_settings, + { + # Disabled functions on slot triggered by queue and timer + for to_disable in local.io_sign_user_func.staging_disabled : + format("AzureWebJobs.%s.Disabled", to_disable) => "true" + } + ) + + subnet_id = module.io_sign_user_snet.id + allowed_subnets = [ + module.io_sign_user_snet.id, + ] + + tags = var.tags +} + +resource "azurerm_monitor_autoscale_setting" "io_sign_user_func" { + count = var.io_sign_user_func.sku_tier == "PremiumV3" ? 1 : 0 + name = format("%s-autoscale", module.io_sign_user_func.name) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + target_resource_id = module.io_sign_user_func.app_service_plan_id + + profile { + name = "default" + + capacity { + default = var.io_sign_user_func.autoscale_default + minimum = var.io_sign_user_func.autoscale_minimum + maximum = var.io_sign_user_func.autoscale_maximum + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_user_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 3500 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_user_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 60 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_user_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 2500 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_user_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 30 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + } + + tags = var.tags +} diff --git a/infra/resources/prod/key_vault.tf b/infra/resources/prod/key_vault.tf new file mode 100644 index 00000000..4e5fb7b5 --- /dev/null +++ b/infra/resources/prod/key_vault.tf @@ -0,0 +1,121 @@ +data "azurerm_user_assigned_identity" "infra_ci" { + name = "${local.product}-infra-github-ci-identity" + resource_group_name = "${local.product}-identity-rg" +} + +data "azurerm_user_assigned_identity" "infra_cd" { + name = "${local.product}-infra-github-cd-identity" + resource_group_name = "${local.product}-identity-rg" +} + +module "key_vault_secrets" { + source = "github.com/pagopa/terraform-azurerm-v3//key_vault_secrets_query?ref=v8.35.0" + + resource_group = azurerm_resource_group.sec_rg.name + key_vault_name = module.key_vault.name + + secrets = [ + "IoServicesSubscriptionKey", + "io-fn-sign-issuer-key", + "io-fn-sign-support-key", + "NamirialPassword", + "NamirialTestPassword", + "SelfCareEventHubConnectionString", + "SelfCareApiKey", + "SlackWebhookUrl", + "LollipopPrimaryApiKey", + "LollipopSecondaryApiKey", + "PdvTokenizerApiKey", + "BackOfficeApiKey", + "io-services-configuration-id", + "io-sign-backoffice-func-key" + ] +} + +module "key_vault" { + source = "github.com/pagopa/terraform-azurerm-v3//key_vault?ref=v8.35.0" + + name = format("%s-%s-kv", local.product, var.domain) + location = azurerm_resource_group.sec_rg.location + resource_group_name = azurerm_resource_group.sec_rg.name + tenant_id = data.azurerm_client_config.current.tenant_id + soft_delete_retention_days = 90 + + tags = var.tags +} + +## adgroup_admin group policy ## +resource "azurerm_key_vault_access_policy" "adgroup_admin" { + key_vault_id = module.key_vault.id + + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azuread_group.adgroup_admin.object_id + + key_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", ] + secret_permissions = ["Get", "List", "Set", "Delete", "Restore", "Recover", ] + storage_permissions = [] + certificate_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", "Restore", "Recover", ] +} + +## adgroup_developers group policy ## +resource "azurerm_key_vault_access_policy" "adgroup_developers" { + key_vault_id = module.key_vault.id + + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azuread_group.adgroup_developers.object_id + + key_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", ] + secret_permissions = ["Get", "List", "Set", "Delete", "Restore", "Recover", ] + storage_permissions = [] + certificate_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", "Restore", "Recover", ] +} + +## adgroup_sign group policy ## +resource "azurerm_key_vault_access_policy" "adgroup_sign" { + key_vault_id = module.key_vault.id + + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azuread_group.adgroup_sign.object_id + + key_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", ] + secret_permissions = ["Get", "List", "Set", "Delete", "Restore", "Recover", ] + storage_permissions = [] + certificate_permissions = ["Get", "List", "Update", "Create", "Import", "Delete", "Restore", "Recover", ] +} + +resource "azurerm_key_vault_access_policy" "infra_ci" { + key_vault_id = module.key_vault.id + + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_user_assigned_identity.infra_ci.principal_id + + secret_permissions = ["Get", "List"] +} + +resource "azurerm_key_vault_access_policy" "infra_cd" { + key_vault_id = module.key_vault.id + + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_user_assigned_identity.infra_cd.principal_id + + secret_permissions = ["Get", "List", "Set"] +} + +# +# azure devops policy +# + +#pagopaspa-io-platform-iac-projects-{subscription} +data "azuread_service_principal" "platform_iac_sp" { + display_name = format("pagopaspa-io-platform-iac-projects-%s", data.azurerm_subscription.current.subscription_id) +} + +resource "azurerm_key_vault_access_policy" "azdevops_platform_iac_policy" { + key_vault_id = module.key_vault.id + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azuread_service_principal.platform_iac_sp.object_id + + secret_permissions = ["Get", "List", "Set", ] + storage_permissions = [] + certificate_permissions = ["SetIssuers", "DeleteIssuers", "Purge", "List", "Get", "ManageContacts", ] +} diff --git a/infra/resources/prod/landing.tf b/infra/resources/prod/landing.tf new file mode 100644 index 00000000..aff368ca --- /dev/null +++ b/infra/resources/prod/landing.tf @@ -0,0 +1,74 @@ +data "azurerm_resource_group" "core_ext" { + name = format("%s-rg-external", local.product) +} + +data "azurerm_resource_group" "core_rg_common" { + name = format("%s-rg-common", local.product) +} + +data "azurerm_key_vault" "core_kv_common" { + name = format("%s-kv-common", local.product) + resource_group_name = data.azurerm_resource_group.core_rg_common.name +} + +data "azurerm_dns_zone" "io_italia_it" { + name = "io.italia.it" + resource_group_name = data.azurerm_resource_group.core_ext.name +} + +data "azurerm_log_analytics_workspace" "common" { + name = var.io_common.log_analytics_workspace_name + resource_group_name = var.io_common.resource_group_name +} + +module "landing_cdn" { + source = "github.com/pagopa/terraform-azurerm-v3//cdn?ref=v8.35.0" + + name = "landing" + prefix = local.project + resource_group_name = azurerm_resource_group.integration_rg.name + location = azurerm_resource_group.integration_rg.location + hostname = "firma.io.italia.it" + https_rewrite_enabled = true + + storage_account_replication_type = "GZRS" + + index_document = "index.html" + error_404_document = "index.html" + + dns_zone_name = data.azurerm_dns_zone.io_italia_it.name + dns_zone_resource_group_name = data.azurerm_resource_group.core_ext.name + + keyvault_vault_name = data.azurerm_key_vault.core_kv_common.name + keyvault_resource_group_name = data.azurerm_resource_group.core_rg_common.name + keyvault_subscription_id = data.azurerm_subscription.current.subscription_id + + querystring_caching_behaviour = "BypassCaching" + + global_delivery_rule = { + cache_expiration_action = [] + cache_key_query_string_action = [] + modify_request_header_action = [] + + # HSTS + modify_response_header_action = [ + { + action = "Overwrite" + name = "Strict-Transport-Security" + value = "max-age=31536000" + }, + # Content-Security-Policy (in Report mode) + { + action = "Append" + name = "Content-Security-Policy" + value = "script-src 'self' 'unsafe-inline'; script-src-elem 'self' 'unsafe-inline' https://cdn.matomo.cloud/pagopa.matomo.cloud/ https://pagopa.matomo.cloud/ https://recaptcha.net/ https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/; style-src 'self' 'unsafe-inline' recaptcha.net; worker-src 'none'; font-src data: 'self'; img-src data: 'self' recaptcha.net; object-src 'none'; " + } + ] + } + + log_analytics_workspace_id = data.azurerm_log_analytics_workspace.common.id + + advanced_threat_protection_enabled = false + + tags = var.tags +} diff --git a/infra/resources/prod/locals.tf b/infra/resources/prod/locals.tf new file mode 100644 index 00000000..e95d0c9e --- /dev/null +++ b/infra/resources/prod/locals.tf @@ -0,0 +1,4 @@ +locals { + project = format("%s-%s-%s", var.prefix, var.env_short, var.domain) + product = format("%s-%s", var.prefix, var.env_short) +} diff --git a/infra/resources/prod/main.tf b/infra/resources/prod/main.tf new file mode 100644 index 00000000..eded645b --- /dev/null +++ b/infra/resources/prod/main.tf @@ -0,0 +1,31 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "<= 3.113.0" + } + azuread = { + source = "hashicorp/azuread" + version = "<= 2.33.0" + } + } + + backend "azurerm" { + resource_group_name = "terraform-state-rg" + storage_account_name = "tfinfprodio" + container_name = "terraform-state" + key = "io-sign.resources.prod.tfstate" + } + + # tflint-ignore: terraform_required_version + # Due to precommit check update required + # required_version = ">= 1.3.7" +} + +provider "azurerm" { + features {} +} + +data "azurerm_subscription" "current" {} + +data "azurerm_client_config" "current" {} diff --git a/infra/resources/prod/monitoring.tf b/infra/resources/prod/monitoring.tf new file mode 100644 index 00000000..295cad9f --- /dev/null +++ b/infra/resources/prod/monitoring.tf @@ -0,0 +1,309 @@ +data "azurerm_application_insights" "application_insights" { + name = "io-p-ai-common" + resource_group_name = "io-p-rg-common" +} + +data "azurerm_key_vault_secret" "monitor_fci_tech_email" { + name = "monitor-fci-tech-email" + key_vault_id = module.key_vault.id +} + +data "azurerm_key_vault_secret" "monitor_fci_tech_slack_email" { + name = "monitor-fci-tech-slack" + key_vault_id = module.key_vault.id +} + +data "azurerm_monitor_action_group" "slack" { + resource_group_name = "io-p-rg-common" + name = "SlackPagoPA" +} + +data "azurerm_monitor_action_group" "email" { + resource_group_name = "io-p-rg-common" + name = "EmailPagoPA" +} + +resource "azurerm_monitor_action_group" "email_fci_tech" { + name = "EmailFirmaConIoTech" + resource_group_name = azurerm_resource_group.integration_rg.name + short_name = "EmailFCITech" + + email_receiver { + name = "sendtooperations" + email_address = data.azurerm_key_vault_secret.monitor_fci_tech_email.value + use_common_alert_schema = true + } + + tags = var.tags +} + +resource "azurerm_monitor_action_group" "slack_fci_tech" { + name = "SlackFirmaConIoTech" + resource_group_name = azurerm_resource_group.integration_rg.name + short_name = "SlackFCITech" + + email_receiver { + name = "sendtoslack" + email_address = data.azurerm_key_vault_secret.monitor_fci_tech_slack_email.value + use_common_alert_schema = true + } + + tags = var.tags +} + +data "azurerm_monitor_action_group" "error_action_group" { + resource_group_name = "io-p-rg-common" + name = "${var.prefix}${var.env_short}error" +} + +resource "azurerm_monitor_metric_alert" "io_sign_user_helathcheck" { + name = format("%s-helathcheck", module.io_sign_user_func.name) + resource_group_name = azurerm_resource_group.backend_rg.name + scopes = [module.io_sign_user_func.id] + description = format("%s health check status is less than 100", module.io_sign_user_func.name) + severity = 1 + frequency = "PT5M" + auto_mitigate = false + + criteria { + metric_namespace = "Microsoft.Web/sites" + metric_name = "HealthCheckStatus" + aggregation = "Maximum" + operator = "LessThan" + threshold = 1 + } + + action { + action_group_id = azurerm_monitor_action_group.email_fci_tech.id + } + + action { + action_group_id = azurerm_monitor_action_group.slack_fci_tech.id + } +} + +resource "azurerm_monitor_scheduled_query_rules_alert" "rejected_requests" { + name = format("%s-rejected-requests", local.project) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + + data_source_id = data.azurerm_application_insights.application_insights.id + description = "[IO-SIGN] There are REJECTED signature requests. Runbook: https://pagopa.atlassian.net/wiki/spaces/SFEQS/pages/935592503/Richieste+di+firma+in+stato+REJECTED" + enabled = true + auto_mitigation_enabled = false + + query = <<-QUERY +customEvents +| where name == "io.sign.signature_request.rejected" +| where customDimensions.environment == "DEFAULT" +| summarize AggregatedValue = count() by bin(timestamp, 30m) +| where AggregatedValue > 1 + QUERY + + severity = 3 + frequency = 30 + time_window = 30 + + trigger { + operator = "GreaterThanOrEqual" + threshold = 1 + } + + action { + action_group = [ + azurerm_monitor_action_group.email_fci_tech.id, + azurerm_monitor_action_group.slack_fci_tech.id, + data.azurerm_monitor_action_group.error_action_group.id + ] + } + + tags = var.tags +} + +resource "azurerm_monitor_scheduled_query_rules_alert" "rejected_test_requests" { + name = format("%s-rejected-test-requests", local.project) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + + data_source_id = data.azurerm_application_insights.application_insights.id + description = "[IO-SIGN] There are REJECTED signature requests in TEST environment. No action required" + enabled = true + auto_mitigation_enabled = false + + query = <<-QUERY +customEvents +| where name == "io.sign.signature_request.rejected" +| where customDimensions.environment == "TEST" +| summarize AggregatedValue = count() by bin(timestamp, 30m) +| where AggregatedValue > 1 + QUERY + + severity = 3 + frequency = 30 + time_window = 30 + + trigger { + operator = "GreaterThanOrEqual" + threshold = 1 + } + + action { + action_group = [ + azurerm_monitor_action_group.slack_fci_tech.id + ] + } + + tags = var.tags +} + +resource "azurerm_monitor_autoscale_setting" "io_sign_backoffice_func" { + name = format("%s-autoscale", module.io_sign_backoffice_func.name) + resource_group_name = azurerm_resource_group.backend_rg.name + location = azurerm_resource_group.backend_rg.location + target_resource_id = module.io_sign_backoffice_func.app_service_plan_id + + profile { + name = "default" + + capacity { + default = var.io_sign_backoffice_func.autoscale_default + minimum = var.io_sign_backoffice_func.autoscale_minimum + maximum = var.io_sign_backoffice_func.autoscale_maximum + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_backoffice_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 3500 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_backoffice_app.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 3500 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_backoffice_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "GreaterThan" + threshold = 60 + divide_by_instance_count = false + } + + scale_action { + direction = "Increase" + type = "ChangeCount" + value = "2" + cooldown = "PT5M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_backoffice_func.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 2500 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + + rule { + metric_trigger { + metric_name = "Requests" + metric_resource_id = module.io_sign_backoffice_app.id + metric_namespace = "microsoft.web/sites" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 2500 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + + rule { + metric_trigger { + metric_name = "CpuPercentage" + metric_resource_id = module.io_sign_backoffice_func.app_service_plan_id + metric_namespace = "microsoft.web/serverfarms" + time_grain = "PT1M" + statistic = "Average" + time_window = "PT5M" + time_aggregation = "Average" + operator = "LessThan" + threshold = 30 + divide_by_instance_count = false + } + + scale_action { + direction = "Decrease" + type = "ChangeCount" + value = "1" + cooldown = "PT20M" + } + } + } + + tags = var.tags +} + diff --git a/infra/resources/prod/network.tf b/infra/resources/prod/network.tf new file mode 100644 index 00000000..56b451b6 --- /dev/null +++ b/infra/resources/prod/network.tf @@ -0,0 +1,445 @@ +data "azurerm_virtual_network" "vnet_common" { + name = format("%s-vnet-common", local.product) + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_virtual_network" "itn_vnet_common" { + name = format("%s-itn-common-vnet-01", local.product) + resource_group_name = format("%s-itn-common-rg-01", local.product) +} + +data "azurerm_subnet" "private_endpoints_subnet" { + name = "pendpoints" + virtual_network_name = format("%s-vnet-common", local.product) + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_subnet" "itn_private_endpoints_subnet" { + name = format("%s-itn-pep-snet-01", local.product) + virtual_network_name = data.azurerm_virtual_network.itn_vnet_common.name + resource_group_name = data.azurerm_virtual_network.itn_vnet_common.resource_group_name +} + +data "azurerm_subnet" "apim_v2" { + name = "apimv2api" + virtual_network_name = format("%s-vnet-common", local.product) + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_nat_gateway" "nat_gateway" { + name = format("%s-natgw", local.product) + resource_group_name = format("%s-rg-common", local.product) +} + +module "io_sign_snet" { + source = "github.com/pagopa/terraform-azurerm-v3//subnet?ref=v8.35.0" + name = format("%s-snet", local.project) + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + virtual_network_name = data.azurerm_virtual_network.vnet_common.name + address_prefixes = var.subnets_cidrs.issuer + + private_endpoint_network_policies_enabled = false + + # network_security_group_id = azurerm_network_security_group.io_sign_issuer_nsg.id + + service_endpoints = [ + "Microsoft.Web", + "Microsoft.AzureCosmosDB", + "Microsoft.Storage", + ] + + delegation = { + name = "default" + service_delegation = { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet_nat_gateway_association" "io_sign_issuer_snet" { + nat_gateway_id = data.azurerm_nat_gateway.nat_gateway.id + subnet_id = module.io_sign_snet.id +} + +resource "azurerm_network_security_group" "io_sign_issuer_nsg" { + name = format("%s-issuer-nsg", local.project) + location = data.azurerm_virtual_network.vnet_common.location + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + + tags = var.tags +} + +module "io_sign_user_snet" { + source = "github.com/pagopa/terraform-azurerm-v3//subnet?ref=v8.35.0" + name = format("%s-user-snet", local.project) + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + virtual_network_name = data.azurerm_virtual_network.vnet_common.name + address_prefixes = var.subnets_cidrs.user + + private_endpoint_network_policies_enabled = false + + # network_security_group_id = azurerm_network_security_group.io_sign_user_nsg.id + + service_endpoints = [ + "Microsoft.Web", + "Microsoft.AzureCosmosDB", + "Microsoft.Storage", + ] + + delegation = { + name = "default" + service_delegation = { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet_nat_gateway_association" "io_sign_user_snet" { + nat_gateway_id = data.azurerm_nat_gateway.nat_gateway.id + subnet_id = module.io_sign_user_snet.id +} + +resource "azurerm_network_security_group" "io_sign_user_nsg" { + name = format("%s-user-nsg", local.project) + location = data.azurerm_virtual_network.vnet_common.location + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + + tags = var.tags +} + +module "io_sign_support_snet" { + source = "github.com/pagopa/terraform-azurerm-v3//subnet?ref=v8.35.0" + name = format("%s-support-snet", local.project) + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + virtual_network_name = data.azurerm_virtual_network.vnet_common.name + address_prefixes = var.subnets_cidrs.support + + private_endpoint_network_policies_enabled = false + + # network_security_group_id = azurerm_network_security_group.io_sign_user_nsg.id + + service_endpoints = [ + "Microsoft.Web", + "Microsoft.AzureCosmosDB", + ] + + delegation = { + name = "default" + service_delegation = { + name = "Microsoft.Web/serverFarms" + actions = ["Microsoft.Network/virtualNetworks/subnets/action"] + } + } +} + +resource "azurerm_subnet_nat_gateway_association" "io_sign_support_snet" { + nat_gateway_id = data.azurerm_nat_gateway.nat_gateway.id + subnet_id = module.io_sign_support_snet.id +} + +resource "azurerm_network_security_group" "io_sign_support_nsg" { + name = format("%s-support-nsg", local.project) + location = data.azurerm_virtual_network.vnet_common.location + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + + tags = var.tags +} + +module "io_sign_eventhub_snet" { + source = "github.com/pagopa/terraform-azurerm-v3//subnet?ref=v8.35.0" + name = format("%s-eventhub-snet", local.project) + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + virtual_network_name = data.azurerm_virtual_network.vnet_common.name + address_prefixes = var.subnets_cidrs.eventhub + + private_endpoint_network_policies_enabled = false + + service_endpoints = ["Microsoft.EventHub"] +} + +resource "azurerm_network_security_group" "io_sign_eventhub_nsg" { + name = format("%s-eventhub-nsg", local.project) + location = data.azurerm_virtual_network.vnet_common.location + resource_group_name = data.azurerm_virtual_network.vnet_common.resource_group_name + + tags = var.tags +} + +data "azurerm_private_dns_zone" "privatelink_documents_azure_com" { + name = "privatelink.documents.azure.com" + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_private_dns_zone" "privatelink_blob_core_windows_net" { + name = "privatelink.blob.core.windows.net" + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_private_dns_zone" "privatelink_queue_core_windows_net" { + name = "privatelink.queue.core.windows.net" + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_private_dns_zone" "privatelink_azurewebsites_net" { + name = "privatelink.azurewebsites.net" + resource_group_name = format("%s-rg-common", local.product) +} + +data "azurerm_private_dns_zone" "privatelink_servicebus_windows_net" { + name = "privatelink.servicebus.windows.net" + resource_group_name = format("%s-evt-rg", local.product) +} + +# Unused at the moment +# tflint-ignore: terraform_unused_declarations +data "azurerm_private_dns_zone" "privatelink_file_core_windows_net" { + name = "privatelink.file.core.windows.net" + resource_group_name = format("%s-rg-common", local.product) +} + +# Unused at the moment +# tflint-ignore: terraform_unused_declarations +data "azurerm_private_dns_zone" "privatelink_table_core_windows_net" { + name = "privatelink.table.core.windows.net" + resource_group_name = format("%s-rg-common", local.product) +} + +resource "azurerm_private_endpoint" "blob" { + name = format("%s-blob-endpoint", module.io_sign_storage.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-blob", module.io_sign_storage.name) + private_connection_resource_id = module.io_sign_storage.id + is_manual_connection = false + subresource_names = ["blob"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_blob_core_windows_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "itn_blob" { + name = format("%s-itn-sign-blob-pep-01", local.product) + location = azurerm_resource_group.sign.location + resource_group_name = azurerm_resource_group.sign.name + subnet_id = data.azurerm_subnet.itn_private_endpoints_subnet.id + + private_service_connection { + name = format("%s-itn-sign-blob-pep-01", local.product) + private_connection_resource_id = module.io_sign_storage_itn.id + is_manual_connection = false + subresource_names = ["blob"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_blob_core_windows_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "queue" { + name = format("%s-queue-endpoint", module.io_sign_storage.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-queue", module.io_sign_storage.name) + private_connection_resource_id = module.io_sign_storage.id + is_manual_connection = false + subresource_names = ["queue"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_queue_core_windows_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "itn_queue" { + name = format("%s-itn-sign-queue-pep-01", local.product) + location = azurerm_resource_group.sign.location + resource_group_name = azurerm_resource_group.sign.name + subnet_id = data.azurerm_subnet.itn_private_endpoints_subnet.id + + private_service_connection { + name = format("%s-itn-sign-queue-pep-01", local.product) + private_connection_resource_id = module.io_sign_storage_itn.id + is_manual_connection = false + subresource_names = ["queue"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_queue_core_windows_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_user_func" { + name = format("%s-endpoint", module.io_sign_user_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-endpoint", module.io_sign_user_func.name) + private_connection_resource_id = module.io_sign_user_func.id + is_manual_connection = false + subresource_names = ["sites"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_user_func_staging" { + count = var.io_sign_user_func.sku_tier == "PremiumV3" ? 1 : 0 + + name = format("%s-staging-endpoint", module.io_sign_user_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-staging-endpoint", module.io_sign_user_func.name) + private_connection_resource_id = module.io_sign_user_func.id + is_manual_connection = false + subresource_names = ["sites-staging"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_issuer_func" { + name = format("%s-endpoint", module.io_sign_issuer_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-endpoint", module.io_sign_issuer_func.name) + private_connection_resource_id = module.io_sign_issuer_func.id + is_manual_connection = false + subresource_names = ["sites"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_issuer_func_staging" { + count = var.io_sign_issuer_func.sku_tier == "PremiumV3" ? 1 : 0 + + name = format("%s-staging-endpoint", module.io_sign_issuer_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-staging-endpoint", module.io_sign_issuer_func.name) + private_connection_resource_id = module.io_sign_issuer_func.id + is_manual_connection = false + subresource_names = ["sites-staging"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_support_func" { + name = format("%s-endpoint", module.io_sign_support_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-endpoint", module.io_sign_support_func.name) + private_connection_resource_id = module.io_sign_support_func.id + is_manual_connection = false + subresource_names = ["sites"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_support_func_staging" { + count = var.io_sign_support_func.sku_tier == "PremiumV3" ? 1 : 0 + + name = format("%s-staging-endpoint", module.io_sign_support_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-staging-endpoint", module.io_sign_support_func.name) + private_connection_resource_id = module.io_sign_support_func.id + is_manual_connection = false + subresource_names = ["sites-staging"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} + +resource "azurerm_private_endpoint" "io_sign_backoffice_func_staging" { + + name = format("%s-staging-endpoint", module.io_sign_backoffice_func.name) + location = azurerm_resource_group.data_rg.location + resource_group_name = azurerm_resource_group.data_rg.name + subnet_id = data.azurerm_subnet.private_endpoints_subnet.id + + private_service_connection { + name = format("%s-staging-endpoint", module.io_sign_backoffice_func.name) + private_connection_resource_id = module.io_sign_backoffice_func.id + is_manual_connection = false + subresource_names = ["sites-staging"] + } + + private_dns_zone_group { + name = "private-dns-zone-group" + private_dns_zone_ids = [data.azurerm_private_dns_zone.privatelink_azurewebsites_net.id] + } + + tags = var.tags +} diff --git a/infra/resources/prod/resource_groups.tf b/infra/resources/prod/resource_groups.tf new file mode 100644 index 00000000..482544b8 --- /dev/null +++ b/infra/resources/prod/resource_groups.tf @@ -0,0 +1,35 @@ +resource "azurerm_resource_group" "data_rg" { + name = format("%s-data-rg", local.project) + location = var.location + + tags = var.tags +} + +resource "azurerm_resource_group" "sign" { + name = format("%s-itn-sign-rg-01", local.product) + location = "italynorth" + + tags = var.tags +} + +resource "azurerm_resource_group" "backend_rg" { + name = format("%s-backend-rg", local.project) + location = var.location + + tags = var.tags +} + +resource "azurerm_resource_group" "sec_rg" { + name = format("%s-sec-rg", local.project) + location = var.location + + tags = var.tags +} + +# Needed to integrate Firma con IO with external domains, products or platforms (ie. eventhub for billing, ...) +resource "azurerm_resource_group" "integration_rg" { + name = format("%s-integration-rg", local.project) + location = var.location + + tags = var.tags +} diff --git a/infra/resources/prod/storage.tf b/infra/resources/prod/storage.tf new file mode 100644 index 00000000..cf5809aa --- /dev/null +++ b/infra/resources/prod/storage.tf @@ -0,0 +1,245 @@ +module "io_sign_storage" { + source = "github.com/pagopa/terraform-azurerm-v3//storage_account?ref=v8.35.0" + name = replace(format("%s-st", local.project), "-", "") + account_kind = "StorageV2" + account_tier = "Standard" + account_replication_type = var.storage_account.replication_type + access_tier = "Hot" + blob_versioning_enabled = var.storage_account.enable_versioning + blob_change_feed_enabled = var.storage_account.change_feed_enabled + resource_group_name = azurerm_resource_group.data_rg.name + location = azurerm_resource_group.data_rg.location + advanced_threat_protection = true + use_legacy_defender_version = true + allow_nested_items_to_be_public = false + public_network_access_enabled = true + + network_rules = { + default_action = "Allow" + ip_rules = [] + bypass = [ + "Logging", + "Metrics", + "AzureServices", + ] + virtual_network_subnet_ids = [] + } + + action = var.storage_account.enable_low_availability_alert ? [ + { + action_group_id = data.azurerm_monitor_action_group.error_action_group.id + webhook_properties = {} + } + ] : [] + + tags = var.tags +} + +module "io_sign_storage_itn" { + source = "github.com/pagopa/terraform-azurerm-v3//storage_account?ref=v8.35.0" + name = replace(format("%s-itn-sign-st-01", local.product), "-", "") + account_kind = "StorageV2" + account_tier = "Standard" + account_replication_type = "ZRS" + access_tier = "Hot" + blob_versioning_enabled = true + blob_change_feed_enabled = false + resource_group_name = azurerm_resource_group.sign.name + location = azurerm_resource_group.sign.location + advanced_threat_protection = false + use_legacy_defender_version = false + allow_nested_items_to_be_public = false + public_network_access_enabled = true + + network_rules = { + default_action = "Allow" + ip_rules = [] + bypass = [ + "Logging", + "Metrics", + "AzureServices", + ] + virtual_network_subnet_ids = [] + } + + action = var.storage_account.enable_low_availability_alert ? [ + { + action_group_id = data.azurerm_monitor_action_group.error_action_group.id + webhook_properties = {} + } + ] : [] + + tags = var.tags +} + +resource "azurerm_storage_management_policy" "io_sign_storage_management_policy" { + storage_account_id = module.io_sign_storage.id + + rule { + name = "deleteafterdays" + enabled = true + filters { + prefix_match = [ + "uploaded-documents", + "validated-documents", + "signed-documents", + ] + blob_types = ["blockBlob"] + } + actions { + base_blob { + delete_after_days_since_modification_greater_than = var.storage_account.delete_after_days + } + } + } +} + +resource "azurerm_storage_management_policy" "io_sign_storage_itn_management_policy" { + storage_account_id = module.io_sign_storage_itn.id + + rule { + name = "deleteafterdays" + enabled = true + filters { + prefix_match = [ + "uploaded-documents", + "validated-documents", + "signed-documents", + ] + blob_types = ["blockBlob"] + } + actions { + base_blob { + delete_after_days_since_modification_greater_than = var.storage_account.delete_after_days + } + } + } +} + +resource "azurerm_storage_container" "uploaded_documents" { + name = "uploaded-documents" + storage_account_name = module.io_sign_storage.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "itn_uploaded_documents" { + name = "uploaded-documents" + storage_account_name = module.io_sign_storage_itn.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "validated_documents" { + name = "validated-documents" + storage_account_name = module.io_sign_storage.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "itn_validated_documents" { + name = "validated-documents" + storage_account_name = module.io_sign_storage_itn.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "signed_documents" { + name = "signed-documents" + storage_account_name = module.io_sign_storage.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "itn_signed_documents" { + name = "signed-documents" + storage_account_name = module.io_sign_storage_itn.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "filled_modules" { + name = "filled-modules" + storage_account_name = module.io_sign_storage.name + container_access_type = "private" +} + +resource "azurerm_storage_container" "itn_filled_modules" { + name = "filled-modules" + storage_account_name = module.io_sign_storage_itn.name + container_access_type = "private" +} + +resource "azurerm_storage_queue" "waiting_for_documents_to_fill" { + name = "waiting-for-documents-to-fill" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_waiting_for_documents_to_fill" { + name = "waiting-for-documents-to-fill" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "on_signature_request_ready" { + name = "on-signature-request-ready" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_on_signature_request_ready" { + name = "on-signature-request-ready" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "on_signature_request_wait_for_signature" { + name = "on-signature-request-wait-for-signature" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_on_signature_request_wait_for_signature" { + name = "on-signature-request-wait-for-signature" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "on_signature_request_rejected" { + name = "on-signature-request-rejected" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_on_signature_request_rejected" { + name = "on-signature-request-rejected" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "on_signature_request_signed" { + name = "on-signature-request-signed" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_on_signature_request_signed" { + name = "on-signature-request-signed" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "waiting_for_qtsp" { + name = "waiting-for-qtsp" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_waiting_for_qtsp" { + name = "waiting-for-qtsp" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "waiting_for_signature_request_updates" { + name = "waiting-for-signature-request-updates" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_waiting_for_signature_request_updates" { + name = "waiting-for-signature-request-updates" + storage_account_name = module.io_sign_storage_itn.name +} + +resource "azurerm_storage_queue" "api_keys" { + name = "api-keys" + storage_account_name = module.io_sign_storage.name +} + +resource "azurerm_storage_queue" "itn_api_keys" { + name = "api-keys" + storage_account_name = module.io_sign_storage_itn.name +} diff --git a/infra/resources/prod/terraform.tfvars b/infra/resources/prod/terraform.tfvars new file mode 100644 index 00000000..724f54d2 --- /dev/null +++ b/infra/resources/prod/terraform.tfvars @@ -0,0 +1,303 @@ +prefix = "io" +env_short = "p" +domain = "sign" +location = "westeurope" + +tags = { + CreatedBy = "Terraform" + Environment = "Prod" + Owner = "IO" + Source = "https://github.com/pagopa/io-infra/src/sign" + CostCenter = "BD100 - STRATEGIC INNOVATION" +} + +# Container App Job GitHub Runner +key_vault_common = { + resource_group_name = "io-p-rg-common" + name = "io-p-kv-common" + pat_secret_name = "github-runner-pat" +} + +# You can retrieve the list of current defined subnets using the CLI command +# az network vnet subnet list --subscription PROD-IO --vnet-name io-p-vnet-common --resource-group io-p-rg-common --output table +# and thus define new CIDRs according to the unallocated address space +subnets_cidrs = { + issuer = ["10.0.102.0/24"] + user = ["10.0.103.0/24"] + eventhub = ["10.0.104.0/24"], + support = ["10.0.147.0/24"] + backoffice = ["10.0.115.0/24"] +} + +storage_account = { + enable_versioning = true + change_feed_enabled = true + delete_after_days = 90 + replication_type = "GZRS" + enable_low_availability_alert = true +} + +cosmos = { + zone_redundant = false + additional_geo_locations = [ + { + location = "northeurope" + failover_priority = 1 + zone_redundant = false + } + ] +} + +io_sign_database_issuer = { + dossiers = { + max_throughput = 1000 + ttl = null + } + signature_requests = { + max_throughput = 1000 + ttl = null + } + uploads = { + max_throughput = 1000 + ttl = 604800 + } + issuers = { + max_throughput = 1000 + ttl = null + } +} + +io_sign_database_user = { + signature_requests = { + max_throughput = 1000 + ttl = null + } + signatures = { + max_throughput = 1000 + ttl = null + } +} + +io_sign_database_backoffice = { + api_keys = { + max_throughput = 1000 + ttl = null + } + api_keys_by_id = { + max_throughput = 1000 + ttl = null + } + issuers = { + max_throughput = 1000 + ttl = null + } + consents = { + max_throughput = 1000 + ttl = null + } +} + +io_sign_issuer_func = { + sku_tier = "PremiumV3" + sku_size = "P1v3" + autoscale_default = 1 + autoscale_minimum = 1 + autoscale_maximum = 5 +} + +io_sign_support_func = { + sku_tier = "PremiumV3" + sku_size = "P1v3" + autoscale_default = 1 + autoscale_minimum = 1 + autoscale_maximum = 5 +} + +io_sign_user_func = { + sku_tier = "PremiumV3" + sku_size = "P1v3" + autoscale_default = 1 + autoscale_minimum = 1 + autoscale_maximum = 5 +} + +io_sign_backoffice_app = { + sku_name = "P1v3" + app_settings = [ + { + name = "NODE_ENV", + value = "production" + }, + { + name = "HOSTNAME", + value = "0.0.0.0" + }, + { + name = "WEBSITE_RUN_FROM_PACKAGE", + value = "1" + }, + { + name = "AUTH_SESSION_SECRET", + key_vault_secret_name = "bo-auth-session-secret" + }, + { + name = "SELFCARE_API_KEY", + key_vault_secret_name = "selfcare-prod-api-key" + }, + { + name = "PDV_TOKENIZER_API_KEY" + key_vault_secret_name = "pdv-tokenizer-api-key" + }, + { + name = "SLACK_WEB_HOOK_URL", + key_vault_secret_name = "slack-webhook-url" + } + ] +} + +io_sign_backoffice_func = { + autoscale_default = 1 + autoscale_minimum = 1 + autoscale_maximum = 3 + app_settings = [ + { + name = "NODE_ENV", + value = "production" + }, + { + name = "SELFCARE_EVENT_HUB_CONTRACTS_NAME", + value = "sc-contracts" + }, + { + name = "BACK_OFFICE_API_BASE_PATH" + value = "https://api.io.pagopa.it/api/v1/sign/backoffice" + }, + { + name = "SelfCareEventHubConnectionString", + key_vault_secret_name = "SelfCareEventHubConnectionString" + }, + { + name = "SLACK_WEBHOOK_URL", + key_vault_secret_name = "slack-webhook-url" + }, + { + name = "BACK_OFFICE_API_KEY" + key_vault_secret_name = "BackOfficeApiKey" + }, + { + name = "GOOGLE_PRIVATE_KEY", + key_vault_secret_name = "bo-google-private-key" + }, + { + name = "GOOGLE_CLIENT_EMAIL", + key_vault_secret_name = "bo-google-client-email" + }, + { + name = "GOOGLE_SPREADSHEET_ID", + key_vault_secret_name = "bo-google-spreadsheet-id" + }, + { + name = "SELFCARE_API_KEY", + key_vault_secret_name = "selfcare-prod-api-key" + }, + ] +} + +integration_hub = { + auto_inflate_enabled = true + sku_name = "Standard" + capacity = 1 + maximum_throughput_units = 5 + zone_redundant = true + alerts_enabled = true + ip_rules = [ + { + ip_mask = "18.192.147.151", # PDND-DATALAKE + action = "Allow" + }, + { + ip_mask = "18.159.227.69", # PDND-DATALAKE + action = "Allow" + }, + { + ip_mask = "3.126.198.129", # PDND-DATALAKE + action = "Allow" + } + ] + hubs = [ + { + name = "billing" + partitions = 3 + message_retention = 7 + consumers = [] + keys = [ + { + name = "io-sign-func-issuer" + listen = false + send = true + manage = false + }, + { + name = "pdnd-invoicing" + listen = true + send = false + manage = false + } + ] + }, + { + name = "analytics" + partitions = 3 + message_retention = 7 + consumers = [] + keys = [ + { + name = "io-sign-func-user" + listen = false + send = true + manage = false + }, + { + name = "io-sign-func-issuer" + listen = false + send = true + manage = false + }, + { + name = "pdnd-invoicing" + listen = true + send = false + manage = false + } + ] + } + ] +} + +# DNS + +dns_zone_names = { + website = "firma.io.pagopa.it" +} + +dns_ses_validation = [ + { + name = "usgxww7qq2vgfzl4da6yv4qb4f7ls5kq._domainkey" + record = "usgxww7qq2vgfzl4da6yv4qb4f7ls5kq.dkim.amazonses.com" + }, + { + name = "e4m2laccz356yraixvndjtoivkwf4sc2._domainkey" + record = "e4m2laccz356yraixvndjtoivkwf4sc2.dkim.amazonses.com" + }, + { + name = "43al7wmot7uxzzz6dfq7fnkcqilx6q6l._domainkey" + record = "43al7wmot7uxzzz6dfq7fnkcqilx6q6l.dkim.amazonses.com" + }, +] + +io_common = { + resource_group_name = "io-p-rg-common" + log_analytics_workspace_name = "io-p-law-common" + appgateway_snet_name = "io-p-appgateway-snet" + vnet_common_name = "io-p-vnet-common" +} diff --git a/infra/resources/prod/variables.tf b/infra/resources/prod/variables.tf new file mode 100644 index 00000000..f28cbd0c --- /dev/null +++ b/infra/resources/prod/variables.tf @@ -0,0 +1,225 @@ +# general + +variable "prefix" { + type = string + validation { + condition = ( + length(var.prefix) < 6 + ) + error_message = "Max length is 6 chars." + } +} + +variable "env_short" { + type = string + validation { + condition = ( + length(var.env_short) <= 1 + ) + error_message = "Max length is 1 chars." + } +} + +variable "domain" { + type = string + validation { + condition = ( + length(var.domain) < 6 + ) + error_message = "Max length is 6 chars." + } +} + +variable "location" { + type = string +} + +variable "tags" { + type = map(any) + default = { + CreatedBy = "Terraform" + } +} + +# domain specific + +variable "key_vault_common" { + type = object({ + resource_group_name = string + name = string + pat_secret_name = string + }) +} + +# DNS +variable "dns_ses_validation" { + type = list(object({ + name = string + record = string + })) + description = "CNAME records to validate SES domain identity" +} + +variable "dns_default_ttl_sec" { + type = number + description = "Default TTL for DNS" + default = 3600 +} + +variable "dns_zone_names" { + type = object({ + website = string + }) + description = "The names for the DNS zones" +} + +variable "subnets_cidrs" { + type = map( + list(string) + ) + description = "The CIDR address prefixes of the subnets" +} + +variable "storage_account" { + type = object({ + enable_versioning = bool + change_feed_enabled = bool + delete_after_days = number + replication_type = string + enable_low_availability_alert = bool + }) + description = "The configuration of the storage account storing documents" +} + +variable "cosmos" { + type = object({ + zone_redundant = bool + additional_geo_locations = list(object({ + location = string + failover_priority = number + zone_redundant = bool + })) + }) +} + +variable "io_sign_database_issuer" { + type = map( + object({ + max_throughput = number + ttl = number + }) + ) +} + +variable "io_sign_database_user" { + type = map( + object({ + max_throughput = number + ttl = number + }) + ) +} + +variable "io_sign_database_backoffice" { + type = map( + object({ + max_throughput = number + ttl = number + }) + ) +} + +variable "io_sign_issuer_func" { + type = object({ + sku_tier = string + sku_size = string + autoscale_default = number + autoscale_minimum = number + autoscale_maximum = number + }) +} + +variable "io_sign_support_func" { + type = object({ + sku_tier = string + sku_size = string + autoscale_default = number + autoscale_minimum = number + autoscale_maximum = number + }) +} + + +variable "io_sign_user_func" { + type = object({ + sku_tier = string + sku_size = string + autoscale_default = number + autoscale_minimum = number + autoscale_maximum = number + }) +} + +variable "integration_hub" { + type = object({ + auto_inflate_enabled = bool + sku_name = string + capacity = number + maximum_throughput_units = number + zone_redundant = bool + alerts_enabled = bool + ip_rules = list(object({ + ip_mask = string + action = string + })) + hubs = list(object({ + name = string + partitions = number + message_retention = number + consumers = list(string) + keys = list(object({ + name = string + listen = bool + send = bool + manage = bool + })) + })) + }) + description = "The configuration, hubs and keys of the event hub relative to external integration" +} + +variable "io_common" { + type = object({ + resource_group_name = string + log_analytics_workspace_name = string + appgateway_snet_name = string + vnet_common_name = string + }) + description = "Name of common resources of IO platform" +} + +variable "io_sign_backoffice_app" { + type = object({ + sku_name = string + app_settings = list(object({ + name = string + value = optional(string, "") + key_vault_secret_name = optional(string) + })) + }) + description = "Configuration of the io-sign-backoffice app service" +} + +variable "io_sign_backoffice_func" { + type = object({ + autoscale_default = number + autoscale_minimum = number + autoscale_maximum = number + app_settings = list(object({ + name = string + value = optional(string, "") + key_vault_secret_name = optional(string) + })) + }) + description = "Configuration of the io-sign-backoffice func app" +}