From e37a3bb1041cd1fb8b4d93fa231d95c1745fbaf7 Mon Sep 17 00:00:00 2001 From: natanelm Date: Sun, 2 Feb 2025 08:43:50 +0000 Subject: [PATCH] Azure vWAN Terraform | Added managed identity support --- .../azure/nva-into-existing-hub/README.md | 2 +- terraform/azure/nva-into-existing-hub/main.tf | 220 ++++++++++------- .../azure/nva-into-existing-hub/versions.tf | 12 +- terraform/azure/nva-into-new-vwan/README.md | 2 +- terraform/azure/nva-into-new-vwan/main.tf | 222 +++++++++++------- terraform/azure/nva-into-new-vwan/versions.tf | 10 + 6 files changed, 297 insertions(+), 171 deletions(-) diff --git a/terraform/azure/nva-into-existing-hub/README.md b/terraform/azure/nva-into-existing-hub/README.md index 6dca42ef..cbc4b57d 100644 --- a/terraform/azure/nva-into-existing-hub/README.md +++ b/terraform/azure/nva-into-existing-hub/README.md @@ -18,7 +18,7 @@ please see the [CloudGuard Network for Azure Virtual WAN Deployment Guide](https - Choose the preferred login method to Azure in order to deploy the solution:
1. Using Service Principal: - Create a [Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal) (or use the existing one) - - Grant the Service Principal at least "**Contributor**" permissions to the Azure subscription
+ - Grant the Service Principal at least "**Contributor**" and "**User Access Administrator**" permissions to the Azure subscription
- The Service Principal credentials can be stored either in the terraform.tfvars or as [Environment Variables](https://www.terraform.io/docs/providers/azuread/guides/service_principal_client_secret.html)
In case the Environment Variables are used, perform modifications described below:
diff --git a/terraform/azure/nva-into-existing-hub/main.tf b/terraform/azure/nva-into-existing-hub/main.tf index b9c37078..58e26bbd 100644 --- a/terraform/azure/nva-into-existing-hub/main.tf +++ b/terraform/azure/nva-into-existing-hub/main.tf @@ -58,6 +58,7 @@ locals { routing-intent-policies = var.routing-intent-internet-traffic == "yes" ? (var.routing-intent-private-traffic == "yes" ? tolist([local.routing_intent-internet-policy, local.routing_intent-private-policy]) : tolist([local.routing_intent-internet-policy])) : (var.routing-intent-private-traffic == "yes" ? tolist([local.routing_intent-private-policy]) : []) req_body = jsonencode({"properties": {"routingPolicies": local.routing-intent-policies}}) req_url = "https://management.azure.com/subscriptions/${var.subscription_id}/resourceGroups/${var.vwan-hub-resource-group}/providers/Microsoft.Network/virtualHubs/${var.vwan-hub-name}/routingIntent/hubRoutingIntent?api-version=2022-01-01" + public_ip_resource_group = var.new-public-ip == "yes" ? azurerm_resource_group.managed-app-rg.name : "/subscriptions/${var.subscription_id}/resourceGroups/${split("/", var.existing-public-ip)[4]}" } //********************** Marketplace Terms & Solution Registration **************************// @@ -91,93 +92,144 @@ resource "azurerm_resource_provider_registration" "solutions" { name = "Microsoft.Solutions" } +//********************** Managed Identiy **************************// +resource "azurerm_user_assigned_identity" "managed_app_identiy" { + location = azurerm_resource_group.managed-app-rg.location + name = "managed_app_identiy" + resource_group_name = azurerm_resource_group.managed-app-rg.name +} + +resource "azurerm_role_assignment" "reader" { + depends_on = [azurerm_user_assigned_identity.managed_app_identiy] + scope = data.azurerm_virtual_hub.vwan-hub.id + role_definition_name = "Reader" + principal_id = azurerm_user_assigned_identity.managed_app_identiy.principal_id +} + +resource "random_id" "randomId" { + keepers = { + resource_group = azurerm_resource_group.managed-app-rg.name + } + byte_length = 8 +} + +resource "azurerm_role_definition" "public-ip-join-role" { + count = var.new-public-ip == "yes" || length(var.existing-public-ip) > 0 ? 1 : 0 + name = "Managed Application Public IP Join Role - ${random_id.randomId.hex}" + scope = local.public_ip_resource_group + permissions { + actions = ["Microsoft.Network/publicIPAddresses/join/action"] + not_actions = [] + } + assignable_scopes = [local.public_ip_resource_group] +} + +resource "azurerm_role_assignment" "public-ip-join-role-assignment" { + count = var.new-public-ip == "yes" || length(var.existing-public-ip) > 0 ? 1 : 0 + scope = local.public_ip_resource_group + role_definition_id = azurerm_role_definition.public-ip-join-role[0].role_definition_resource_id + principal_id = azurerm_user_assigned_identity.managed_app_identiy.principal_id +} //********************** Managed Application Configuration **************************// -resource "azurerm_managed_application" "nva" { +resource "azapi_resource" "managed-app" { depends_on = [azurerm_marketplace_agreement.accept-marketplace-terms, azurerm_resource_provider_registration.solutions] - name = var.managed-app-name - location = azurerm_resource_group.managed-app-rg.location - resource_group_name = azurerm_resource_group.managed-app-rg.name - kind = "MarketPlace" - managed_resource_group_name = var.nva-rg-name - - plan { - name = "vwan-app" - product = "cp-vwan-managed-app" - publisher = "checkpoint" - version = "1.0.16" + type = "Microsoft.Solutions/applications@2019-07-01" + name = var.managed-app-name + location = azurerm_resource_group.managed-app-rg.location + parent_id = azurerm_resource_group.managed-app-rg.id + body = { + kind = "MarketPlace", + plan = { + name = "vwan-app" + product = "cp-vwan-managed-app" + publisher = "checkpoint" + version = "1.0.21" + }, + identity = { + type = "UserAssigned" + userAssignedIdentities = { + (azurerm_user_assigned_identity.managed_app_identiy.id) = {} + } + }, + properties = { + parameters = { + location = { + value = azurerm_resource_group.managed-app-rg.location + }, + hubId = { + value = data.azurerm_virtual_hub.vwan-hub.id + }, + osVersion = { + value = var.os-version + }, + LicenseType = { + value = var.license-type + }, + imageVersion = { + value = element(local.image_versions, length(local.image_versions) -1) + }, + scaleUnit = { + value = var.scale-unit + }, + bootstrapScript = { + value = var.bootstrap-script + }, + adminShell = { + value = var.admin-shell + }, + sicKey = { + value = var.sic-key + }, + sshPublicKey = { + value = var.ssh-public-key + }, + BGP = { + value = var.bgp-asn + }, + NVA = { + value = var.nva-name + }, + customMetrics = { + value = var.custom-metrics + }, + hubASN = { + value = data.azurerm_virtual_hub.vwan-hub.virtual_router_asn + }, + hubPeers = { + value = data.azurerm_virtual_hub.vwan-hub.virtual_router_ips + }, + smart1CloudTokenA = { + value = var.smart1-cloud-token-a + }, + smart1CloudTokenB = { + value = var.smart1-cloud-token-b + }, + smart1CloudTokenC = { + value = var.smart1-cloud-token-c + }, + smart1CloudTokenD = { + value = var.smart1-cloud-token-d + }, + smart1CloudTokenE = { + value = var.smart1-cloud-token-e + }, + publicIPIngress = { + value = (var.new-public-ip == "yes" || length(var.existing-public-ip) > 0) ? "yes" : "no" + }, + createNewIPIngress = { + value = var.new-public-ip + }, + ipIngressExistingResourceId = { + value = var.existing-public-ip + }, + templateName = { + value = "wan_terraform" + } + }, + managedResourceGroupId = "/subscriptions/${var.subscription_id}/resourcegroups/${var.nva-rg-name}" + } } - parameter_values = jsonencode({ - location = { - value = azurerm_resource_group.managed-app-rg.location - }, - hubId = { - value = data.azurerm_virtual_hub.vwan-hub.id - }, - osVersion = { - value = var.os-version - }, - LicenseType = { - value = var.license-type - }, - imageVersion = { - value = element(local.image_versions, length(local.image_versions) -1) - }, - scaleUnit = { - value = var.scale-unit - }, - bootstrapScript = { - value = var.bootstrap-script - }, - adminShell = { - value = var.admin-shell - }, - sicKey = { - value = var.sic-key - }, - sshPublicKey = { - value = var.ssh-public-key - }, - BGP = { - value = var.bgp-asn - }, - NVA = { - value = var.nva-name - }, - customMetrics = { - value = var.custom-metrics - }, - hubASN = { - value = data.azurerm_virtual_hub.vwan-hub.virtual_router_asn - }, - hubPeers = { - value = data.azurerm_virtual_hub.vwan-hub.virtual_router_ips - }, - smart1CloudTokenA = { - value = var.smart1-cloud-token-a - }, - smart1CloudTokenB = { - value = var.smart1-cloud-token-b - }, - smart1CloudTokenC = { - value = var.smart1-cloud-token-c - }, - smart1CloudTokenD = { - value = var.smart1-cloud-token-d - }, - smart1CloudTokenE = { - value = var.smart1-cloud-token-e - }, - publicIPIngress = { - value = (var.new-public-ip == "yes" || length(var.existing-public-ip) > 0) ? "yes" : "no" - }, - createNewIPIngress = { - value = var.new-public-ip - } - ipIngressExistingResourceId = { - value = var.existing-public-ip - } - }) } //********************** Routing Intent **************************// @@ -185,7 +237,7 @@ resource "azurerm_managed_application" "nva" { data "external" "update-routing-intent" { count = length(local.routing-intent-policies) != 0 ? 1 : 0 - depends_on = [azurerm_managed_application.nva] + depends_on = [azapi_resource.managed-app] program = ["python", "../modules/add-routing-intent.py", "${local.req_url}", "${local.req_body}", "${local.access_token}"] } diff --git a/terraform/azure/nva-into-existing-hub/versions.tf b/terraform/azure/nva-into-existing-hub/versions.tf index 2c81dc30..a2cac57a 100644 --- a/terraform/azure/nva-into-existing-hub/versions.tf +++ b/terraform/azure/nva-into-existing-hub/versions.tf @@ -5,13 +5,23 @@ terraform { source = "hashicorp/azurerm" version = "~> 3.90.0" } + azapi = { + source = "Azure/azapi" + version = "~> 2.2.0" + } + random = { + version = "~> 3.5.1" + } } } +provider "azapi" { +} + provider "azurerm" { subscription_id = var.subscription_id client_id = var.client_id client_secret = var.client_secret tenant_id = var.tenant_id features {} -} +} \ No newline at end of file diff --git a/terraform/azure/nva-into-new-vwan/README.md b/terraform/azure/nva-into-new-vwan/README.md index b5d82afc..bd12c9fe 100644 --- a/terraform/azure/nva-into-new-vwan/README.md +++ b/terraform/azure/nva-into-new-vwan/README.md @@ -21,7 +21,7 @@ please see the [CloudGuard Network for Azure Virtual WAN Deployment Guide](https - Choose the preferred login method to Azure in order to deploy the solution:
1. Using Service Principal: - Create a [Service Principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal) (or use the existing one) - - Grant the Service Principal at least "**Contributor**" permissions to the Azure subscription
+ - Grant the Service Principal at least "**Contributor**" and "**User Access Administrator**" permissions to the Azure subscription
- The Service Principal credentials can be stored either in the terraform.tfvars or as [Environment Variables](https://www.terraform.io/docs/providers/azuread/guides/service_principal_client_secret.html)
In case the Environment Variables are used, perform modifications described below:
diff --git a/terraform/azure/nva-into-new-vwan/main.tf b/terraform/azure/nva-into-new-vwan/main.tf index a94ab093..9d8c7bf2 100644 --- a/terraform/azure/nva-into-new-vwan/main.tf +++ b/terraform/azure/nva-into-new-vwan/main.tf @@ -67,6 +67,7 @@ locals { routing-intent-policies = var.routing-intent-internet-traffic == "yes" ? (var.routing-intent-private-traffic == "yes" ? tolist([local.routing_intent-internet-policy, local.routing_intent-private-policy]) : tolist([local.routing_intent-internet-policy])) : (var.routing-intent-private-traffic == "yes" ? tolist([local.routing_intent-private-policy]) : []) req_body = jsonencode({"properties": {"routingPolicies": local.routing-intent-policies}}) req_url = "https://management.azure.com/subscriptions/${var.subscription_id}/resourceGroups/${azurerm_resource_group.managed-app-rg.name}/providers/Microsoft.Network/virtualHubs/${var.vwan-hub-name}/routingIntent/hubRoutingIntent?api-version=2022-01-01" + public_ip_resource_group = var.new-public-ip == "yes" ? azurerm_resource_group.managed-app-rg.id : "/subscriptions/${var.subscription_id}/resourceGroups/${split("/", var.existing-public-ip)[4]}" } @@ -102,98 +103,151 @@ resource "azurerm_resource_provider_registration" "solutions" { name = "Microsoft.Solutions" } +//********************** Managed Identiy **************************// +resource "azurerm_user_assigned_identity" "managed_app_identiy" { + location = azurerm_resource_group.managed-app-rg.location + name = "managed_app_identiy" + resource_group_name = azurerm_resource_group.managed-app-rg.name +} + +resource "azurerm_role_assignment" "reader" { + depends_on = [azurerm_user_assigned_identity.managed_app_identiy] + scope = azurerm_virtual_hub.vwan-hub.id + role_definition_name = "Reader" + principal_id = azurerm_user_assigned_identity.managed_app_identiy.principal_id +} + +resource "random_id" "randomId" { + keepers = { + resource_group = azurerm_resource_group.managed-app-rg.name + } + byte_length = 8 +} + +resource "azurerm_role_definition" "public-ip-join-role" { + count = var.new-public-ip == "yes" || length(var.existing-public-ip) > 0 ? 1 : 0 + name = "Managed Application Public IP Join Role - ${random_id.randomId.hex}" + scope = local.public_ip_resource_group + permissions { + actions = ["Microsoft.Network/publicIPAddresses/join/action"] + not_actions = [] + } + assignable_scopes = [local.public_ip_resource_group] +} + +resource "azurerm_role_assignment" "public-ip-join-role-assignment" { + count = var.new-public-ip == "yes" || length(var.existing-public-ip) > 0 ? 1 : 0 + scope = local.public_ip_resource_group + role_definition_id = azurerm_role_definition.public-ip-join-role[0].role_definition_resource_id + principal_id = azurerm_user_assigned_identity.managed_app_identiy.principal_id +} + //********************** Managed Application Configuration **************************// -resource "azurerm_managed_application" "nva" { +resource "azapi_resource" "managed-app" { depends_on = [azurerm_marketplace_agreement.accept-marketplace-terms, azurerm_resource_provider_registration.solutions] - name = var.managed-app-name - location = azurerm_resource_group.managed-app-rg.location - resource_group_name = azurerm_resource_group.managed-app-rg.name - kind = "MarketPlace" - managed_resource_group_name = var.nva-rg-name - - plan { - name = "vwan-app" - product = "cp-vwan-managed-app" - publisher = "checkpoint" - version = "1.0.16" + type = "Microsoft.Solutions/applications@2019-07-01" + name = var.managed-app-name + location = azurerm_resource_group.managed-app-rg.location + parent_id = azurerm_resource_group.managed-app-rg.id + body = { + kind = "MarketPlace", + plan = { + name = "vwan-app" + product = "cp-vwan-managed-app" + publisher = "checkpoint" + version = "1.0.21" + }, + identity = { + type = "UserAssigned" + userAssignedIdentities = { + (azurerm_user_assigned_identity.managed_app_identiy.id) = {} + } + }, + properties = { + parameters = { + location = { + value = azurerm_resource_group.managed-app-rg.location + }, + hubId = { + value = azurerm_virtual_hub.vwan-hub.id + }, + osVersion = { + value = var.os-version + }, + LicenseType = { + value = var.license-type + }, + imageVersion = { + value = element(local.image_versions, length(local.image_versions) -1) + }, + scaleUnit = { + value = var.scale-unit + }, + bootstrapScript = { + value = var.bootstrap-script + }, + adminShell = { + value = var.admin-shell + }, + sicKey = { + value = var.sic-key + }, + sshPublicKey = { + value = var.ssh-public-key + }, + BGP = { + value = var.bgp-asn + }, + NVA = { + value = var.nva-name + }, + customMetrics = { + value = var.custom-metrics + }, + hubASN = { + value = azurerm_virtual_hub.vwan-hub.virtual_router_asn + }, + hubPeers = { + value = azurerm_virtual_hub.vwan-hub.virtual_router_ips + }, + smart1CloudTokenA = { + value = var.smart1-cloud-token-a + }, + smart1CloudTokenB = { + value = var.smart1-cloud-token-b + }, + smart1CloudTokenC = { + value = var.smart1-cloud-token-c + }, + smart1CloudTokenD = { + value = var.smart1-cloud-token-d + }, + smart1CloudTokenE = { + value = var.smart1-cloud-token-e + }, + publicIPIngress = { + value = (var.new-public-ip == "yes" || length(var.existing-public-ip) > 0) ? "yes" : "no" + }, + createNewIPIngress = { + value = var.new-public-ip + }, + ipIngressExistingResourceId = { + value = var.existing-public-ip + }, + templateName = { + value = "wan_terraform" + } + }, + managedResourceGroupId = "/subscriptions/${var.subscription_id}/resourcegroups/${var.nva-rg-name}" + } } - parameter_values = jsonencode({ - location = { - value = azurerm_resource_group.managed-app-rg.location - }, - hubId = { - value = azurerm_virtual_hub.vwan-hub.id - }, - osVersion = { - value = var.os-version - }, - LicenseType = { - value = var.license-type - }, - imageVersion = { - value = element(local.image_versions, length(local.image_versions) -1) - }, - scaleUnit = { - value = var.scale-unit - }, - bootstrapScript = { - value = var.bootstrap-script - }, - adminShell = { - value = var.admin-shell - }, - sicKey = { - value = var.sic-key - }, - sshPublicKey = { - value = var.ssh-public-key - }, - BGP = { - value = var.bgp-asn - }, - NVA = { - value = var.nva-name - }, - customMetrics = { - value = var.custom-metrics - }, - hubASN = { - value = azurerm_virtual_hub.vwan-hub.virtual_router_asn - }, - hubPeers = { - value = azurerm_virtual_hub.vwan-hub.virtual_router_ips - }, - smart1CloudTokenA = { - value = var.smart1-cloud-token-a - }, - smart1CloudTokenB = { - value = var.smart1-cloud-token-b - }, - smart1CloudTokenC = { - value = var.smart1-cloud-token-c - }, - smart1CloudTokenD = { - value = var.smart1-cloud-token-d - }, - smart1CloudTokenE = { - value = var.smart1-cloud-token-e - }, - publicIPIngress = { - value = (var.new-public-ip == "yes" || length(var.existing-public-ip) > 0) ? "yes" : "no" - }, - createNewIPIngress = { - value = var.new-public-ip - } - ipIngressExistingResourceId = { - value = var.existing-public-ip - } - }) } + //********************** Routing Intent **************************// data "external" "update-routing-intent" { count = length(local.routing-intent-policies) != 0 ? 1 : 0 - depends_on = [azurerm_managed_application.nva] + depends_on = [azapi_resource.managed-app] program = ["python", "../modules/add-routing-intent.py", "${local.req_url}", "${local.req_body}", "${local.access_token}"] } diff --git a/terraform/azure/nva-into-new-vwan/versions.tf b/terraform/azure/nva-into-new-vwan/versions.tf index ca6ac207..a2cac57a 100644 --- a/terraform/azure/nva-into-new-vwan/versions.tf +++ b/terraform/azure/nva-into-new-vwan/versions.tf @@ -5,9 +5,19 @@ terraform { source = "hashicorp/azurerm" version = "~> 3.90.0" } + azapi = { + source = "Azure/azapi" + version = "~> 2.2.0" + } + random = { + version = "~> 3.5.1" + } } } +provider "azapi" { +} + provider "azurerm" { subscription_id = var.subscription_id client_id = var.client_id