diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 821ee52..2dcda0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: format: sarif output: trivy.sarif - - name: Upload SARIF file + - name: Upload Trivy SARIF results uses: github/codeql-action/upload-sarif@v2 timeout-minutes: 1 with: diff --git a/main.tf b/main.tf index 67193f3..3a4d559 100644 --- a/main.tf +++ b/main.tf @@ -155,6 +155,35 @@ module "vault" { tags = local.tags } +# Creates a storage account for storing GraphDB backups +module "backup" { + source = "./modules/backup" + + resource_name_prefix = var.resource_name_prefix + location = var.location + resource_group_name = azurerm_resource_group.graphdb.name + + nacl_subnet_ids = [azurerm_subnet.graphdb-vmss.id] + nacl_ip_rules = var.management_cidr_blocks + + storage_account_tier = var.storage_account_tier + storage_account_replication_type = var.storage_account_replication_type + + tags = local.tags +} + +# Creates and assigns required roles to the identity and services +module "roles" { + source = "./modules/roles" + + resource_name_prefix = var.resource_name_prefix + resource_group_id = azurerm_resource_group.graphdb.id + + identity_principal_id = module.identity.identity_principal_id + key_vault_id = module.vault.key_vault_id + backups_storage_container_id = module.backup.storage_account_id +} + # Managed GraphDB configurations in the Key Vault module "configuration" { source = "./modules/configuration" @@ -189,7 +218,7 @@ module "tls" { tags = local.tags # Wait for role assignments - depends_on = [module.identity, module.vault] + depends_on = [module.vault] } # Creates a public application gateway for forwarding internet traffic to the GraphDB proxies @@ -257,13 +286,11 @@ module "vm" { resource_name_prefix = var.resource_name_prefix location = var.location resource_group_name = azurerm_resource_group.graphdb.name - resource_group_id = azurerm_resource_group.graphdb.id zones = var.zones graphdb_subnet_id = azurerm_subnet.graphdb-vmss.id identity_id = module.identity.identity_id - identity_principal_id = module.identity.identity_principal_id application_gateway_backend_address_pool_ids = [module.application_gateway.gateway_backend_address_pool_id] # Configurations for the user data script @@ -281,28 +308,11 @@ module "vm" { custom_user_data = var.custom_graphdb_vm_user_data - backup_schedule = var.backup_schedule + backup_storage_container_url = module.backup.storage_container_id + backup_schedule = var.backup_schedule tags = local.tags # Wait for configurations to be created in the key vault and roles to be assigned - depends_on = [module.configuration] -} - -# Creates a storage account for storing GraphDB backups -module "backup" { - source = "./modules/backup" - - resource_name_prefix = var.resource_name_prefix - location = var.location - resource_group_name = azurerm_resource_group.graphdb.name - - nacl_subnet_ids = [azurerm_subnet.graphdb-vmss.id] - nacl_ip_rules = var.management_cidr_blocks - - identity_principal_id = module.identity.identity_principal_id - storage_account_tier = var.storage_account_tier - storage_account_replication_type = var.storage_account_replication_type - - tags = local.tags + depends_on = [module.configuration, module.roles] } diff --git a/modules/backup/main.tf b/modules/backup/main.tf index bb58cac..fa48f3d 100644 --- a/modules/backup/main.tf +++ b/modules/backup/main.tf @@ -48,20 +48,6 @@ resource "azurerm_storage_container" "graphdb-backup" { container_access_type = "private" } -# Create an Azure Storage blob -resource "azurerm_storage_blob" "graphdb-backup" { - name = "${var.resource_name_prefix}-backup" - type = "Block" - storage_account_name = azurerm_storage_account.graphdb-backup.name - storage_container_name = azurerm_storage_container.graphdb-backup.name -} - -resource "azurerm_role_assignment" "graphdb-backup" { - principal_id = var.identity_principal_id - role_definition_name = "Storage Blob Data Contributor" - scope = azurerm_storage_account.graphdb-backup.id -} - resource "azurerm_storage_management_policy" "graphdb-backup-retention" { storage_account_id = azurerm_storage_account.graphdb-backup.id diff --git a/modules/backup/outputs.tf b/modules/backup/outputs.tf index 410e662..cf8be02 100644 --- a/modules/backup/outputs.tf +++ b/modules/backup/outputs.tf @@ -1,9 +1,19 @@ +output "storage_account_id" { + description = "Storage account identifier for storing GraphDB backups" + value = azurerm_storage_account.graphdb-backup.id +} + output "storage_account_name" { description = "Storage account name for storing GraphDB backups" value = azurerm_storage_account.graphdb-backup.name } -output "container_name" { +output "storage_container_id" { + description = "Identifier of the storage container for GraphDB backups" + value = azurerm_storage_container.graphdb-backup.id +} + +output "storage_container_name" { description = "Name of the storage container for GraphDB backups" value = azurerm_storage_container.graphdb-backup.name } diff --git a/modules/backup/variables.tf b/modules/backup/variables.tf index 7ec2e7f..ec00cbb 100644 --- a/modules/backup/variables.tf +++ b/modules/backup/variables.tf @@ -35,13 +35,6 @@ variable "nacl_ip_rules" { default = [] } -# Identity - -variable "identity_principal_id" { - description = "Principal identifier of a user assigned identity for assigning permissions" - type = string -} - # Storage specifics variable "storage_account_tier" { diff --git a/modules/configuration/main.tf b/modules/configuration/main.tf index 79b0741..aa6e90f 100644 --- a/modules/configuration/main.tf +++ b/modules/configuration/main.tf @@ -68,19 +68,34 @@ resource "azurerm_key_vault_secret" "graphdb-java-options" { tags = var.tags } -# TODO: Cannot assign the secret resource as scope for some reason... it doesn't show in the console and it does not work in the VMs -# TODO: Not the right place for this to be here if we cannot give more granular access +resource "azurerm_role_assignment" "graphdb-license-secret-reader" { + principal_id = var.identity_principal_id + scope = azurerm_key_vault_secret.graphdb-license.resource_versionless_id + role_definition_name = "Key Vault Secrets User" +} -# Give rights to the provided identity to be able to read it from the vault -resource "azurerm_role_assignment" "graphdb-license-reader" { +resource "azurerm_role_assignment" "graphdb-cluster-token-secret-reader" { principal_id = var.identity_principal_id - scope = var.key_vault_id - role_definition_name = "Key Vault Reader" + scope = azurerm_key_vault_secret.graphdb-cluster-token.resource_versionless_id + role_definition_name = "Key Vault Secrets User" } -# Give rights to the provided identity to actually get the secret value -resource "azurerm_role_assignment" "graphdb-license-secret-reader" { +resource "azurerm_role_assignment" "graphdb-java-options-secret-reader" { + count = var.graphdb_java_options != null ? 1 : 0 + principal_id = var.identity_principal_id + scope = azurerm_key_vault_secret.graphdb-java-options[0].resource_versionless_id + role_definition_name = "Key Vault Secrets User" +} + +resource "azurerm_role_assignment" "graphdb-password-secret-reader" { + principal_id = var.identity_principal_id + scope = azurerm_key_vault_secret.graphdb-password.resource_versionless_id + role_definition_name = "Key Vault Secrets User" +} + +resource "azurerm_role_assignment" "graphdb-properties-secret-reader" { + count = var.graphdb_properties_path != null ? 1 : 0 principal_id = var.identity_principal_id - scope = var.key_vault_id + scope = azurerm_key_vault_secret.graphdb-properties[0].resource_versionless_id role_definition_name = "Key Vault Secrets User" } diff --git a/modules/identity/variables.tf b/modules/identity/variables.tf index 67c945f..e1eb46d 100644 --- a/modules/identity/variables.tf +++ b/modules/identity/variables.tf @@ -1,3 +1,5 @@ +# General configurations + variable "resource_name_prefix" { description = "Resource name prefix used for tagging and naming Azure resources" type = string diff --git a/modules/roles/README.md b/modules/roles/README.md new file mode 100644 index 0000000..39117e9 --- /dev/null +++ b/modules/roles/README.md @@ -0,0 +1 @@ +# GraphDB Roles Module diff --git a/modules/roles/main.tf b/modules/roles/main.tf new file mode 100644 index 0000000..86b5670 --- /dev/null +++ b/modules/roles/main.tf @@ -0,0 +1,45 @@ +# Assign the identity to have read access to the key vault +resource "azurerm_role_assignment" "graphdb-vmss-key-vault-reader" { + principal_id = var.identity_principal_id + scope = var.key_vault_id + role_definition_name = "Key Vault Reader" +} + +# Assign the identity to be able to upload GraphDB backups in the storage BLOB +resource "azurerm_role_assignment" "graphdb-backup" { + principal_id = var.identity_principal_id + scope = var.backups_storage_container_id + role_definition_name = "Storage Blob Data Contributor" +} + +resource "azurerm_role_definition" "managed_disk_manager" { + name = "${var.resource_name_prefix}-ManagedDiskManager-6" + scope = var.resource_group_id + description = "This is a custom role created via Terraform required for creating data disks for GraphDB" + + permissions { + actions = [ + "Microsoft.Compute/disks/read", + "Microsoft.Compute/disks/write", + "Microsoft.Compute/virtualMachineScaleSets/read", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/write", + "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read", + "Microsoft.Network/virtualNetworks/subnets/join/action", + "Microsoft.Network/applicationGateways/backendAddressPools/join/action", + "Microsoft.Network/networkSecurityGroups/join/action" + ] + not_actions = [] + } + + assignable_scopes = [ + var.resource_group_id + ] +} + +resource "azurerm_role_assignment" "rg-contributor-role" { + principal_id = var.identity_principal_id + scope = var.resource_group_id + role_definition_name = azurerm_role_definition.managed_disk_manager.name + + depends_on = [azurerm_role_definition.managed_disk_manager] +} diff --git a/modules/roles/output.tf b/modules/roles/output.tf new file mode 100644 index 0000000..e69de29 diff --git a/modules/roles/variables.tf b/modules/roles/variables.tf new file mode 100644 index 0000000..f9da504 --- /dev/null +++ b/modules/roles/variables.tf @@ -0,0 +1,32 @@ +# Common configurations + +variable "resource_name_prefix" { + description = "Resource name prefix used for tagging and naming Azure resources" + type = string +} + +variable "resource_group_id" { + description = "Identifier of the resource group where GraphDB will be deployed." + type = string +} + +# Identity + +variable "identity_principal_id" { + description = "Principal identifier of a user assigned identity for assigning permissions" + type = string +} + +# Key Vault + +variable "key_vault_id" { + description = "Identifier of a Key Vault for storing GraphDB configurations" + type = string +} + +# Backups storage + +variable "backups_storage_container_id" { + description = "Identifier of the storage container for GraphDB backups" + type = string +} diff --git a/modules/tls/main.tf b/modules/tls/main.tf index 4c730ad..f31d19d 100644 --- a/modules/tls/main.tf +++ b/modules/tls/main.tf @@ -6,9 +6,8 @@ resource "azurerm_user_assigned_identity" "graphdb-tls-certificate" { tags = var.tags } -# TODO: probably have to add Key Vault Reader as well - -resource "azurerm_role_assignment" "graphdb-tls-certificate-reader" { +# Azure AG requires this role to the be assigned to the Key Vault directly +resource "azurerm_role_assignment" "graphdb-tls-key-vault-secrets-reader" { principal_id = azurerm_user_assigned_identity.graphdb-tls-certificate.principal_id scope = var.key_vault_id role_definition_name = "Key Vault Secrets User" diff --git a/modules/vm/main.tf b/modules/vm/main.tf index ec07803..c406d61 100644 --- a/modules/vm/main.tf +++ b/modules/vm/main.tf @@ -5,6 +5,7 @@ locals { disk_iops_read_write : var.disk_iops_read_write disk_mbps_read_write : var.disk_mbps_read_write disk_size_gb : var.disk_size_gb + backup_storage_container_url : var.backup_storage_container_url, backup_schedule : var.backup_schedule }) } @@ -63,8 +64,6 @@ resource "azurerm_linux_virtual_machine_scale_set" "graphdb" { } tags = var.tags - - depends_on = [azurerm_role_assignment.rg-contributor-role, azurerm_role_assignment.rg-reader-role] } resource "azurerm_monitor_autoscale_setting" "graphdb-autoscale-settings" { @@ -86,60 +85,3 @@ resource "azurerm_monitor_autoscale_setting" "graphdb-autoscale-settings" { tags = var.tags } - -resource "azurerm_role_definition" "managed_disk_manager" { - name = "${var.resource_name_prefix}-ManagedDiskManager" - scope = var.resource_group_id - description = "This is a custom role created via Terraform required for creating data disks for GraphDB" - - permissions { - actions = [ - "Microsoft.Compute/disks/read", - "Microsoft.Compute/disks/write", - "Microsoft.Compute/virtualMachineScaleSets/read", - "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/write", - "Microsoft.Compute/virtualMachineScaleSets/virtualMachines/read", - "Microsoft.Network/virtualNetworks/subnets/join/action", - "Microsoft.Network/applicationGateways/backendAddressPools/join/action", - "Microsoft.Network/networkSecurityGroups/join/action" - ] - not_actions = [] - } - - assignable_scopes = [ - var.resource_group_id - ] -} - -resource "azurerm_role_assignment" "rg-contributor-role" { - principal_id = var.identity_principal_id - scope = var.resource_group_id - role_definition_name = "${var.resource_name_prefix}-ManagedDiskManager" - depends_on = [azurerm_role_definition.managed_disk_manager] -} - -resource "azurerm_role_definition" "backup_role" { - name = "${var.resource_name_prefix}-ReadOnlyVMSSStorageRole" - scope = var.resource_group_id - description = "This is a custom role created via Terraform required for creating backups in GraphDB" - - permissions { - actions = [ - "Microsoft.Compute/virtualMachineScaleSets/read", - "Microsoft.Storage/storageAccounts/read" - ] - not_actions = [] - } - - assignable_scopes = [ - var.resource_group_id - ] -} - -resource "azurerm_role_assignment" "rg-reader-role" { - principal_id = var.identity_principal_id - scope = var.resource_group_id - role_definition_name = "${var.resource_name_prefix}-ReadOnlyVMSSStorageRole" - depends_on = [azurerm_role_definition.backup_role] -} - diff --git a/modules/vm/templates/entrypoint.sh.tpl b/modules/vm/templates/entrypoint.sh.tpl index fec1cba..ba69e4c 100644 --- a/modules/vm/templates/entrypoint.sh.tpl +++ b/modules/vm/templates/entrypoint.sh.tpl @@ -207,9 +207,7 @@ fi BACKUP_NAME="\$(date +'%Y-%m-%d_%H-%M-%S').tar" TEMP_BACKUP_DIR="/var/opt/graphdb/" -STORAGE_ACCOUNT="\$(az storage account list --resource-group "\$RESOURCE_GROUP" --query "[].name" --output tsv)" -CONTAINER_NAME="\$(az storage container list --account-name "\$STORAGE_ACCOUNT" --query "[].name" --output tsv --auth-mode login)" -BLOB_URL="\$(az storage blob url --account-name "\$STORAGE_ACCOUNT" --container-name "\$CONTAINER_NAME" --name "\$BACKUP_NAME" --auth-mode login --output tsv)" +BLOB_URL="${backup_storage_container_url}/\$${BACKUP_NAME}" function trigger_backup { curl -X POST --output "\$${TEMP_BACKUP_DIR}\$${BACKUP_NAME}" -H 'Content-Type: application/json' 'http://localhost:7200/rest/recovery/backup' diff --git a/modules/vm/variables.tf b/modules/vm/variables.tf index d8c3f2b..a5b0d07 100644 --- a/modules/vm/variables.tf +++ b/modules/vm/variables.tf @@ -27,11 +27,6 @@ variable "resource_group_name" { type = string } -variable "resource_group_id" { - description = "Identifier of the resource group where GraphDB will be deployed." - type = string -} - # Networking variable "graphdb_subnet_id" { @@ -46,11 +41,6 @@ variable "identity_id" { type = string } -variable "identity_principal_id" { - description = "Principal identifier of a user assigned identity with permissions" - type = string -} - variable "key_vault_name" { description = "Name of a Key Vault containing GraphDB configurations" type = string @@ -127,6 +117,13 @@ variable "disk_mbps_read_write" { default = null } +# Backups + +variable "backup_storage_container_url" { + description = "URL to a storage container for uploading GraphDB backups" + type = string +} + variable "backup_schedule" { description = "Cron expression for the backup job." type = string