Skip to content

Commit

Permalink
Merge pull request #445 from recognizegroup/feature/22793-workflow-po…
Browse files Browse the repository at this point in the history
…-handler

feat: Added Standard Logic App with Managed Identity and IP restriction (for HTTP trigger)
  • Loading branch information
tom-reinders authored Feb 11, 2025
2 parents 9eb0cff + 90ec974 commit 9f54751
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 10 deletions.
149 changes: 139 additions & 10 deletions modules/azure/logic_app_standard/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ terraform {
source = "hashicorp/azurerm"
version = "~> 3.48"
}
archive = {
source = "hashicorp/archive"
version = "~> 2.3"
azapi = {
source = "Azure/azapi"
version = "~> 1.4"
}
azuread = {
source = "hashicorp/azuread"
version = "~> 2.36"
}
}

Expand All @@ -19,12 +23,11 @@ provider "azurerm" {
features {}
}

provider "archive" {
}

locals {
identity_type = var.use_managed_identity && length(var.identity_ids) > 0 ? "SystemAssigned, UserAssigned" : var.use_managed_identity ? "SystemAssigned" : length(var.identity_ids) > 0 ? "UserAssigned" : null
is_linux = length(regexall("/home/", lower(abspath(path.root)))) > 0
identity_type = var.use_managed_identity && length(var.identity_ids) > 0 ? "SystemAssigned, UserAssigned" : var.use_managed_identity ? "SystemAssigned" : length(var.identity_ids) > 0 ? "UserAssigned" : null
is_linux = length(regexall("/home/", lower(abspath(path.root)))) > 0
identifiers = var.managed_identity_provider != null ? concat(["api://${var.managed_identity_provider.create.application_name}"], var.managed_identity_provider.identifier_uris != null ? var.managed_identity_provider.identifier_uris : []) : []
allowed_audiences = var.managed_identity_provider != null ? concat(local.identifiers, var.managed_identity_provider.allowed_audiences != null ? var.managed_identity_provider.allowed_audiences : []) : []
}

resource "azurerm_logic_app_standard" "app" {
Expand All @@ -47,12 +50,36 @@ resource "azurerm_logic_app_standard" "app" {
ftps_state = "Disabled"
elastic_instance_minimum = var.elastic_instance_minimum
pre_warmed_instance_count = var.pre_warmed_instance_count

dynamic "ip_restriction" {
for_each = var.ip_restrictions

content {
ip_address = ip_restriction.value.ip_address
service_tag = ip_restriction.value.service_tag
virtual_network_subnet_id = ip_restriction.value.virtual_network_subnet_id
name = ip_restriction.value.name
priority = ip_restriction.value.priority
action = ip_restriction.value.action

dynamic "headers" {
for_each = ip_restriction.value.headers

content {
x_azure_fdid = headers.value.x_azure_fdid
x_fd_health_probe = headers.value.x_fd_health_probe
x_forwarded_for = headers.value.x_forwarded_for
x_forwarded_host = headers.value.x_forwarded_host
}
}
}
}
}

app_settings = merge({
WEBSITE_NODE_DEFAULT_VERSION = "~18",
FUNCTIONS_WORKER_RUNTIME = "node",
}, var.app_settings)
FUNCTIONS_WORKER_RUNTIME = "node"
}, var.managed_identity_provider != null ? { MICROSOFT_PROVIDER_AUTHENTICATION_SECRET = azuread_application_password.password[0].value } : {}, var.app_settings)

app_service_plan_id = var.service_plan_id
storage_account_access_key = var.storage_account_access_key
Expand Down Expand Up @@ -158,3 +185,105 @@ resource "azurerm_monitor_diagnostic_setting" "diagnostic_setting" {
}
}
}

# Managed Identity Provider
data "azuread_client_config" "current" {}

resource "random_uuid" "oath2_uuid" {}

resource "azuread_application" "application" {
count = var.managed_identity_provider != null ? 1 : 0
display_name = var.managed_identity_provider.create.display_name
owners = var.managed_identity_provider.create.owners != null ? concat([data.azuread_client_config.current.object_id], var.managed_identity_provider.create.owners) : [data.azuread_client_config.current.object_id]
sign_in_audience = "AzureADMyOrg"
identifier_uris = local.identifiers

api {
requested_access_token_version = 2

oauth2_permission_scope {
admin_consent_description = var.managed_identity_provider.create.oauth2_settings.admin_consent_description
admin_consent_display_name = var.managed_identity_provider.create.oauth2_settings.admin_consent_display_name
enabled = var.managed_identity_provider.create.oauth2_settings.enabled
id = random_uuid.oath2_uuid.result
type = var.managed_identity_provider.create.oauth2_settings.type
user_consent_description = var.managed_identity_provider.create.oauth2_settings.user_consent_description
user_consent_display_name = var.managed_identity_provider.create.oauth2_settings.user_consent_display_name
value = var.managed_identity_provider.create.oauth2_settings.role_value
}
}

web {
redirect_uris = ["https://${var.logic_app_name}.azurewebsites.net/.auth/login/aad/callback"]

implicit_grant {
access_token_issuance_enabled = false
id_token_issuance_enabled = true
}
}

required_resource_access {
resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph

resource_access {
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read
type = "Scope"
}
}
}

resource "null_resource" "always_run" {
triggers = {
timestamp = "${timestamp()}"
}
}

resource "azapi_update_resource" "setup_auth_settings" {
count = var.managed_identity_provider != null ? 1 : 0
type = "Microsoft.Web/sites/config@2020-12-01"
resource_id = "${azurerm_logic_app_standard.app.id}/config/web"

depends_on = [
azurerm_logic_app_standard.app,
null_resource.always_run
]

body = jsonencode({
properties = {
siteAuthSettingsV2 = {
globalValidation = {
excludedPaths = []
require_authentication = true,
// Even though is looks weird, it is needed. Otherwise, the app and also the designer in Azure Portal are not working
// https://techcommunity.microsoft.com/blog/integrationsonazureblog/trigger-workflows-in-standard-logic-apps-with-easy-auth/3207378
unauthenticatedClientAction = "AllowAnonymous"
},
IdentityProviders = {
azureActiveDirectory = {
enabled = true,
registration = {
clientId = azuread_application.application[0].application_id
clientSecretSettingName = "MICROSOFT_PROVIDER_AUTHENTICATION_SECRET"
},
validation = {
allowedAudiences = local.allowed_audiences
}
}
}
}
}
})
lifecycle {
/* This action should always be replaces since is works under the hood as an api call
* So it does not really track issues with the function app properly
*/
replace_triggered_by = [
null_resource.always_run
]
}
}

resource "azuread_application_password" "password" {
count = var.managed_identity_provider != null ? 1 : 0
application_object_id = azuread_application.application[0].object_id
}
53 changes: 53 additions & 0 deletions modules/azure/logic_app_standard/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,56 @@ variable "log_analytics_diagnostic_categories" {
description = "Optional list of diagnostic categories to override the default categories."
default = []
}

variable "managed_identity_provider" {
type = object({
existing = optional(object({
client_id = string
client_secret = string
}))
create = optional(object({
application_name = string
display_name = string
oauth2_settings = object({
admin_consent_description = string
admin_consent_display_name = string
enabled = bool
type = string
user_consent_description = string
user_consent_display_name = string
role_value = string
})
owners = optional(list(string)) # Deployment user will be added as owner by default
redirect_uris = optional(list(string)) # Only for additional URIs, function uri will be added by default
group_id = optional(string) # Group ID where service principal of the existing application will belong to
}))
identifier_uris = optional(list(string)) # api://<application_name> will be added by default if application is create
allowed_audiences = optional(list(string)) # api://<application-name> will be added by default
})
validation {
condition = var.managed_identity_provider.existing != null || var.managed_identity_provider.create != null
error_message = "Variable managed_identity_provider has to provide either an existing managed identity provider or given information to create one"
}
description = "The managed identity provider to use for connections on this function app"
default = null
}

variable "ip_restrictions" {
type = list(object({
ip_address = optional(string),
service_tag = optional(string),
virtual_network_subnet_id = optional(string),
name = optional(string),
priority = optional(number),
action = optional(string),

headers = optional(list(object({
x_azure_fdid = optional(list(string)),
x_fd_health_probe = optional(list(string)),
x_forwarded_for = optional(list(string)),
x_forwarded_host = optional(list(string))
})))
}))
description = "A List of objects representing IP restrictions."
default = []
}

0 comments on commit 9f54751

Please sign in to comment.