Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# All Values Required
AZURE_ENV=<AZURE CLOUD ENVIRONMENT>
ENGINE_APP_ID=<ENGINE APP REGISTRATION CLIENT ID>
ENGINE_APP_SECRET=<ENGINE APP REGISTRATION SECRET>
UI_APP_ID=<UI APP REGISTRATION CLIENT ID>
TENANT_ID=<AZURE AD TENANT ID>
COSMOS_URL=https://<COSMOS NAME>.documents.azure.com
KEYVAULT_URL=https://<KEYVAULT NAME>.vault.azure.net

# Legacy Values
# COSMOS_KEY=<COSMOS ACCESS KEY>
# ENGINE_APP_SECRET=<ENGINE APP REGISTRATION SECRET>
54 changes: 32 additions & 22 deletions deploy/deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -721,13 +721,6 @@ process {
}
}

Write-Host "INFO: Creating Azure IPAM Engine Secret" -ForegroundColor Green

# Create IPAM Engine Secret
$engineSecret = Invoke-WithGraphRetry -ScriptBlock {
New-AzADAppCredential -ApplicationObject $engineObject -StartDate (Get-Date) -EndDate (Get-Date).AddYears(2)
}

if (-not $DisableUI) {
Write-Host "INFO: Azure IPAM Engine & UI Applications/Service Principals created successfully" -ForegroundColor Green
}
Expand All @@ -737,7 +730,6 @@ process {

$appDetails = @{
EngineAppId = $engineApp.AppId
EngineSecret = $engineSecret.SecretText
}

# Add UI AppID to AppDetails (If DisableUI not specified)
Expand Down Expand Up @@ -894,8 +886,6 @@ process {
[string]$UIAppId = [GUID]::Empty,
[Parameter(Mandatory = $true)]
[string]$EngineAppId,
[Parameter(Mandatory = $true)]
[string]$EngineSecret,
[Parameter(Mandatory = $false)]
[bool]$DisableUI = $false
)
Expand All @@ -907,14 +897,13 @@ process {

# Update Parameter Values
$parametersObject.parameters.engineAppId.value = $EngineAppId
$parametersObject.parameters.engineAppSecret.value = $EngineSecret

if (-not $DisableUI) {
$parametersObject.parameters.uiAppId.value = $UIAppId
$parametersObject.parameters = $parametersObject.parameters | Select-Object -Property uiAppId, engineAppId, engineAppSecret
$parametersObject.parameters = $parametersObject.parameters | Select-Object -Property uiAppId, engineAppId
}
else {
$parametersObject.parameters = $parametersObject.parameters | Select-Object -Property engineAppId, engineAppSecret
$parametersObject.parameters = $parametersObject.parameters | Select-Object -Property engineAppId
}

# Output updated parameter file for Bicep deployment
Expand All @@ -937,13 +926,12 @@ process {
# Read Values from Parameters
$UIAppId = $parametersObject.parameters.uiAppId.value ?? [GUID]::Empty
$EngineAppId = $parametersObject.parameters.engineAppId.value
$EngineSecret = $parametersObject.parameters.engineAppSecret.value
$script:DisableUI = ($UIAppId -eq [GUID]::Empty) ? $true : $false

if ((-not $EngineAppId) -or (-not $EngineSecret)) {
if ((-not $EngineAppId)) {
Write-Host "ERROR: Missing required parameters from Bicep parameter file" -ForegroundColor Red
Write-Host "ERROR: Please ensure the following parameters are present in the Bicep parameter file" -ForegroundColor Red
Write-Host "ERROR: Required: [engineAppId, engineAppSecret]" -ForegroundColor Red
Write-Host "ERROR: Required: [engineAppId]" -ForegroundColor Red
Write-Host ""
Write-Host "ERROR: Please refer to the deployment documentation for more information" -ForegroundColor Red
Write-Host "ERROR: " -ForegroundColor Red -NoNewline
Expand All @@ -960,7 +948,6 @@ process {
$appDetails = @{
UIAppId = $UIAppId
EngineAppId = $EngineAppId
EngineSecret = $EngineSecret
}

return $appDetails
Expand All @@ -973,8 +960,6 @@ process {
[Parameter(Mandatory = $true)]
[string]$EngineAppId,
[Parameter(Mandatory = $true)]
[string]$EngineSecret,
[Parameter(Mandatory = $false)]
[string]$NamePrefix,
[Parameter(Mandatory = $false)]
[string]$AzureCloud,
Expand All @@ -997,7 +982,6 @@ process {
# Instantiate deployment parameter object
$deploymentParameters = @{
engineAppId = $EngineAppId
engineAppSecret = $EngineSecret
uiAppId = $UiAppId
}

Expand Down Expand Up @@ -1160,8 +1144,28 @@ process {
} while ($publishSuccess -eq $False -and $publishRetries -ge 0)
}

function Update-UIApplication {
param(
Function New-FederatedCredential {
Param(
[Parameter(Mandatory = $true)]
[string]$EngineAppId,
[Parameter(Mandatory = $true)]
[string]$ManagedIdentityPrincipalId
)

Write-Host "INFO: Creating Federated Credential" -ForegroundColor Green

New-AzADAppFederatedCredential `
-ApplicationObjectId (Get-AzADApplication -ApplicationId $EngineAppId).Id `
-Audience "api://AzureADTokenExchange" `
-Issuer "https://login.microsoftonline.com/$($(Get-AzContext).Tenant.Id)/v2.0" `
-Subject $ManagedIdentityPrincipalId `
-Name "ipam-engine-mi-credential" | Out-Null

Write-Host "INFO: Federated Credential Created" -ForegroundColor Green
}

Function Update-UIApplication {
Param(
[Parameter(Mandatory = $true)]
[string]$UIAppId,
[Parameter(Mandatory = $true)]
Expand Down Expand Up @@ -1305,6 +1309,12 @@ process {
-Endpoint $deployment.Outputs["appServiceHostName"].Value
}

if ($PSCmdlet.ParameterSetName -notin 'AppsOnly') {
New-FederatedCredential `
-EngineAppId $appDetails.EngineAppId `
-ManagedIdentityPrincipalId $deployment.Outputs["managedIdentityPrincipalId"].Value
}

if ($PSCmdlet.ParameterSetName -in ('App', 'Function')) {
if (-not $ZipFilePath) {
try {
Expand Down
6 changes: 1 addition & 5 deletions deploy/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ param uiAppId string = '00000000-0000-0000-0000-000000000000'
@description('IPAM-Engine App Registration Client/App ID')
param engineAppId string

@secure()
@description('IPAM-Engine App Registration Client Secret')
param engineAppSecret string

@description('Tags')
param tags object = {}

Expand Down Expand Up @@ -104,7 +100,6 @@ module keyVault './modules/keyVault.bicep' = {
identityClientId: managedIdentity.outputs.clientId
uiAppId: uiAppId
engineAppId: engineAppId
engineAppSecret: engineAppSecret
workspaceId: logAnalyticsWorkspace.outputs.workspaceId
}
}
Expand Down Expand Up @@ -200,3 +195,4 @@ output appServiceName string = deployAsFunc ? resourceNames.functionName : resou
output appServiceHostName string = deployAsFunc ? functionApp!.outputs.functionAppHostName : appService!.outputs.appServiceHostName
output acrName string = privateAcr ? containerRegistry!.outputs.acrName : ''
output acrUri string = privateAcr ? containerRegistry!.outputs.acrUri : ''
output managedIdentityPrincipalId string = managedIdentity.outputs.principalId
3 changes: 0 additions & 3 deletions deploy/main.parameters.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
"engineAppId": {
"value": "<ENGINE APP REGISTRATION APP/CLIENT ID>"
},
"engineAppSecret": {
"value": "<ENGINE APP REGISTRATION CLIENT SECRET>"
},
"deployAsFunc": {
"value": false
},
Expand Down
4 changes: 0 additions & 4 deletions deploy/modules/appService.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@ var appServiceSiteConfig = {
name: 'ENGINE_APP_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-ID/)'
}
{
name: 'ENGINE_APP_SECRET'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-SECRET/)'
}
{
name: 'TENANT_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/TENANT-ID/)'
Expand Down
4 changes: 0 additions & 4 deletions deploy/modules/functionApp.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,6 @@ var functionAppSiteConfig = {
name: 'ENGINE_APP_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-ID/)'
}
{
name: 'ENGINE_APP_SECRET'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-SECRET/)'
}
{
name: 'TENANT_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/TENANT-ID/)'
Expand Down
12 changes: 0 additions & 12 deletions deploy/modules/keyVault.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ param uiAppId string
@description('IPAM-Engine App Registration Client/App ID')
param engineAppId string

@secure()
@description('IPAM-Engine App Registration Client Secret')
param engineAppSecret string

@description('Log Analytics Worskpace ID')
param workspaceId string

Expand Down Expand Up @@ -73,14 +69,6 @@ resource engineId 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
}
}

resource engineSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: 'ENGINE-SECRET'
properties: {
value: engineAppSecret
}
}

resource appTenant 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: 'TENANT-ID'
Expand Down
50 changes: 37 additions & 13 deletions engine/app/routers/common/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from azure.cosmos.aio import CosmosClient
from azure.identity.aio import (
ClientSecretCredential,
ClientAssertionCredential,
ManagedIdentityCredential,
OnBehalfOfCredential,
)
Expand Down Expand Up @@ -190,25 +191,48 @@ async def get_obo_token(assertion):
async def get_client_credentials():
"""Return a client-secret credential for the IPAM service principal (app-only auth)."""

credential = ClientSecretCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
client_secret=globals.CLIENT_SECRET,
authority=globals.AUTHORITY_HOST
)
# If CLIENT_SECRET is defined, use it for credentials. Otherwise, use the Managed Identity
if globals.CLIENT_SECRET:
credential = ClientSecretCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
client_secret=globals.CLIENT_SECRET,
authority=globals.AUTHORITY_HOST
)
else:
mi_token = await managed_identity_credential.get_token("api://AzureADTokenExchange")

credential = ClientAssertionCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
func=lambda: mi_token.token,
authority=globals.AUTHORITY_HOST
)

return credential

async def get_obo_credentials(assertion):
"""Return an on-behalf-of credential built from the given user assertion (delegated auth)."""

credential = OnBehalfOfCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
client_secret=globals.CLIENT_SECRET,
user_assertion=assertion,
authority=globals.AUTHORITY_HOST
)
# If CLIENT_SECRET is defined, use it for credentials. Otherwise, use the Managed Identity
if globals.CLIENT_SECRET:
credential = OnBehalfOfCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
client_secret=globals.CLIENT_SECRET,
user_assertion=assertion,
authority=globals.AUTHORITY_HOST
)
else:
mi_token = await managed_identity_credential.get_token("api://AzureADTokenExchange")

credential = OnBehalfOfCredential(
tenant_id=globals.TENANT_ID,
client_id=globals.CLIENT_ID,
client_assertion_func=lambda: mi_token.token,
user_assertion=assertion,
authority=globals.AUTHORITY_HOST
)

return credential

Expand Down
2 changes: 1 addition & 1 deletion engine/requirements.lock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ azure-mgmt-datafactory==9.2.0
azure-mgmt-managementgroups==1.0.0
azure-mgmt-network==29.0.0
azure-mgmt-resource==25.0.0
azure-mgmt-resource-subscriptions==1.0.0
azure-mgmt-resource-subscriptions==1.0.0b1
azure-mgmt-resourcegraph==8.0.0
certifi==2025.8.3
cffi==2.0.0
Expand Down
4 changes: 0 additions & 4 deletions migrate/modules/appService.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ var appServiceSiteConfig = {
name: 'ENGINE_APP_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-ID/)'
}
{
name: 'ENGINE_APP_SECRET'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-SECRET/)'
}
{
name: 'TENANT_ID'
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/TENANT-ID/)'
Expand Down