зеркало из https://github.com/Azure/ipam.git
Updated deploy script, bicep templates, switches Cosmos DB to use RBAC, switch KeyVault to use RBAC, updated NPM packages, added scripts for version management and building zip asset file and updated docs and screenshots to align with other updates
This commit is contained in:
Родитель
2a49304f17
Коммит
ef26ce6bd2
Двоичные данные
build/ipam.zip → assets/ipam.zip
Двоичные данные
build/ipam.zip → assets/ipam.zip
Двоичный файл не отображается.
187
build/build.ps1
187
build/build.ps1
|
@ -12,23 +12,32 @@ param(
|
|||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $true)]
|
||||
[ValidateScript({
|
||||
if(Test-Path -LiteralPath $_ -PathType Container) {
|
||||
return $true
|
||||
}
|
||||
elseif(Test-Path -LiteralPath $_ -PathType Leaf) {
|
||||
throw 'The Path parameter must be a folder, file paths are not allowed.'
|
||||
}
|
||||
throw 'Invalid File Path'
|
||||
if (Test-Path -LiteralPath $_ -PathType Container) {
|
||||
return $true
|
||||
}
|
||||
elseif (Test-Path -LiteralPath $_ -PathType Leaf) {
|
||||
throw 'The Path parameter must be a folder, file paths are not allowed.'
|
||||
}
|
||||
throw 'Invalid File Path'
|
||||
})]
|
||||
[string]
|
||||
$Path,
|
||||
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $false)]
|
||||
[ValidateScript({
|
||||
if ($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1) {
|
||||
return $true
|
||||
}
|
||||
throw 'File name contains invalid characters'
|
||||
})]
|
||||
[string]
|
||||
$FileName = 'ipam.zip'
|
||||
)
|
||||
|
||||
# Root Directory
|
||||
$ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName
|
||||
|
||||
# Define minimum NodeJS and NPM versions required to build the Azure IPAM UI solution
|
||||
$MIN_NODE_VERSION = [System.Version]'18.0.0'
|
||||
$MIN_NPM_VERSION = [System.Version]'8.6.0'
|
||||
|
@ -37,92 +46,98 @@ $MIN_NPM_VERSION = [System.Version]'8.6.0'
|
|||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Set Log File Location
|
||||
$logPath = [Io.Path]::Combine('..', 'logs')
|
||||
New-Item -ItemType Directory -Force -Path $logpath | Out-Null
|
||||
$logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs"
|
||||
New-Item -ItemType Directory -Path $logpath -Force | Out-Null
|
||||
|
||||
$buildLog = Join-Path -Path $logPath -ChildPath "deploy_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
$buildLog = Join-Path -Path $logPath -ChildPath "build_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
|
||||
Start-Transcript -Path $buildLog | Out-Null
|
||||
|
||||
# Verify NodeJS & NPM are Installed and Meet the Minimum Version Requirements
|
||||
Write-Host "INFO: Private flag set, verifying NodeJS is present and has the correct version" -ForegroundColor Green
|
||||
Write-Verbose -Message "Private flag set, verifying NodeJS is present and has the correct version"
|
||||
try {
|
||||
Write-Host
|
||||
Write-Host "INFO: Verifying NodeJS is present and has the correct version" -ForegroundColor Green
|
||||
|
||||
# Check for NodeJS and NPM and fetch their current versions
|
||||
$npmErr = $(
|
||||
$npmDetails = npm version --json
|
||||
) 2>&1
|
||||
# Check for NodeJS and NPM and fetch their current versions
|
||||
$npmErr = $(
|
||||
$npmDetails = npm version --json
|
||||
) 2>&1
|
||||
|
||||
# Extract NodeJs and NPM versions and exit if either is not detected
|
||||
if($null -eq $npmErr) {
|
||||
$npmDetailsJson = [string]$npmDetails | ConvertFrom-Json
|
||||
# Extract NodeJs and NPM versions and exit if either is not detected
|
||||
if($null -eq $npmErr) {
|
||||
$npmDetailsJson = [string]$npmDetails | ConvertFrom-Json
|
||||
|
||||
$npmVersion = [version]$npmDetailsJson.npm
|
||||
$nodeVersion = [version]$npmDetailsJson.node
|
||||
} else {
|
||||
Write-Host "ERROR: NodeJS not detected!" -ForegroundColor red
|
||||
Write-Host "ERROR: NodeJS is required to build the Azure IPAM code when automatic updates are disabled!" -ForegroundColor red
|
||||
exit
|
||||
$npmVersion = [version]$npmDetailsJson.npm
|
||||
$nodeVersion = [version]$npmDetailsJson.node
|
||||
} else {
|
||||
Write-Host "ERROR: NodeJS not detected!" -ForegroundColor red
|
||||
Write-Host "ERROR: NodeJS is required to build the Azure IPAM code package!" -ForegroundColor red
|
||||
exit
|
||||
}
|
||||
|
||||
# Check for required NodeJS version
|
||||
if($nodeVersion -lt $MIN_NODE_VERSION) {
|
||||
Write-Host "ERROR: NodeJS must be version $MIN_NODE_VERSION or greater!" -ForegroundColor red
|
||||
}
|
||||
|
||||
# Check for required NPM version
|
||||
if($npmVersion -lt $MIN_NPM_VERSION) {
|
||||
Write-Host "ERROR: NPM must be version $MIN_NPM_VERSION or greater!" -ForegroundColor red
|
||||
}
|
||||
|
||||
# Exit if NodeJS or NPM versions do not meet the minimum version requirements
|
||||
if(($nodeVersion -lt $MIN_NODE_VERSION) -or ($npmVersion -lt $MIN_NPM_VERSION)) {
|
||||
exit
|
||||
}
|
||||
|
||||
Write-Host "INFO: Building application creating ZIP Deploy package" -ForegroundColor Green
|
||||
|
||||
# Create path to UI dir from script file location
|
||||
$uiDir = Join-Path -Path $ROOT_DIR -ChildPath "ui"
|
||||
|
||||
# Switch to UI dir for build process
|
||||
Push-Location -Path $uiDir
|
||||
|
||||
Write-Host "INFO: Running NPM Build..." -ForegroundColor Green
|
||||
|
||||
# Build Azure IPAM UI
|
||||
$npmBuildErr = $(
|
||||
$npmBuild = npm run build --no-update-notifier
|
||||
) 2>&1
|
||||
|
||||
# Switch back to original dir
|
||||
Pop-Location
|
||||
|
||||
# Create the Azure IPAM ZIP Deploy archive if NPM Build was successful
|
||||
if(-not $npmBuildErr) {
|
||||
Write-Host "INFO: Creating ZIP Deploy archive..." -ForegroundColor Green
|
||||
|
||||
$FilePath = Join-Path -Path $Path -ChildPath $FileName
|
||||
|
||||
Compress-Archive -Path ..\engine\app -DestinationPath $FilePath -Force
|
||||
Compress-Archive -Path ..\engine\requirements.txt -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\scripts\* -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\ipam-func -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\ipam-sentinel -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\host.json -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\ui\dist -DestinationPath $FilePath -Update
|
||||
} else {
|
||||
Write-Host "ERROR: Cannot create ZIP Deploy archive!" -ForegroundColor red
|
||||
throw $npmBuildErr
|
||||
}
|
||||
|
||||
Write-Host "INFO: Azure IPAM Zip Deploy archive successfully created" -ForegroundColor Green
|
||||
|
||||
$fullPath = (Resolve-Path -Path $FilePath).Path
|
||||
|
||||
Write-Host
|
||||
Write-Host "ZIP Asset Path: $fullPath" -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
# Check for required NodeJS version
|
||||
if($nodeVersion -lt $MIN_NODE_VERSION) {
|
||||
Write-Host "ERROR: NodeJS must be version $MIN_NODE_VERSION or greater!" -ForegroundColor red
|
||||
catch {
|
||||
$_ | Out-File -FilePath $buildLog -Append
|
||||
Write-Host "ERROR: Unable to build Azure IPAM Zip assets due to an exception, see log for detailed information!" -ForegroundColor red
|
||||
Write-Host "Build Log: $buildLog" -ForegroundColor Red
|
||||
}
|
||||
|
||||
# Check for required NPM version
|
||||
if($npmVersion -lt $MIN_NPM_VERSION) {
|
||||
Write-Host "ERROR: NPM must be version $MIN_NPM_VERSION or greater!" -ForegroundColor red
|
||||
finally {
|
||||
Write-Host
|
||||
Stop-Transcript | Out-Null
|
||||
}
|
||||
|
||||
# Exit if NodeJS or NPM versions do not meet the minimum version requirements
|
||||
if(($nodeVersion -lt $MIN_NODE_VERSION) -or ($npmVersion -lt $MIN_NPM_VERSION)) {
|
||||
exit
|
||||
}
|
||||
|
||||
Write-Host "INFO: Building application code and pushing via ZIP Deploy" -ForegroundColor Green
|
||||
Write-Verbose -Message "Building application code and pushing via ZIP Deploy"
|
||||
|
||||
Write-Host "INFO: Running NPM Build..." -ForegroundColor Green
|
||||
Write-Verbose -Message "INFO: Running NPM Build..."
|
||||
|
||||
# Create path to UI dir from script file location
|
||||
$scriptPath = $MyInvocation.MyCommand.Path
|
||||
$parentDir = (Get-Item $scriptPath).Directory.Parent.FullName
|
||||
$uiDir = Join-Path $parentDir "ui"
|
||||
|
||||
# Switch to UI dir for build process
|
||||
Push-Location -Path $uiDir
|
||||
|
||||
# Build Azure IPAM UI
|
||||
$npmBuildErr = $(
|
||||
$npmBuild = npm run build --no-update-notifier
|
||||
) 2>&1
|
||||
|
||||
# Switch back to original dir
|
||||
Pop-Location
|
||||
|
||||
# Create the Azure IPAM ZIP Deploy archive if NPM Build was successful
|
||||
if(-not $npmBuildErr) {
|
||||
Write-Host "INFO: Creating ZIP Deploy archive..." -ForegroundColor Green
|
||||
Write-Verbose -Message "INFO: Creating ZIP Deploy archive..."
|
||||
|
||||
$FilePath = Join-Path -Path $Path -ChildPath $FileName
|
||||
|
||||
Compress-Archive -Path ..\engine\app -DestinationPath $FilePath -Force
|
||||
Compress-Archive -Path ..\engine\requirements.txt -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\scripts\* -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\ipam-func -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\ipam-sentinel -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\engine\host.json -DestinationPath $FilePath -Update
|
||||
Compress-Archive -Path ..\ui\dist -DestinationPath $FilePath -Update
|
||||
} else {
|
||||
Write-Host "ERROR: Cannot create ZIP Deploy archive!" -ForegroundColor red
|
||||
throw $npmBuildErr
|
||||
}
|
||||
|
||||
Write-Host "INFO: Azure IPAM Zip Deploy archive successfully created" -ForegroundColor Green
|
||||
Write-Verbose -Message "Azure IPAM Zip Deploy archive successfully created"
|
||||
|
||||
Write-Host ""
|
||||
Stop-Transcript | Out-Null
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
###############################################################################################################
|
||||
##
|
||||
## Azure IPAM ZIP Deploy Updater Script
|
||||
##
|
||||
###############################################################################################################
|
||||
|
||||
# Set minimum version requirements
|
||||
#Requires -Version 7.2
|
||||
|
||||
# Intake and set global parameters
|
||||
param(
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $true)]
|
||||
[string]
|
||||
$AppName,
|
||||
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $true)]
|
||||
[string]
|
||||
$ResourceGroupName
|
||||
)
|
||||
|
||||
# Root Directory
|
||||
$ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName
|
||||
|
||||
# Set preference variables
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Set Log File Location
|
||||
$logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs"
|
||||
New-Item -ItemType Directory -Path $logpath -Force | Out-Null
|
||||
|
||||
$updateLog = Join-Path -Path $logPath -ChildPath "update_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
|
||||
Start-Transcript -Path $updateLog | Out-Null
|
||||
|
||||
try {
|
||||
Write-Host
|
||||
Write-Host "INFO: Uploading ZIP Deploy archive..." -ForegroundColor Green
|
||||
|
||||
$zipPath = Join-Path -Path $ROOT_DIR -ChildPath 'assets' -AdditionalChildPath "ipam.zip"
|
||||
|
||||
$publishRetries = 5
|
||||
$publishSuccess = $False
|
||||
|
||||
do {
|
||||
try {
|
||||
Publish-AzWebApp -ResourceGroupName $ResourceGroupName -Name $AppName -ArchivePath $zipPath -Restart -Force | Out-Null
|
||||
$publishSuccess = $True
|
||||
Write-Host "INFO: ZIP Deploy archive successfully uploaded" -ForegroundColor Green
|
||||
} catch {
|
||||
if($publishRetries -gt 0) {
|
||||
Write-Host "WARNING: Problem while uploading ZIP Deploy archive! Retrying..." -ForegroundColor DarkYellow
|
||||
$publishRetries--
|
||||
} else {
|
||||
Write-Host "ERROR: Unable to upload ZIP Deploy archive!" -ForegroundColor Red
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
} while ($publishSuccess -eq $False -and $publishRetries -gt 0)
|
||||
}
|
||||
catch {
|
||||
$_ | Out-File -FilePath $updateLog -Append
|
||||
Write-Host "ERROR: Unable to push Azure IPAM ZIP Deploy update due to an exception, see log for detailed information!" -ForegroundColor red
|
||||
Write-Host "Update Log: $updateLog" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
Write-Host
|
||||
Stop-Transcript | Out-Null
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
###############################################################################################################
|
||||
##
|
||||
## Azure IPAM Version Update Script
|
||||
##
|
||||
###############################################################################################################
|
||||
|
||||
# Set minimum version requirements
|
||||
#Requires -Version 7.2
|
||||
|
||||
# Intake and set global parameters
|
||||
[CmdletBinding(DefaultParameterSetName = 'Explicit')]
|
||||
param(
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $true,
|
||||
ParameterSetName = 'Explicit')]
|
||||
[System.Version]
|
||||
$Version,
|
||||
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $false,
|
||||
ParameterSetName = 'Implicit')]
|
||||
[switch]
|
||||
$BumpMajor,
|
||||
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $false,
|
||||
ParameterSetName = 'Implicit')]
|
||||
[switch]
|
||||
$BumpMinor,
|
||||
|
||||
[Parameter(ValueFromPipelineByPropertyName = $true,
|
||||
Mandatory = $false,
|
||||
ParameterSetName = 'Implicit')]
|
||||
[switch]
|
||||
$BumpBuild
|
||||
)
|
||||
|
||||
# Root Directory
|
||||
$ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName
|
||||
|
||||
# Set preference variables
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Set Log File Location
|
||||
$logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs"
|
||||
New-Item -ItemType Directory -Path $logpath -Force | Out-Null
|
||||
|
||||
$versionLog = Join-Path -Path $logPath -ChildPath "version_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
|
||||
Start-Transcript -Path $versionLog | Out-Null
|
||||
|
||||
try {
|
||||
Write-Host
|
||||
Write-Host "NOTE: Version Update Type: $($PSCmdlet.ParameterSetName)" -ForegroundColor Magenta
|
||||
|
||||
# Create path to adjacent directories from script file location
|
||||
$scriptPath = $MyInvocation.MyCommand.Path
|
||||
$parentDir = (Get-Item $scriptPath).Directory.Parent.FullName
|
||||
$uiDir = Join-Path -Path $parentDir -ChildPath "ui"
|
||||
$engineDir = Join-Path -Path $parentDir -ChildPath "engine" -AdditionalChildPath "app"
|
||||
$docsDir = Join-Path -Path $parentDir -ChildPath "docs"
|
||||
|
||||
Write-Host "INFO: Reading version from UI package.json file" -ForegroundColor Green
|
||||
|
||||
# Read version from UI package.json
|
||||
$packageJsonFile = Join-Path -Path $uiDir -ChildPath "package.json"
|
||||
$packageJsonContent = Get-Content -Path $packageJsonFile | ConvertFrom-Json
|
||||
$packageJsonVersion = $packageJsonContent.version
|
||||
|
||||
Write-Host "INFO: Reading version from Engine version.json file" -ForegroundColor Green
|
||||
|
||||
# Read version from Engine version.json
|
||||
$engineVersionFile = Join-Path -Path $engineDir -ChildPath "version.json"
|
||||
$engineVersionContent = Get-Content -Path $engineVersionFile | ConvertFrom-Json
|
||||
$engineVersion = $engineVersionContent.version
|
||||
|
||||
Write-Host "INFO: Reading version from Docs _coverpage.md file" -ForegroundColor Green
|
||||
|
||||
# Read version from Docs coverpage.md
|
||||
$versionPattern = "(?<=<small>).*(?=<\/small>)"
|
||||
$coverpageFile = Join-Path -Path $docsDir -ChildPath "_coverpage.md"
|
||||
$coverpageContent = Get-Content -Path $coverpageFile
|
||||
$coverpageVersion = ($coverpageContent | Select-String $versionPattern).Matches.Groups[0].Value
|
||||
|
||||
if ($PSCmdlet.ParameterSetName -eq 'Explicit') {
|
||||
$updatedVersion = "{0}.{1}.{2}" -f $Version.Major, $Version.Minor, $Version.Build
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ParameterSetName -eq 'Implicit') {
|
||||
Write-Host "INFO: Calculating new version number" -ForegroundColor Green
|
||||
|
||||
$currentVersions = @($packageJsonVersion, $engineVersion, $coverpageVersion)
|
||||
$notEqual = $currentVersions -ne $currentVersions[0]
|
||||
|
||||
if($notEqual) {
|
||||
Write-Host "ERROR: The current versions do not match" -ForegroundColor Red
|
||||
Write-Host "ERROR: UI: $packageJsonVersion" -ForegroundColor Red
|
||||
Write-Host "ERROR: Engine: $engineVersion" -ForegroundColor Red
|
||||
Write-Host "ERROR: Docs: $coverpageVersion" -ForegroundColor Red
|
||||
Write-Host "ERROR: Cannot implicitly bump versions" -ForegroundColor Red
|
||||
|
||||
throw [System.ArgumentException]::New("Current file versions do not match.")
|
||||
} else {
|
||||
$currVer = [System.Version]$currentVersions[0]
|
||||
}
|
||||
|
||||
$majorVersion = $currVer.Major
|
||||
$minorVersion = $currVer.Minor
|
||||
$buildVersion = $currVer.Build
|
||||
|
||||
if($BumpMajor) {
|
||||
$majorVersion += 1
|
||||
}
|
||||
|
||||
if($BumpMinor) {
|
||||
$minorVersion += 1
|
||||
}
|
||||
|
||||
if($BumpBuild) {
|
||||
$buildVersion += 1
|
||||
}
|
||||
|
||||
$updatedVersion = "{0}.{1}.{2}" -f $majorVersion, $minorVersion, $buildVersion
|
||||
}
|
||||
|
||||
Write-Host "INFO: Updating version for UI package.json file" -ForegroundColor Green
|
||||
|
||||
# Update version for UI package.json
|
||||
$packageJsonContent.version = $updatedVersion
|
||||
$packageJsonContent | ConvertTo-Json | Set-Content -Path $packageJsonFile
|
||||
|
||||
Write-Host "INFO: Updating version for Engine version.json file" -ForegroundColor Green
|
||||
|
||||
# Update version for Engine version.json
|
||||
$engineVersionContent.version = $updatedVersion
|
||||
$engineVersionContent | ConvertTo-Json | Set-Content -Path $engineVersionFile
|
||||
|
||||
Write-Host "INFO: Updating version for Docs coverpage.md file" -ForegroundColor Green
|
||||
|
||||
# Update version for Docs coverpage.md
|
||||
$coverpageContent = $coverpageContent -replace $versionPattern, $updatedVersion
|
||||
$coverpageContent | Set-Content -Path $coverpageFile
|
||||
|
||||
Write-Host "INFO: Azure IPAM versions successfully updated" -ForegroundColor Green
|
||||
Write-Host
|
||||
Write-Host "Updated Version -> v$updatedVersion" -ForegroundColor Yellow
|
||||
}
|
||||
catch {
|
||||
$_ | Out-File -FilePath $versionLog -Append
|
||||
Write-Host "ERROR: Unable to update Azure IPAM component versions due to an exception, see log for detailed information!" -ForegroundColor red
|
||||
Write-Host "Version Log: $versionLog" -ForegroundColor Red
|
||||
}
|
||||
finally {
|
||||
Write-Host
|
||||
Stop-Transcript | Out-Null
|
||||
}
|
|
@ -89,10 +89,10 @@ resource appService 'Microsoft.Web/sites@2021-02-01' = {
|
|||
name: 'COSMOS_URL'
|
||||
value: cosmosDbUri
|
||||
}
|
||||
{
|
||||
name: 'COSMOS_KEY'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/COSMOS-KEY/)'
|
||||
}
|
||||
// {
|
||||
// name: 'COSMOS_KEY'
|
||||
// value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/COSMOS-KEY/)'
|
||||
// }
|
||||
{
|
||||
name: 'DATABASE_NAME'
|
||||
value: databaseName
|
||||
|
@ -101,6 +101,10 @@ resource appService 'Microsoft.Web/sites@2021-02-01' = {
|
|||
name: 'CONTAINER_NAME'
|
||||
value: containerName
|
||||
}
|
||||
{
|
||||
name: 'MANAGED_IDENTITY_ID'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/IDENTITY-ID/)'
|
||||
}
|
||||
{
|
||||
name: 'UI_APP_ID'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/UI-ID/)'
|
||||
|
|
|
@ -16,6 +16,13 @@ param location string = resourceGroup().location
|
|||
@description('Log Analytics Workspace ID')
|
||||
param workspaceId string
|
||||
|
||||
@description('Managed Identity PrincipalId')
|
||||
param principalId string
|
||||
|
||||
var dbContributor = '00000000-0000-0000-0000-000000000002'
|
||||
var dbContributorId = '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccount.name}/sqlRoleDefinitions/${dbContributor}'
|
||||
var dbContributorRoleAssignmentId = guid(dbContributor, principalId, cosmosAccount.id)
|
||||
|
||||
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2021-04-15' = {
|
||||
name: cosmosAccountName
|
||||
location: location
|
||||
|
@ -32,6 +39,7 @@ resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2021-04-15' = {
|
|||
]
|
||||
databaseAccountOfferType: 'Standard'
|
||||
enableAutomaticFailover: true
|
||||
disableKeyBasedMetadataWriteAccess: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,4 +158,14 @@ resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-pr
|
|||
}
|
||||
}
|
||||
|
||||
resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = {
|
||||
name: dbContributorRoleAssignmentId
|
||||
parent: cosmosAccount
|
||||
properties: {
|
||||
roleDefinitionId: dbContributorId
|
||||
principalId: principalId
|
||||
scope: cosmosAccount.id
|
||||
}
|
||||
}
|
||||
|
||||
output cosmosDocumentEndpoint string = cosmosAccount.properties.documentEndpoint
|
||||
|
|
|
@ -218,25 +218,21 @@ DynamicParam {
|
|||
$validators.Remove('appServiceName')
|
||||
}
|
||||
|
||||
$attrFull = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrFull.ParameterSetName = 'ResourceNames'
|
||||
$attrFull.ParameterSetName = "App"
|
||||
$attrFull.Mandatory = $false
|
||||
$attrApp = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrApp.ParameterSetName = "App"
|
||||
$attrApp.Mandatory = $false
|
||||
|
||||
$attrTemplateOnly = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrTemplateOnly.ParameterSetName = 'ResourceNames'
|
||||
$attrTemplateOnly.ParameterSetName = "AppContainer"
|
||||
$attrTemplateOnly.Mandatory = $false
|
||||
$attrAppContainer = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrAppContainer.ParameterSetName = "AppContainer"
|
||||
$attrAppContainer.Mandatory = $false
|
||||
|
||||
$attrFunction = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrFunction.ParameterSetName = 'ResourceNames'
|
||||
$attrFunction.ParameterSetName = "Function"
|
||||
$attrFunction.Mandatory = $false
|
||||
|
||||
$attrFunction = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrFunction.ParameterSetName = 'ResourceNames'
|
||||
$attrFunction.ParameterSetName = "FunctionContainer"
|
||||
$attrFunction.Mandatory = $false
|
||||
$attrFunctionContainer = [System.Management.Automation.ParameterAttribute]::new()
|
||||
$attrFunctionContainer.ParameterSetName = "FunctionContainer"
|
||||
$attrFunctionContainer.Mandatory = $false
|
||||
|
||||
$attrValidation = [System.Management.Automation.ValidateScriptAttribute]::new({
|
||||
$invalidFields = [System.Collections.ArrayList]@()
|
||||
|
@ -276,9 +272,10 @@ DynamicParam {
|
|||
})
|
||||
|
||||
$attributeCollection = [System.Collections.ObjectModel.Collection[System.Attribute]]::new()
|
||||
$attributeCollection.Add($attrFull)
|
||||
$attributeCollection.Add($attrTemplateOnly)
|
||||
$attributeCollection.Add($attrApp)
|
||||
$attributeCollection.Add($attrAppContainer)
|
||||
$attributeCollection.Add($attrFunction)
|
||||
$attributeCollection.Add($attrFunctionContainer)
|
||||
$attributeCollection.Add($attrValidation)
|
||||
|
||||
$param = [System.Management.Automation.RuntimeDefinedParameter]::new('ResourceNames', [hashtable], $attributeCollection)
|
||||
|
@ -298,8 +295,13 @@ process {
|
|||
AzureChinaCloud = "AZURE_CHINA"
|
||||
}
|
||||
|
||||
# Root Directory
|
||||
$ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName
|
||||
|
||||
# Minimum Required Azure CLI Version
|
||||
$MIN_AZ_CLI_VER = [System.Version]'2.35.0'
|
||||
|
||||
# Check for Debug Flag
|
||||
$DEBUG_MODE = [bool]$PSCmdlet.MyInvocation.BoundParameters[“Debug”].IsPresent
|
||||
|
||||
# Set preference variables
|
||||
|
@ -311,8 +313,8 @@ process {
|
|||
$Env:SuppressAzurePowerShellBreakingChangeWarnings = $true
|
||||
|
||||
# Set Log File Location
|
||||
$logPath = [Io.Path]::Combine('..', 'logs')
|
||||
New-Item -ItemType Directory -Force -Path $logpath | Out-Null
|
||||
$logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs"
|
||||
New-Item -ItemType Directory -Path $logpath -Force| Out-Null
|
||||
|
||||
$debugLog = Join-Path -Path $logPath -ChildPath "debug_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
$errorLog = Join-Path -Path $logPath -ChildPath "error_$(get-date -format `"yyyyMMddhhmmsstt`").log"
|
||||
|
@ -387,7 +389,7 @@ process {
|
|||
# Create IPAM UI Application (If -UI:$false not specified)
|
||||
if (-not $DisableUI) {
|
||||
Write-Host "INFO: Creating Azure IPAM UI Application" -ForegroundColor Green
|
||||
Write-Verbose -Message "Creating Azure IPAM UI Application"
|
||||
|
||||
$uiApp = New-AzADApplication `
|
||||
-DisplayName $UiAppName `
|
||||
-SPARedirectUri "https://replace-this-value.azurewebsites.net" `
|
||||
|
@ -396,20 +398,20 @@ process {
|
|||
|
||||
$engineResourceMap = @{
|
||||
"AZURE_PUBLIC" = @{
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033")
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013" # Azure Service Management
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033") # user_impersonation
|
||||
}
|
||||
"AZURE_US_GOV" = @{
|
||||
ResourceAppId = "40a69793-8fe6-4db1-9591-dbc5c57b17d8"
|
||||
ResourceAccessIds = @("8eb49ffc-05ac-454c-9027-8648349217dd", "e59ee429-1fb1-4054-b99f-f542e8dc9b95")
|
||||
ResourceAppId = "40a69793-8fe6-4db1-9591-dbc5c57b17d8" # Azure Service Management
|
||||
ResourceAccessIds = @("8eb49ffc-05ac-454c-9027-8648349217dd", "e59ee429-1fb1-4054-b99f-f542e8dc9b95") # user_impersonation
|
||||
}
|
||||
"AZURE_GERMANY" = @{
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033")
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013" # Azure Service Management
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033") # user_impersonation
|
||||
}
|
||||
"AZURE_CHINA" = @{
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013"
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033")
|
||||
ResourceAppId = "797f4846-ba00-4fd7-ba43-dac1f8f63013" # Azure Service Management
|
||||
ResourceAccessIds = @("41094075-9dad-400e-a0bd-54e686782033") # user_impersonation
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,17 +471,17 @@ process {
|
|||
$engineApiSettings.Add("KnownClientApplication", $knownClientApplication)
|
||||
}
|
||||
|
||||
# Create IPAM Engine Application
|
||||
Write-Host "INFO: Creating Azure IPAM Engine Application" -ForegroundColor Green
|
||||
Write-Verbose -Message "Creating Azure IPAM Engine Application"
|
||||
|
||||
# Create IPAM Engine Application
|
||||
$engineApp = New-AzADApplication `
|
||||
-DisplayName $EngineAppName `
|
||||
-Api $engineApiSettings `
|
||||
-RequiredResourceAccess $engineResourceAccessList
|
||||
|
||||
# Update IPAM Engine API Endpoint
|
||||
Write-Host "INFO: Updating Azure IPAM Engine API Endpoint" -ForegroundColor Green
|
||||
Write-Verbose -Message "Updating Azure IPAM Engine API Endpoint"
|
||||
|
||||
# Update IPAM Engine API Endpoint
|
||||
Update-AzADApplication -ApplicationId $engineApp.AppId -IdentifierUri "api://$($engineApp.AppId)"
|
||||
|
||||
$uiEngineApiAccess =@{
|
||||
|
@ -497,7 +499,7 @@ process {
|
|||
# Update IPAM UI Application Resource Access (If -UI:$false not specified)
|
||||
if (-not $DisableUI) {
|
||||
Write-Host "INFO: Updating Azure IPAM UI Application Resource Access" -ForegroundColor Green
|
||||
Write-Verbose -Message "Updating Azure IPAM UI Application Resource Access"
|
||||
|
||||
Update-AzADApplication -ApplicationId $uiApp.AppId -RequiredResourceAccess $uiResourceAccess
|
||||
|
||||
$uiObject = Get-AzADApplication -ApplicationId $uiApp.AppId
|
||||
|
@ -508,31 +510,29 @@ process {
|
|||
# Create IPAM UI Service Principal (If -UI:$false not specified)
|
||||
if (-not $DisableUI) {
|
||||
Write-Host "INFO: Creating Azure IPAM UI Service Principal" -ForegroundColor Green
|
||||
Write-Verbose -Message "Creating Azure IPAM UI Service Principal"
|
||||
|
||||
New-AzADServicePrincipal -ApplicationObject $uiObject | Out-Null
|
||||
}
|
||||
|
||||
$scope = "/providers/Microsoft.Management/managementGroups/$TenantId"
|
||||
|
||||
# Create IPAM Engine Service Principal
|
||||
Write-Host "INFO: Creating Azure IPAM Engine Service Principal" -ForegroundColor Green
|
||||
Write-Verbose -Message "Creating Azure IPAM Engine Service Principal"
|
||||
|
||||
# Create IPAM Engine Service Principal
|
||||
New-AzADServicePrincipal -ApplicationObject $engineObject `
|
||||
-Role "Reader" `
|
||||
-Scope $scope `
|
||||
| Out-Null
|
||||
|
||||
# Create IPAM Engine Secret
|
||||
Write-Host "INFO: Creating Azure IPAM Engine Secret" -ForegroundColor Green
|
||||
Write-Verbose -Message "Creating Azure IPAM Engine Secret"
|
||||
|
||||
# Create IPAM Engine Secret
|
||||
$engineSecret = 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
|
||||
Write-Verbose -Message "Azure IPAM Engine & UI Applications/Service Principals created successfully"
|
||||
} else {
|
||||
Write-Host "INFO: Azure IPAM Engine Application/Service Principal created successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Azure IPAM Engine Application/Service Principal created successfully"
|
||||
}
|
||||
|
||||
$appDetails = @{
|
||||
|
@ -603,9 +603,9 @@ process {
|
|||
$accesstoken = ConvertTo-SecureString $accesstoken -AsPlainText -Force
|
||||
}
|
||||
|
||||
# Connect to Microsoft Graph
|
||||
Write-Host "INFO: Logging in to Microsoft Graph" -ForegroundColor Green
|
||||
Write-Verbose -Message "Logging in to Microsoft Graph"
|
||||
|
||||
# Connect to Microsoft Graph
|
||||
Connect-MgGraph -Environment $msGraphMap[$AzureCloud].Environment -AccessToken $accesstoken | Out-Null
|
||||
|
||||
# Fetch Azure IPAM UI Service Principal (If -UI:$false not specified)
|
||||
|
@ -621,7 +621,7 @@ process {
|
|||
# Grant admin consent for Microsoft Graph API permissions assigned to IPAM UI application (If -UI:$false not specified)
|
||||
if (-not $DisableUI) {
|
||||
Write-Host "INFO: Granting admin consent for Microsoft Graph API permissions assigned to IPAM UI application" -ForegroundColor Green
|
||||
Write-Verbose -Message "Granting admin consent for Microsoft Graph API permissions assigned to IPAM UI application"
|
||||
|
||||
foreach($scope in $uiGraphScopes) {
|
||||
$msGraphId = Get-AzADServicePrincipal `
|
||||
-ApplicationId $scope.scopeId
|
||||
|
@ -635,13 +635,12 @@ process {
|
|||
}
|
||||
|
||||
Write-Host "INFO: Admin consent for Microsoft Graph API permissions granted successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Admin consent for Microsoft Graph API permissions granted successfully"
|
||||
}
|
||||
|
||||
# Grant admin consent to the IPAM UI application for exposed API from the IPAM Engine application (If -UI:$false not specified)
|
||||
if (-not $DisableUI) {
|
||||
Write-Host "INFO: Granting admin consent to the IPAM UI application for exposed API from the IPAM Engine application" -ForegroundColor Green
|
||||
Write-Verbose -Message "Granting admin consent to the IPAM UI application for exposed API from the IPAM Engine application"
|
||||
|
||||
New-MgOauth2PermissionGrant `
|
||||
-ResourceId $engineSpn.Id `
|
||||
-Scope "access_as_user" `
|
||||
|
@ -650,12 +649,11 @@ process {
|
|||
| Out-Null
|
||||
|
||||
Write-Host "INFO: Admin consent for IPAM Engine exposed API granted successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Admin consent for IPAM Engine exposed API granted successfully"
|
||||
}
|
||||
|
||||
# Grant admin consent for Azure Service Management API permissions assigned to IPAM Engine application
|
||||
Write-Host "INFO: Granting admin consent for Azure Service Management API permissions assigned to IPAM Engine application" -ForegroundColor Green
|
||||
Write-Verbose -Message "Granting admin consent for Azure Service Management API permissions assigned to IPAM Engine application"
|
||||
|
||||
# Grant admin consent for Azure Service Management API permissions assigned to IPAM Engine application
|
||||
foreach($scope in $engineGraphScopes) {
|
||||
$msGraphId = Get-AzADServicePrincipal `
|
||||
-ApplicationId $scope.scopeId
|
||||
|
@ -669,7 +667,6 @@ process {
|
|||
}
|
||||
|
||||
Write-Host "INFO: Admin consent for Azure Service Management API permissions granted successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Admin consent for Azure Service Management API permissions granted successfully"
|
||||
}
|
||||
|
||||
Function Save-Parameters {
|
||||
|
@ -685,7 +682,6 @@ process {
|
|||
)
|
||||
|
||||
Write-Host "INFO: Populating Bicep parameter file for infrastructure deployment" -ForegroundColor Green
|
||||
Write-Verbose -Message "Populating Bicep parameter file for infrastructure deployment"
|
||||
|
||||
# Retrieve JSON object from sample parameter file
|
||||
$parametersObject = Get-Content main.parameters.example.json | ConvertFrom-Json
|
||||
|
@ -705,7 +701,6 @@ process {
|
|||
$parametersObject | ConvertTo-Json -Depth 4 | Out-File -FilePath main.parameters.json
|
||||
|
||||
Write-Host "INFO: Bicep parameter file populated successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Bicep parameter file populated successfully"
|
||||
}
|
||||
|
||||
Function Import-Parameters {
|
||||
|
@ -715,7 +710,6 @@ process {
|
|||
)
|
||||
|
||||
Write-Host "INFO: Importing values from Bicep parameters file" -ForegroundColor Green
|
||||
Write-Verbose -Message "Importing values from Bicep parameters file"
|
||||
|
||||
# Retrieve JSON object from sample parameter file
|
||||
$parametersObject = Get-Content $ParameterFile | ConvertFrom-Json
|
||||
|
@ -742,7 +736,6 @@ process {
|
|||
# $deployType = $script:AsFunction ? 'Function' : 'Full'
|
||||
|
||||
Write-Host "INFO: Successfully import Bicep parameter values for deployment" -ForegroundColor Green
|
||||
Write-Verbose -Message "Successfully import Bicep parameter values for deployment"
|
||||
|
||||
$appDetails = @{
|
||||
UIAppId = $UIAppId
|
||||
|
@ -778,7 +771,6 @@ process {
|
|||
)
|
||||
|
||||
Write-Host "INFO: Deploying IPAM bicep templates" -ForegroundColor Green
|
||||
Write-Verbose -Message "Deploying bicep templates"
|
||||
|
||||
# Instantiate deployment parameter object
|
||||
$deploymentParameters = @{
|
||||
|
@ -830,7 +822,6 @@ process {
|
|||
$DebugPreference = 'SilentlyContinue'
|
||||
|
||||
Write-Host "INFO: IPAM bicep templates deployed successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "IPAM bicep template deployed successfully"
|
||||
|
||||
return $deployment
|
||||
}
|
||||
|
@ -844,7 +835,6 @@ process {
|
|||
)
|
||||
|
||||
Write-Host "INFO: Updating UI Application with SPA configuration" -ForegroundColor Green
|
||||
Write-Verbose -Message "Updating UI Application with SPA configuration"
|
||||
|
||||
$appServiceEndpoint = "https://$Endpoint"
|
||||
|
||||
|
@ -852,7 +842,6 @@ process {
|
|||
Update-AzADApplication -ApplicationId $UIAppId -SPARedirectUri $appServiceEndpoint
|
||||
|
||||
Write-Host "INFO: UI Application SPA configuration update complete" -ForegroundColor Green
|
||||
Write-Verbose -Message "UI Application SPA configuration update complete"
|
||||
}
|
||||
|
||||
# Main Deployment Script Section
|
||||
|
@ -865,10 +854,9 @@ process {
|
|||
|
||||
try {
|
||||
if($PrivateAcr) {
|
||||
# Verify Minimum Azure CLI Version
|
||||
Write-Host "INFO: PrivateACR flag set, verifying minimum Azure CLI version" -ForegroundColor Green
|
||||
Write-Verbose -Message "PrivateACR flag set, verifying minimum Azure CLI version"
|
||||
|
||||
# Verify Minimum Azure CLI Version
|
||||
$azureCliVer = [System.Version](az version | ConvertFrom-Json).'azure-cli'
|
||||
|
||||
if($azureCliVer -lt $MIN_AZ_CLI_VER) {
|
||||
|
@ -876,10 +864,9 @@ process {
|
|||
exit
|
||||
}
|
||||
|
||||
# Verify Azure PowerShell and Azure CLI Contexts Match
|
||||
Write-Host "INFO: PrivateACR flag set, verifying Azure PowerShell and Azure CLI contexts match" -ForegroundColor Green
|
||||
Write-Verbose -Message "PrivateACR flag set, verifying Azure PowerShell and Azure CLI contexts match"
|
||||
|
||||
# Verify Azure PowerShell and Azure CLI Contexts Match
|
||||
$azureCliContext = $(az account show | ConvertFrom-Json) 2>$null
|
||||
|
||||
if(-not $azureCliContext) {
|
||||
|
@ -897,25 +884,23 @@ process {
|
|||
}
|
||||
|
||||
if ($PSCmdlet.ParameterSetName -in ('App', 'AppContainer', 'Function', 'FunctionContainer', 'AppsOnly')) {
|
||||
# Fetch Tenant ID
|
||||
Write-Host "INFO: Fetching Tenant ID from Azure PowerShell SDK" -ForegroundColor Green
|
||||
Write-Verbose -Message "Fetching Tenant ID from Azure PowerShell SDK"
|
||||
|
||||
# Fetch Tenant ID
|
||||
$tenantId = (Get-AzContext).Tenant.Id
|
||||
|
||||
# Fetch Azure Cloud Type
|
||||
Write-Host "INFO: Fetching Azure Cloud type from Azure PowerShell SDK" -ForegroundColor Green
|
||||
Write-Verbose -Message "Fetching Azure Cloud type from Azure PowerShell SDK"
|
||||
|
||||
# Fetch Azure Cloud Type
|
||||
$azureCloud = $AZURE_ENV_MAP[(Get-AzContext).Environment.Name]
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ParameterSetName -in ('App', 'AppContainer', 'Function', 'FunctionContainer')) {
|
||||
# Validate Azure Region
|
||||
Write-Host "INFO: Validating Azure Region selected for deployment" -ForegroundColor Green
|
||||
Write-Verbose -Message "Validating Azure Region selected for deployment"
|
||||
|
||||
# Validate Azure Region
|
||||
if (Test-Location -Location $Location) {
|
||||
Write-Host "INFO: Azure Region validated successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Azure Region validated successfully"
|
||||
} else {
|
||||
Write-Host "ERROR: Location provided is not a valid Azure Region!" -ForegroundColor red
|
||||
exit
|
||||
|
@ -969,9 +954,8 @@ process {
|
|||
|
||||
if ($PSCmdlet.ParameterSetName -in ('App', 'Function')) {
|
||||
Write-Host "INFO: Uploading ZIP Deploy archive..." -ForegroundColor Green
|
||||
Write-Verbose -Message "INFO: Uploading ZIP Deploy archive..."
|
||||
|
||||
$zipPath = Join-Path '../build' -ChildPath "ipam.zip"
|
||||
$zipPath = Join-Path -Path $ROOT_DIR -ChildPath 'assets' -AdditionalChildPath "ipam.zip"
|
||||
|
||||
$publishRetries = 5
|
||||
$publishSuccess = $False
|
||||
|
@ -980,6 +964,7 @@ process {
|
|||
try {
|
||||
Publish-AzWebApp -ResourceGroupName $deployment.Outputs["resourceGroupName"].Value -Name $deployment.Outputs["appServiceName"].Value -ArchivePath $zipPath -Restart -Force | Out-Null
|
||||
$publishSuccess = $True
|
||||
Write-Host "INFO: ZIP Deploy archive successfully uploaded" -ForegroundColor Green
|
||||
} catch {
|
||||
if($publishRetries -gt 0) {
|
||||
Write-Host "WARNING: Problem while uploading ZIP Deploy archive! Retrying..." -ForegroundColor DarkYellow
|
||||
|
@ -994,7 +979,6 @@ process {
|
|||
|
||||
if ($PSCmdlet.ParameterSetName -in ('AppContainer', 'FunctionContainer') -and $PrivateAcr) {
|
||||
Write-Host "INFO: Building and pushing container images to Azure Container Registry" -ForegroundColor Green
|
||||
Write-Verbose -Message "Building and pushing container images to Azure Container Registry"
|
||||
|
||||
$containerMap = @{
|
||||
Debian = @{
|
||||
|
@ -1015,39 +999,34 @@ process {
|
|||
}
|
||||
}
|
||||
|
||||
$dockerPath = [Io.Path]::Combine('..')
|
||||
$dockerFile = Join-Path -Path $dockerPath -ChildPath 'Dockerfile'
|
||||
$dockerFileFunc = Join-Path -Path $dockerPath -ChildPath 'Dockerfile.func'
|
||||
$dockerFile = Join-Path -Path $ROOT_DIR -ChildPath 'Dockerfile'
|
||||
$dockerFileFunc = Join-Path -Path $ROOT_DIR -ChildPath 'Dockerfile.func'
|
||||
|
||||
if($Function) {
|
||||
Write-Host "INFO: Building Function container..." -ForegroundColor Green
|
||||
Write-Verbose -Message "INFO: Building Function container..."
|
||||
|
||||
$funcBuildOutput = $(
|
||||
az acr build -r $deployment.Outputs["acrName"].Value `
|
||||
-t ipamfunc:latest `
|
||||
-f $dockerFileFunc $dockerPath
|
||||
-f $dockerFileFunc $ROOT_DIR
|
||||
) *>&1
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw $funcBuildOutput
|
||||
} else {
|
||||
Write-Host "INFO: Function container image build and push completed successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Function container image build and push completed successfully"
|
||||
}
|
||||
|
||||
Write-Host "INFO: Restarting Function App" -ForegroundColor Green
|
||||
Write-Verbose -Message "Restarting Function App"
|
||||
|
||||
Restart-AzFunctionApp -Name $deployment.Outputs["appServiceName"].Value -ResourceGroupName $deployment.Outputs["resourceGroupName"].Value -Force | Out-Null
|
||||
} else {
|
||||
Write-Host "INFO: Building App container ($ContainerType)..." -ForegroundColor Green
|
||||
Write-Verbose -Message "INFO: Building App container ($ContainerType)..."
|
||||
|
||||
$appBuildOutput = $(
|
||||
az acr build -r $deployment.Outputs["acrName"].Value `
|
||||
-t ipam:latest `
|
||||
-f $dockerFile $dockerPath `
|
||||
-f $dockerFile $ROOT_DIR `
|
||||
--build-arg PORT=$($containerMap[$ContainerType].Port) `
|
||||
--build-arg BUILD_IMAGE=$($containerMap[$ContainerType].Images.Build) `
|
||||
--build-arg SERVE_IMAGE=$($containerMap[$ContainerType].Images.Serve)
|
||||
|
@ -1057,18 +1036,15 @@ process {
|
|||
throw $appBuildOutput
|
||||
} else {
|
||||
Write-Host "INFO: App container image build and push completed successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "App container image build and push completed successfully"
|
||||
}
|
||||
|
||||
Write-Host "INFO: Restarting App Service" -ForegroundColor Green
|
||||
Write-Verbose -Message "Restarting App Service"
|
||||
|
||||
Restart-AzWebApp -Name $deployment.Outputs["appServiceName"].Value -ResourceGroupName $deployment.Outputs["resourceGroupName"].Value | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "INFO: Azure IPAM Solution deployed successfully" -ForegroundColor Green
|
||||
Write-Verbose -Message "Azure IPAM Solution deployed successfully"
|
||||
|
||||
if ($($PSCmdlet.ParameterSetName -notin 'AppsOnly') -and (-not $DisableUI) -and $ParameterFile) {
|
||||
$updateUrl = "https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Authentication/appId/$($appDetails.UIAppId)"
|
||||
|
|
|
@ -92,10 +92,10 @@ resource functionApp 'Microsoft.Web/sites@2021-03-01' = {
|
|||
name: 'COSMOS_URL'
|
||||
value: cosmosDbUri
|
||||
}
|
||||
{
|
||||
name: 'COSMOS_KEY'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/COSMOS-KEY/)'
|
||||
}
|
||||
// {
|
||||
// name: 'COSMOS_KEY'
|
||||
// value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/COSMOS-KEY/)'
|
||||
// }
|
||||
{
|
||||
name: 'DATABASE_NAME'
|
||||
value: databaseName
|
||||
|
@ -104,6 +104,10 @@ resource functionApp 'Microsoft.Web/sites@2021-03-01' = {
|
|||
name: 'CONTAINER_NAME'
|
||||
value: containerName
|
||||
}
|
||||
{
|
||||
name: 'MANAGED_IDENTITY_ID'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/IDENTITY-ID/)'
|
||||
}
|
||||
{
|
||||
name: 'UI_APP_ID'
|
||||
value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/UI-ID/)'
|
||||
|
|
|
@ -5,7 +5,10 @@ param keyVaultName string
|
|||
param location string = resourceGroup().location
|
||||
|
||||
@description('Managed Identity PrincipalId')
|
||||
param principalId string
|
||||
param identityPrincipalId string
|
||||
|
||||
@description('Managed Identity ClientId')
|
||||
param identityClientId string
|
||||
|
||||
@description('AzureAD TenantId')
|
||||
param tenantId string = subscription().tenantId
|
||||
|
@ -23,26 +26,17 @@ param engineAppSecret string
|
|||
@description('Log Analytics Worskpace ID')
|
||||
param workspaceId string
|
||||
|
||||
// KeyVault Secret Permissions Assigned to Managed Identity
|
||||
var secretsPermissions = [
|
||||
'get'
|
||||
]
|
||||
var keyVaultUser = '4633458b-17de-408a-b874-0445c86b69e6'
|
||||
var keyVaultUserId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultUser)
|
||||
var keyVaultUserRoleAssignmentId = guid(keyVaultUser, identityPrincipalId, keyVault.id)
|
||||
|
||||
resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = {
|
||||
name: keyVaultName
|
||||
location: location
|
||||
properties: {
|
||||
enablePurgeProtection: true
|
||||
enableRbacAuthorization: true
|
||||
tenantId: tenantId
|
||||
accessPolicies: [
|
||||
{
|
||||
objectId: principalId
|
||||
tenantId: tenantId
|
||||
permissions: {
|
||||
secrets: secretsPermissions
|
||||
}
|
||||
}
|
||||
]
|
||||
sku: {
|
||||
name: 'standard'
|
||||
family: 'A'
|
||||
|
@ -54,6 +48,14 @@ resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = {
|
|||
}
|
||||
}
|
||||
|
||||
resource identityId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
|
||||
parent: keyVault
|
||||
name: 'IDENTITY-ID'
|
||||
properties: {
|
||||
value: identityClientId
|
||||
}
|
||||
}
|
||||
|
||||
resource uiId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = {
|
||||
parent: keyVault
|
||||
name: 'UI-ID'
|
||||
|
@ -114,5 +116,15 @@ resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-pr
|
|||
}
|
||||
}
|
||||
|
||||
resource keyVaultUserAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
|
||||
name: keyVaultUserRoleAssignmentId
|
||||
scope: keyVault
|
||||
properties: {
|
||||
principalType: 'ServicePrincipal'
|
||||
roleDefinitionId: keyVaultUserId
|
||||
principalId: identityPrincipalId
|
||||
}
|
||||
}
|
||||
|
||||
output keyVaultName string = keyVault.name
|
||||
output keyVaultUri string = keyVault.properties.vaultUri
|
||||
|
|
|
@ -87,7 +87,8 @@ module keyVault 'keyVault.bicep' = {
|
|||
params: {
|
||||
location: location
|
||||
keyVaultName: resourceNames.keyVaultName
|
||||
principalId: managedIdentity.outputs.principalId
|
||||
identityPrincipalId: managedIdentity.outputs.principalId
|
||||
identityClientId: managedIdentity.outputs.clientId
|
||||
uiAppId: uiAppId
|
||||
engineAppId: engineAppId
|
||||
engineAppSecret: engineAppSecret
|
||||
|
@ -106,6 +107,7 @@ module cosmos 'cosmos.bicep' = {
|
|||
cosmosDatabaseName: resourceNames.cosmosDatabaseName
|
||||
keyVaultName: keyVault.outputs.keyVaultName
|
||||
workspaceId: logAnalyticsWorkspace.outputs.workspaceId
|
||||
principalId: managedIdentity.outputs.principalId
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
@description('Contributor Role Assignment GUID')
|
||||
param contributorAssignmentName string = newGuid()
|
||||
|
||||
@description('Deployment Location')
|
||||
param location string = resourceGroup().location
|
||||
|
||||
@description('Managed Identity Name')
|
||||
param managedIdentityName string
|
||||
|
||||
@description('Managed Identity Operator Role Assignment GUID')
|
||||
param managedIdentityOperatorAssignmentName string = newGuid()
|
||||
|
||||
var contributor = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
|
||||
var contributorId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', contributor)
|
||||
var contributorRoleAssignmentId = guid(contributor, managedIdentity.id, subscription().id)
|
||||
var managedIdentityOperator = 'f1a07417-d97a-45cb-824c-7a7467783830'
|
||||
var managedIdentityOperatorId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', managedIdentityOperator)
|
||||
var managedIdentityOperatorRoleAssignmentId = guid(managedIdentityOperator, managedIdentity.id, subscription().id)
|
||||
|
||||
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
|
||||
name: managedIdentityName
|
||||
|
@ -21,8 +17,7 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-
|
|||
}
|
||||
|
||||
resource contributorAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
|
||||
#disable-next-line use-stable-resource-identifiers
|
||||
name: contributorAssignmentName
|
||||
name: contributorRoleAssignmentId
|
||||
properties: {
|
||||
principalType: 'ServicePrincipal'
|
||||
roleDefinitionId: contributorId
|
||||
|
@ -31,8 +26,7 @@ resource contributorAssignment 'Microsoft.Authorization/roleAssignments@2020-04-
|
|||
}
|
||||
|
||||
resource managedIdentityOperatorAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
|
||||
#disable-next-line use-stable-resource-identifiers
|
||||
name: managedIdentityOperatorAssignmentName
|
||||
name: managedIdentityOperatorRoleAssignmentId
|
||||
properties: {
|
||||
principalType: 'ServicePrincipal'
|
||||
roleDefinitionId: managedIdentityOperatorId
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Welcome to IPAM!
|
||||
# Welcome to Azure IPAM!
|
||||
|
||||
<!--
|
||||
Guidelines on README format: https://review.docs.microsoft.com/help/onboard/admin/samples/concepts/readme-template?branch=master
|
||||
|
@ -9,51 +9,60 @@ Taxonomies for products and languages: https://review.docs.microsoft.com/new-hop
|
|||
-->
|
||||
|
||||
## Overview and Architecture
|
||||
IPAM was developed to give customers a simple, straightforward way to manage their IP address space in Azure. IPAM enables end-to-end planning, deploying, managing and monitoring of your IP address space, with an intuitive user experience. IPAM automatically discovers IP address utilization in your Azure tenant and enables you to manage it all from a centralized UI. You can also interface with IPAM programmatically via a RESTful API to facilitate IP address management at scale via Infrastructure as Code (IaC). IPAM is designed and architected based on the 5 pillars of the [Microsoft Azure Well Architected Framework](https://docs.microsoft.com/en-us/azure/architecture/framework/).
|
||||
Azure IPAM was developed to give customers a simple, straightforward way to manage their IP address space in Azure. It enables end-to-end planning, deploying, managing and monitoring of your IP address space, with an intuitive user experience. Additionally, it can automatically discover IP address utilization within your Azure tenant and enables you to manage it all from a centralized UI. You can also interface with the Azure IPAM service programmatically via a RESTful API to facilitate IP address management at scale via Infrastructure as Code (IaC) and CI/CD pipelines. Azure IPAM is designed and architected based on the 5 pillars of the [Microsoft Azure Well Architected Framework](https://docs.microsoft.com/en-us/azure/architecture/framework/).
|
||||
|
||||
| Full (App Service) | Function |
|
||||
| App Service | Function |
|
||||
:-----------------------------------------------------------------:|:---------------------------------------------------------------------------:
|
||||
| ![IPAM Architecture](./images/ipam_architecture_full.png ':size=70%') | ![IPAM Architecture](./images/ipam_architecture_function.png ':size=70%') |
|
||||
|
||||
## IPAM Infrastructure
|
||||
The IPAM solution is comprised of containers running on Azure App Services. IPAM can also be deployed in an API-only fashion with an Azure Function if no UI is required (e.g. pure IaC model). The containers are built and published to a public Azure Container Registry (ACR), but you may also choose to build your own containers and host them in a Private Container Registry. More details on this can be found in the [Deployment](./deployment/README.md) section. All of the supporting infrastructure is deployed and runs within your Azure Tenant, none of the resources are shared with other IPAM users (outside of the publicly hosted ACR).
|
||||
## Azure IPAM Infrastructure
|
||||
The Azure IPAM solution is delivered via a container running in Azure App Services or as an Azure Function. It can also be deployed in an API-only fashion if no UI is required (e.g. pure IaC model). The container is built and published to a public Azure Container Registry (ACR), but you may also choose to build your own container and host it in a Private Container Registry. More details on this can be found in the [Deployment](./deployment/README.md) section. All of the supporting infrastructure is deployed and runs within your Azure Tenant and none of the resources are shared with other IPAM users (outside of the publicly hosted ACR).
|
||||
|
||||
Here is a more specific breakdown of the components used:
|
||||
|
||||
- **App Registrations**
|
||||
- 2x App Registrations
|
||||
- *Engine* App Registration
|
||||
- Granted **reader** permission to the root management group to facilitate IPAM Admin operations (global visibility)
|
||||
- Granted **reader** permission to the [Root Management Group](https://learn.microsoft.com/en-us/azure/governance/management-groups/overview#root-management-group-for-each-directory) to facilitate IPAM Admin operations (global visibility)
|
||||
- Authentication point for IPAM API operations ([on-behalf-of](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow) flow)
|
||||
- *UI* App Registration
|
||||
- *UI* App Registration *(Optional if no UI is desired)*
|
||||
- Granted **read** permissions for Microsoft Graph API's
|
||||
- Added as a *known client application* for the *Engine* App Registration
|
||||
- Authentication point for the IPAM UI ([auth code](https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow) flow)
|
||||
- **Resource Group**
|
||||
- House all Azure infrastructure related resources
|
||||
- **App Service Plan with App Service** *(Full Deployment only)*
|
||||
- Run the IPAM Engine, UI, and Load Balancer containers as a multi-container App Service
|
||||
- **App Service Plan with Function App** *(Function Deployment only)*
|
||||
- Run IPAM Engine as an Azure Function
|
||||
- **Storage Account with Blob Container** *(Function Deployment only)*
|
||||
- This account stores the Function metadata
|
||||
- Contains all Azure IPAM deployed resources
|
||||
- **App Service Plan with App Service** *(AppContainer Deployment only)*
|
||||
- Runs the Azure IPAM solution as a container within App Services
|
||||
- **App Service Plan with Function App** *(FunctionContainer Deployment only)*
|
||||
- Runs the Azure IPAM solution as a container within Azure Functions
|
||||
- **Storage Account with Blob Container** *(FunctionContainer Deployment only)*
|
||||
- Storage for the Azure Function metadata
|
||||
- **Cosmos DB**
|
||||
- Backend NoSQL datastore for the IPAM application
|
||||
- **KeyVault**
|
||||
- Stores the following secrets:
|
||||
- App Registration application IDs and Secrets (Engine & UI)
|
||||
- Cosmos DB read-write key
|
||||
- Managed Identity ID
|
||||
- Azure Tenant ID
|
||||
- **User Assigned Managed Identity**
|
||||
- Assigned to the App Service to retrieve secrets from KeyVault and NGINX configuration data from the Storage Account
|
||||
- Assigned to the App Service to retrieve secrets from KeyVault
|
||||
|
||||
## How IPAM Works
|
||||
## How Azure IPAM Works
|
||||
|
||||
As mentioned above, the IPAM application is made up of two containers, one that runs the front end user interface, and the other that runs the backend engine. For the *Full* deployment, there is also a Load Balancer container running [NGINX](https://www.nginx.com/) for path-based routing. IPAM has been designed as such to accommodate the following use cases...
|
||||
Azure IPAM has been designed as such to radically simplify the often daunting task of IP address management within Azure and was built to accommodate use cases such as the following...
|
||||
|
||||
- A user interface is not needed or you plan on providing your own user interface (API-only)
|
||||
- You plan on interfacing with IPAM exclusively via the RESTful API
|
||||
- You plan on running the backend engine in a lightweight fashion, such as Azure Functions or Azure Container Instances
|
||||
- Discovery
|
||||
- Identify networks, subnets and endpoints holistically across your Azure tenant
|
||||
- Visualize misconfigurations such as orphaned endpoints and improperly configured virtual network peers
|
||||
- Organize
|
||||
- Group Azure networks into *Spaces* and *Blocks* aligned to internal lines of business and enterprise CIDR assignments
|
||||
- Track IP and CIDR consumption
|
||||
- Map external (non-Azure) networks to Azure CIDR ranges
|
||||
- Plan
|
||||
- Explore "what if" cases such as how may subnets of a given mask are available within a given CIDR block
|
||||
- Self-Service
|
||||
- Allow users to reserve CIDR blocks for new virtual network and subnet creation programatically
|
||||
- Integration with Azure template deployments (ARM/Bicep), Terraform and CI/CD pipelines
|
||||
|
||||
## User Interface
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
## IPAM REST API Overview
|
||||
You can interface with the full set of capabilities of IPAM via a REST API. We use Swagger to define API documentation in OpenAPI v3 Specification format.
|
||||
## Azure IPAM REST API Overview
|
||||
You can interface with the full set of capabilities of Azure IPAM via a REST API. We use Swagger to define API documentation in OpenAPI v3 Specification format.
|
||||
|
||||
API docs can be found at the `/api/docs` path of your IPAM website. Here you will find information on methods, parameters, and request body details for all available APIs.
|
||||
API docs can be found at the `/api/docs` path of your Azure IPAM website. Here you will find information on methods, parameters, and request body details for all available APIs.
|
||||
|
||||
![IPAM openapi specification](./images/openapispec.png)
|
||||
|
||||
|
@ -9,7 +9,7 @@ API docs can be found at the `/api/docs` path of your IPAM website. Here you wil
|
|||
You can interface with the API like you would any other REST API. We'll be using [Postman](https://www.postman.com) and [Azure PowerShell](https://docs.microsoft.com/en-us/powershell/azure/what-is-azure-powershell) for our examples.
|
||||
|
||||
## Obtaining an Azure AD Token
|
||||
First things first, you'll need to obtain an Azure AD token for authentication purposes. You can retrieve one via the IPAM UI at anytime by selecting **Token** from the menu presented when clicking on your user avatar in the upper righthand corner.
|
||||
First things first, you'll need to obtain an Azure AD token for authentication purposes. You can retrieve one via the Azure IPAM UI at anytime by selecting **Token** from the menu presented when clicking on your user avatar in the upper righthand corner.
|
||||
|
||||
![IPAM azure ad token](./images/token.png)
|
||||
|
||||
|
@ -17,7 +17,7 @@ You'll then be presented with a message notifying you that your token has been s
|
|||
|
||||
![IPAM azure ad token clipboard](./images/token_clipboard.png)
|
||||
|
||||
You can also retrieve an Azure AD token from IPAM via Azure PowerShell by using the [Get-AzAccessToken](https://docs.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken) commandlet. The token is retrieved from the API exposed via the backend engine application registration. This is the **ResourceUrl** you will be making the access token call against via Azure PowerShell.
|
||||
You can also retrieve an Azure AD token from Azure IPAM via Azure PowerShell by using the [Get-AzAccessToken](https://docs.microsoft.com/en-us/powershell/module/az.accounts/get-azaccesstoken) commandlet. The token is retrieved from the API exposed via the backend engine application registration. This is the **ResourceUrl** you will be making the access token call against via Azure PowerShell.
|
||||
|
||||
![IPAM api resource url](./images/ipam_api_resource_url.png)
|
||||
|
||||
|
@ -90,10 +90,10 @@ $response
|
|||
|
||||
id : ABNsJjXXyTRDTRCdJEJThu
|
||||
cidr : 10.1.5.0/24
|
||||
userId : harvey@elnica6yahoo.onmicrosoft.com
|
||||
userId : user@ipam.onmicrosoft.com
|
||||
createdOn : 1662514052.26623
|
||||
status : wait
|
||||
tag : @{X-IPAM-RES-ID=ABNsJjXXyTRDTRCdJEJThu}
|
||||
````
|
||||
|
||||
Take a look at our **Azure Landing Zone integration** example found under the `deploy` directory in the repository for a real work example of how to automate vNET creation by means of Bicep and leveraging the IPAM API.
|
||||
Take a look at our **Azure Landing Zone integration** example found under the `deploy` directory in the repository for a real work example of how to automate vNET creation by means of Bicep and leveraging the Azure IPAM API.
|
||||
|
|
|
@ -12,7 +12,7 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
|
|||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Running an IPAM Development Environment with Docker Compose
|
||||
## Running an Azure IPAM Development Environment with Docker Compose
|
||||
We have included a Docker Compose file in the root directory of the project (`docker-compose.yml`), to quickly build a fully functional Azure IPAM development environment. The Docker Compose file is also dependant on an `env` file to correctly pass all of the required environment variables into the containers. You can use the `env.example` file, also found at the root directory of the project, as a template to create your own `env` file.
|
||||
|
||||
To start a development environment of the Azure IPAM solution via Docker Compose, run the following commands from the root directory of the project:
|
||||
|
@ -29,32 +29,24 @@ docker compose rm -s -v -f
|
|||
```
|
||||
|
||||
## Building Production Containers Images and Pushing them to DockerHub
|
||||
We user Dockerfiles to build the containers for each of the Azure IPAM supporting components including the Engine, UI, and Load Balancer. If you choose, you can build these containers yourself and host them in DockerHub.
|
||||
We use Dockerfiles to build the containers for the Azure IPAM solution and have two located in the root directory of the project. One is designed for use when running inside a solution such as Azure App Services (as well as other containerized environments) and another specifically designed for running inside Azure Functions. If you choose, you can build these containers yourself and host them in DockerHub.
|
||||
|
||||
To do so, run the following Docker commands from the root directory of the project:
|
||||
|
||||
```shell
|
||||
# Engine Container
|
||||
docker build --rm --no-cache -t <Repository Name>/ipam-engine:latest -f ./engine/Dockerfile.deb ./engine
|
||||
docker push <Repository Name>/ipam-engine:latest
|
||||
# App Services Container
|
||||
docker build --rm --no-cache -t <Repository Name>/ipam:latest -f ./Dockerfile .
|
||||
docker push <Repository Name>/ipam:latest
|
||||
|
||||
# Function Container
|
||||
docker build --rm --no-cache -t <Repository Name>/ipam-func:latest -f ./engine/Dockerfile.func ./engine
|
||||
docker push <Repository Name>/ipam-func:latest
|
||||
|
||||
# UI Container
|
||||
docker build --rm --no-cache -t <Repository Name>/ipam-ui:latest -f ./ui/Dockerfile.deb ./ui
|
||||
docker push <Repository Name>/ipam-ui:latest
|
||||
|
||||
# Load Balancer Container
|
||||
docker build --rm --no-cache -t <Repository Name>/ipam-lb:latest -f ./lb/Dockerfile ./lb
|
||||
docker push <Repository Name>/ipam-lb:latest
|
||||
docker build --rm --no-cache -t <Repository Name>/ipamfunc:latest -f ./Dockerfile.func .
|
||||
docker push <Repository Name>/ipamfunc:latest
|
||||
```
|
||||
|
||||
## Building Production Containers Images Using a Private ACR
|
||||
## Building & Updating Production Containers Images Using a Private ACR
|
||||
In addition to the DockerHub option (above), alternatively you may choose to leverage an Azure Container Registry to host your Azure IPAM containers. Also, you may have selected the `-PrivateACR` flag during the deployment of your Azure IPAM environment, and from time to time you will need to update your containers as new code is released.
|
||||
|
||||
To do so, run the following Azure CLI commands from the root directory of the project:
|
||||
Before running the update commands, you'll need to authenticate to the Azure CLI
|
||||
|
||||
```shell
|
||||
# Authenicate to Azure CLI
|
||||
|
@ -62,7 +54,21 @@ az login
|
|||
|
||||
# Set Target Azure Subscription
|
||||
az account set --subscription "<Target Subscription Name/GUID>"
|
||||
```
|
||||
|
||||
Next, use the following commands to update the Azure IPAM containers within your private Azure Container Registry
|
||||
|
||||
```shell
|
||||
# App Services Container
|
||||
az acr build -r <ACR Name> -t ipam:latest -f ./Dockerfile .
|
||||
|
||||
# Function Container
|
||||
az acr build -r <ACR Name> -t ipamfunc:latest -f .Dockerfile.func .
|
||||
```
|
||||
|
||||
If you're using the legacy Azure IPAM multi-container deployment (prior to v3.0.0), please use the following commands to update your containers instead
|
||||
|
||||
```shell
|
||||
# Engine Container
|
||||
az acr build -r <ACR Name> -t ipam-engine:latest -f ./engine/Dockerfile.deb ./engine
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# IPAM Deployment Overview
|
||||
# Azure IPAM Deployment Overview
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
@ -12,31 +12,38 @@ To successfully deploy the solution, the following prerequisites must be met:
|
|||
- [User Access Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#user-access-administrator)
|
||||
- [Custom Role](https://learn.microsoft.com/en-us/azure/role-based-access-control/custom-roles) with *allow* permissions of `Microsoft.Authorization/roleAssignments/write`
|
||||
- [Global Administrator](https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#global-administrator) (needed to grant admin consent for the App Registration API permissions)
|
||||
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed
|
||||
- Required to clone the Azure IPAM GitHub repository
|
||||
- [PowerShell](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell) version 7.2.0 or later installed
|
||||
- [Azure PowerShell](https://learn.microsoft.com/en-us/powershell/azure/install-az-ps) version 8.0.0 or later installed
|
||||
- [Microsoft Graph PowerShell SDK](https://learn.microsoft.com/en-us/powershell/microsoftgraph/installation) version 1.9.6 or later installed
|
||||
- [Microsoft Graph PowerShell SDK](https://learn.microsoft.com/en-us/powershell/microsoftgraph/installation) version 2.0.0 or later installed
|
||||
- Required for *Full* or *Apps Only* deployments to grant [Admin Consent](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/grant-admin-consent) to the App Registrations
|
||||
- [Bicep CLI](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/install) version 0.10.161 or later installed
|
||||
- [Bicep CLI](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/install) version 0.21.1 or later installed
|
||||
- [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) version 2.35.0 or later installed (optional)
|
||||
- Required only if you are building your own container images and pushing them to Azure Container Registry (Private ACR)
|
||||
- Docker (Linux) / Docker Desktop (Windows) installed (optional)
|
||||
- Required only if you are building your own container images and running them locally for development/testing purposes
|
||||
- Required only if you are building your own container image and pushing it to a private Azure Container Registry (Private ACR)
|
||||
- [Docker (Linux)](https://docs.docker.com/engine/install/) / [Docker Desktop (Windows)](https://docs.docker.com/desktop/install/windows-install/) installed (optional)
|
||||
- Required only if you are building your own container image and running it locally for development/testing purposes
|
||||
|
||||
## Deployment Overview
|
||||
|
||||
The Azure IPAM solution is deployed via a PowerShell deployment script, `deploy.ps1`, found in the `deploy` directory of the project. The infrastructure stack is defined via Azure Bicep files. The deployment can be performed via your local machine or from the development container found in the project. You have the following options for deployment:
|
||||
|
||||
- Two-part deployment
|
||||
- Part 1: App Registrations only
|
||||
- Two-part deployment *(Azure Identities and Permissions Only)*
|
||||
- Part 1: Azure Identities only
|
||||
- App Registrations and Service Principals are created
|
||||
- Required permissions are assigned to App Registrations and Service Principals
|
||||
- Configuration details are saved to a `parameters.json` file which will be shared with the infrastructure team
|
||||
- Part 2: Infrastructure Stack only
|
||||
- UI and Engine containers hosted in App Service or...
|
||||
- Engine container hosted in an Azure Function
|
||||
- Deploy the entire solution (App Registrations + Azure Infrastructure)
|
||||
- UI and Engine containers hosted in App Service or...
|
||||
- Engine container hosted in an Azure Function
|
||||
- Part 2: Azure Infrastructure only
|
||||
- Parameters are read from supplied `parameters.json` file
|
||||
- Azure infrastructure components are deployed
|
||||
- Azure App Service is pointed to public or private Azure Container Registry
|
||||
- Single deployment *(Azure Identities, Permissions and Infrastructure)*
|
||||
- App Registrations and Service Principals are created
|
||||
- Required permissions are assigned to App Registrations and Service Principals
|
||||
- Azure infrastructure components are deployed
|
||||
- Azure App Service is pointed to public or private Azure Container Registry
|
||||
|
||||
The two-part deployment option is provided in the event that a single team doesn't have the necessary permissions to deploy both the App Registrations in Azure AD, and the Azure infrastructure stack. In the event that you do have all of the the necessary permissions in Azure AD and on the Azure infrastructure side, then you have the option to deploy the entire solution all at once.
|
||||
The two-part deployment option is provided in the event that a single team within your organization doesn't have the necessary permissions to deploy both the Azure identities within Entra ID, and the Azure infrastructure stack. If a single group does have all of the the necessary permissions in Entra ID and on the Azure infrastructure side, then you have the option to deploy the complete solution all at once.
|
||||
|
||||
## Authenticate to Azure PowerShell
|
||||
|
||||
|
@ -122,15 +129,16 @@ To deploy the full solution, run the following from within the `deploy` director
|
|||
|
||||
You have the ability to pass optional flags to the deployment script:
|
||||
|
||||
| Parameter | Description |
|
||||
| :---------------------------------------------- | :------------------------------------------------------------------------ |
|
||||
| `-UIAppName <name>` | Changes the name of the UI app registration |
|
||||
| `-EngineAppName <name>` | Changes the name of the Engine app registration |
|
||||
| `-Tags @{<tag> = '<value>'; <tag> = '<value>'}` | Attaches the hashtable as tags on the deployed IPAM resource group |
|
||||
| `-ResourceNames @{<resource1> = '<name>'; <resource2> = '<name>'}` | Overrides default resource names with custom names **<sup>1,2</sup>** |
|
||||
| `-NamePrefix <prefix>` | Replaces the default resource prefix of "ipam" with an alternative prefix **<sup>3</sup>** |
|
||||
| `-AsFunction` | Deploys the engine container only to an Azure Function |
|
||||
| `-PrivateACR` | Deploys a private Azure Container Registry and builds the IPAM containers |
|
||||
| Parameter | Description |
|
||||
| :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
|
||||
| `-UIAppName <name>` | Changes the name of the UI app registration |
|
||||
| `-EngineAppName <name>` | Changes the name of the Engine app registration |
|
||||
| `-Tags @{<tag> = '<value>'; <tag> = '<value>'}` | Attaches the hashtable as tags on the deployed IPAM resource group |
|
||||
| `-ResourceNames @{<resource1> = '<name>'; <resource2> = '<name>'}` | Overrides default resource names with custom names **<sup>1,2</sup>** |
|
||||
| `-NamePrefix <prefix>` | Replaces the default resource prefix of "ipam" with an alternative prefix **<sup>3</sup>** |
|
||||
| `-Function` | Deploys the engine container only to an Azure Function |
|
||||
| `-PrivateACR` | Deploys a private Azure Container Registry and builds the IPAM containers |
|
||||
| `-DisableUI` | Solution will be deployed without a UI, no UI identities will be created |
|
||||
|
||||
> **NOTE 1:** The required values will vary based on the deployment type.
|
||||
|
||||
|
@ -168,7 +176,7 @@ You have the ability to pass optional flags to the deployment script:
|
|||
```powershell
|
||||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-AsFunction
|
||||
-Function
|
||||
```
|
||||
|
||||
**Deploy IPAM solution with a private Container Registry:**
|
||||
|
@ -199,7 +207,7 @@ $ResourceNames = @{
|
|||
-ResourceNames $ResourceNames
|
||||
```
|
||||
|
||||
**Override default resource names with custom resource names and deploy as an Azure Function:**
|
||||
**Override default resource names with custom resource names and use a private Container Registry:**
|
||||
|
||||
```powershell
|
||||
$ResourceNames = @{
|
||||
|
@ -217,11 +225,11 @@ $ResourceNames = @{
|
|||
|
||||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ResourceNames $ResourceNames
|
||||
-ResourceNames $ResourceNames `
|
||||
-PrivateACR
|
||||
```
|
||||
|
||||
**Override default resource names with custom resource names and use a private Container Registry:**
|
||||
**Override default resource names with custom resource names and deploy as an Azure Function:**
|
||||
|
||||
```powershell
|
||||
$ResourceNames = @{
|
||||
|
@ -240,12 +248,12 @@ $ResourceNames = @{
|
|||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ResourceNames $ResourceNames
|
||||
-AsFunction
|
||||
-Function
|
||||
```
|
||||
|
||||
## App Registration Only Deployment
|
||||
## Azure Identities (Only) Deployment
|
||||
|
||||
To deploy App Registrations only, run the following from within the `deploy` directory:
|
||||
To deploy Azure Identities only, run the following from within the `deploy` directory:
|
||||
|
||||
```powershell
|
||||
./deploy.ps1 -AppsOnly
|
||||
|
@ -253,11 +261,11 @@ To deploy App Registrations only, run the following from within the `deploy` dir
|
|||
|
||||
You have the ability to pass optional flags to the deployment script:
|
||||
|
||||
| Parameter | Description |
|
||||
| :---------------------- | :-------------------------------------------------------------------------------------------------- |
|
||||
| `-UIAppName <name>` | Changes the name of the UI app registration |
|
||||
| `-EngineAppName <name>` | Changes the name of the Engine app registration |
|
||||
| `-AsFunction` | Indicates that this solution will be deployed as an Azure Function, no UI App Registration required |
|
||||
| Parameter | Description |
|
||||
| :---------------------- | :----------------------------------------------------------------------- |
|
||||
| `-UIAppName <name>` | Changes the name of the UI app registration |
|
||||
| `-EngineAppName <name>` | Changes the name of the Engine app registration |
|
||||
| `-DisableUI` | Solution will be deployed without a UI, no UI identities will be created |
|
||||
|
||||
**Customize the name of the App Registrations:**
|
||||
|
||||
|
@ -268,15 +276,15 @@ You have the ability to pass optional flags to the deployment script:
|
|||
-EngineAppName "my-engine-app-reg"
|
||||
```
|
||||
|
||||
**Deploy IPAM solution as an Azure Function:**
|
||||
**Deploy IPAM solution without a UI (API-only):**
|
||||
|
||||
```powershell
|
||||
./deploy.ps1 `
|
||||
-AppsOnly `
|
||||
-AsFunction
|
||||
-DisableUI
|
||||
```
|
||||
|
||||
As part of the app registration deployment, a `main.parameters.json` file is generated with pre-populated parameters for the app registration IDs as well as the engine app registration secret. This parameter file will then be used to perform the infrastructure deployment.
|
||||
As part of the app registration deployment, a `main.parameters.json` file is generated with pre-populated parameters for the app registration IDs as well as the engine app registration secret. This parameter file will then be used to perform the infrastructure only deployment.
|
||||
|
||||
## Infrastructure Stack (Only) Deployment
|
||||
|
||||
|
@ -292,12 +300,13 @@ Once your parameters file is ready, run the following from within the `deploy` d
|
|||
|
||||
You have the ability to pass optional flags to the deployment script:
|
||||
|
||||
| Parameter | Description |
|
||||
| :---------------------------------------------- | :------------------------------------------------------------------------ |
|
||||
| `-Tags @{<tag> = '<value>'; <tag> = '<value>'}` | Attaches the hashtable as tags on the deployed IPAM resource group |
|
||||
| `-ResourceNames @{<resource1> = '<name>'; <resource2> = '<name>'}` | Overrides default resource names with custom names **<sup>1,2</sup>** |
|
||||
| `-NamePrefix <prefix>` | Replaces the default resource prefix of "ipam" with an alternative prefix **<sup>3</sup>** |
|
||||
| `-PrivateACR` | Deploys a private Azure Container Registry and builds the IPAM containers |
|
||||
| Parameter | Description |
|
||||
| :----------------------------------------------------------------- | :----------------------------------------------------------------------------------------- |
|
||||
| `-Tags @{<tag> = '<value>'; <tag> = '<value>'}` | Attaches the hashtable as tags on the deployed IPAM resource group |
|
||||
| `-ResourceNames @{<resource1> = '<name>'; <resource2> = '<name>'}` | Overrides default resource names with custom names **<sup>1,2</sup>** |
|
||||
| `-NamePrefix <prefix>` | Replaces the default resource prefix of "ipam" with an alternative prefix **<sup>3</sup>** |
|
||||
| `-PrivateACR` | Deploys a private Azure Container Registry and builds the IPAM containers |
|
||||
| `-Function` | Deploys the engine container only to an Azure Function |
|
||||
|
||||
> **NOTE 1:** The required values will vary based on the deployment type.
|
||||
|
||||
|
@ -305,6 +314,15 @@ You have the ability to pass optional flags to the deployment script:
|
|||
|
||||
> **NOTE 3:** Maximum of seven (7) characters. This is because the prefix is used to generate names for several different Azure resource types with varying maximum lengths.
|
||||
|
||||
**Change the name prefix for the Azure resources:**
|
||||
|
||||
```powershell
|
||||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ParameterFile ./main.parameters.json `
|
||||
-NamePrefix "devipam"
|
||||
```
|
||||
|
||||
**Add custom tags to the Azure resources:**
|
||||
|
||||
```powershell
|
||||
|
@ -314,13 +332,13 @@ You have the ability to pass optional flags to the deployment script:
|
|||
-Tags @{owner = 'ipamadmin@example.com'; environment = 'development'}
|
||||
```
|
||||
|
||||
**Change the name prefix for the Azure resources:**
|
||||
**Deploy IPAM solution as an Azure Function:**
|
||||
|
||||
```powershell
|
||||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ParameterFile ./main.parameters.json `
|
||||
-NamePrefix "devipam"
|
||||
-Function
|
||||
```
|
||||
|
||||
**Deploy IPAM solution with a private Container Registry:**
|
||||
|
@ -372,7 +390,7 @@ $ResourceNames = @{
|
|||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ParameterFile ./main.parameters.json `
|
||||
-ResourceNames $ResourceNames
|
||||
-ResourceNames $ResourceNames `
|
||||
-PrivateACR
|
||||
```
|
||||
|
||||
|
@ -395,7 +413,6 @@ $ResourceNames = @{
|
|||
./deploy.ps1 `
|
||||
-Location "westus3" `
|
||||
-ParameterFile ./main.parameters.json `
|
||||
-ResourceNames $ResourceNames
|
||||
-ResourceNames $ResourceNames `
|
||||
-Function
|
||||
```
|
||||
|
||||
> **NOTE:** Use this format when the `-AsFunction` flag was used during the *App Registration Only* step above
|
||||
|
|
Двоичные данные
docs/images/ipam_architecture_full.png
Двоичные данные
docs/images/ipam_architecture_full.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 147 KiB После Ширина: | Высота: | Размер: 207 KiB |
Двоичные данные
docs/images/ipam_architecture_function.png
Двоичные данные
docs/images/ipam_architecture_function.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 117 KiB После Ширина: | Высота: | Размер: 160 KiB |
|
@ -1,11 +1,11 @@
|
|||
# Questions or Comments
|
||||
|
||||
The IPAM team welcomes engagement and contributions from the community.
|
||||
The Azure IPAM team welcomes engagement and contributions from the community.
|
||||
|
||||
## Discussions
|
||||
|
||||
We have set up a [GitHub Discussions](https://github.com/Azure/ipam/discussions) page to make it easy to engage with the IPAM team without opening an issue.
|
||||
We have set up a [GitHub Discussions](https://github.com/Azure/ipam/discussions) page to make it easy to engage with the Azure IPAM team without opening an issue.
|
||||
|
||||
## Issues
|
||||
|
||||
In the event you come across a bug in the IPAM tool, please open a [GitHub Issue](https://github.com/Azure/ipam/issues) and someone from the team will respond as soon ASAP. Please include as much detail as you can about what actions were taken leading up to the discovery of the issue, and how (if possible) it can be reproduced.
|
||||
In the event you come across a bug in the Azure IPAM tool, please open a [GitHub Issue](https://github.com/Azure/ipam/issues) and someone from the team will respond as soon ASAP. Please include as much detail as you can about what actions were taken leading up to the discovery of the issue, and how (if possible) it can be reproduced.
|
||||
|
|
|
@ -131,3 +131,5 @@ az cosmosdb update --resource-group <ResourceGroupName> --name <CosmosDBAccountN
|
|||
#### <u>Notes</u>
|
||||
|
||||
This flag may have been set by [Azure Policy](https://learn.microsoft.com/en-us/azure/governance/policy/overview). You can find more details about this policy [here](https://learn.microsoft.com/en-us/azure/cosmos-db/policy-reference#azure-cosmos-db) under *Azure Cosmos DB key based metadata write access should be disabled*. You may need to contact your policy administrator to request an exception for Azure IPAM.
|
||||
|
||||
Additionally this issue only applies to legacy deployments of Azure IPAM (prior to v3.0.0) as the latest versions use SQL [role-based access control](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac) to read/write data from Cosmos DB.
|
||||
|
|
|
@ -34,6 +34,10 @@ class Globals:
|
|||
session = aiohttp.ClientSession(connector=conn)
|
||||
self.shared_transport = AioHttpTransport(session=session, session_owner=False)
|
||||
|
||||
@property
|
||||
def MANAGED_IDENTITY_ID(self):
|
||||
return os.environ.get('MANAGED_IDENTITY_ID')
|
||||
|
||||
@property
|
||||
def CLIENT_ID(self):
|
||||
return os.environ.get('CLIENT_ID') or os.environ.get('ENGINE_APP_ID')
|
||||
|
|
|
@ -7,9 +7,11 @@ from fastapi.middleware.gzip import GZipMiddleware
|
|||
from fastapi_restful.tasks import repeat_every
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
from azure.identity.aio import ManagedIdentityCredential
|
||||
|
||||
from azure.cosmos.aio import CosmosClient
|
||||
from azure.cosmos import PartitionKey
|
||||
from azure.cosmos.exceptions import CosmosResourceExistsError, CosmosResourceNotFoundError
|
||||
from azure.cosmos.exceptions import CosmosResourceExistsError, CosmosResourceNotFoundError, CosmosHttpResponseError
|
||||
|
||||
from app.routers import (
|
||||
azure,
|
||||
|
@ -40,7 +42,9 @@ from app.routers.common.helper import (
|
|||
cosmos_replace
|
||||
)
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
BUILD_DIR = os.path.join(os.getcwd(), "dist")
|
||||
IPAM_VERSION = json.load(open(os.path.join(ROOT_DIR, "version.json")))['version']
|
||||
|
||||
try:
|
||||
UI_APP_ID = uuid.UUID(os.environ.get('UI_APP_ID'))
|
||||
|
@ -56,7 +60,7 @@ Azure IPAM is a lightweight solution developed on top of the Azure platform desi
|
|||
app = FastAPI(
|
||||
title = "Azure IPAM",
|
||||
description = description,
|
||||
version = "3.0.0",
|
||||
version = IPAM_VERSION,
|
||||
contact = {
|
||||
"name": "Azure IPAM Team",
|
||||
"url": "https://github.com/azure/ipam",
|
||||
|
@ -168,12 +172,20 @@ if os.path.isdir(BUILD_DIR) and UI_APP_ID and VALID_APP_ID:
|
|||
return FileResponse(BUILD_DIR + "/index.html")
|
||||
|
||||
async def db_upgrade():
|
||||
cosmos_client = CosmosClient(globals.COSMOS_URL, credential=globals.COSMOS_KEY)
|
||||
managed_identity_credential = ManagedIdentityCredential(
|
||||
client_id = globals.MANAGED_IDENTITY_ID
|
||||
)
|
||||
|
||||
database_name = "ipam-db"
|
||||
cosmos_client = CosmosClient(
|
||||
globals.COSMOS_URL,
|
||||
credential=globals.COSMOS_KEY if globals.COSMOS_KEY else managed_identity_credential,
|
||||
transport=globals.SHARED_TRANSPORT
|
||||
)
|
||||
|
||||
database_name = globals.DATABASE_NAME
|
||||
database = cosmos_client.get_database_client(database_name)
|
||||
|
||||
container_name = "ipam-container"
|
||||
container_name = globals.CONTAINER_NAME
|
||||
container = database.get_container_client(container_name)
|
||||
|
||||
try:
|
||||
|
@ -362,6 +374,7 @@ async def db_upgrade():
|
|||
# logger.info("No existing Virtual Hubs to patch...")
|
||||
|
||||
await cosmos_client.close()
|
||||
await managed_identity_credential.close()
|
||||
|
||||
@app.on_event("startup")
|
||||
async def ipam_startup():
|
||||
|
@ -402,32 +415,46 @@ async def ipam_startup():
|
|||
with open(env_file, "w") as env_file:
|
||||
env_file.write(env_data_js)
|
||||
|
||||
client = CosmosClient(globals.COSMOS_URL, credential=globals.COSMOS_KEY)
|
||||
managed_identity_credential = ManagedIdentityCredential(
|
||||
client_id = globals.MANAGED_IDENTITY_ID
|
||||
)
|
||||
|
||||
cosmos_client = CosmosClient(
|
||||
globals.COSMOS_URL,
|
||||
credential=globals.COSMOS_KEY if globals.COSMOS_KEY else managed_identity_credential,
|
||||
transport=globals.SHARED_TRANSPORT
|
||||
)
|
||||
|
||||
database_name = globals.DATABASE_NAME
|
||||
|
||||
try:
|
||||
logger.info('Creating Database...')
|
||||
database = await client.create_database(
|
||||
logger.info('Verifying Database Exists...')
|
||||
database = await cosmos_client.create_database_if_not_exists(
|
||||
id = database_name
|
||||
)
|
||||
except CosmosResourceExistsError:
|
||||
logger.warning('Database exists! Using existing database...')
|
||||
database = client.get_database_client(database_name)
|
||||
except CosmosHttpResponseError as e:
|
||||
logger.error('Cosmos database does not exist, error initializing Azure IPAM!')
|
||||
raise e
|
||||
|
||||
|
||||
database = cosmos_client.get_database_client(database_name)
|
||||
|
||||
container_name = globals.CONTAINER_NAME
|
||||
|
||||
try:
|
||||
logger.info('Creating Container...')
|
||||
container = await database.create_container(
|
||||
logger.info('Verifying Container Exists...')
|
||||
container = await database.create_container_if_not_exists(
|
||||
id = container_name,
|
||||
partition_key = PartitionKey(path = "/tenant_id")
|
||||
)
|
||||
except CosmosResourceExistsError:
|
||||
logger.warning('Container exists! Using existing container...')
|
||||
container = database.get_container_client(container_name)
|
||||
except CosmosHttpResponseError as e:
|
||||
logger.error('Cosmos container does not exist, error initializing Azure IPAM!')
|
||||
raise e
|
||||
|
||||
container = database.get_container_client(container_name)
|
||||
|
||||
await client.close()
|
||||
await cosmos_client.close()
|
||||
await managed_identity_credential.close()
|
||||
|
||||
await db_upgrade()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from fastapi import HTTPException
|
||||
|
||||
from azure.identity.aio import OnBehalfOfCredential, ClientSecretCredential
|
||||
from azure.identity.aio import OnBehalfOfCredential, ManagedIdentityCredential, ClientSecretCredential
|
||||
|
||||
from azure.core import MatchConditions
|
||||
from azure.core.exceptions import ClientAuthenticationError, HttpResponseError, ServiceRequestError
|
||||
|
@ -18,9 +18,13 @@ from functools import wraps
|
|||
|
||||
from app.globals import globals
|
||||
|
||||
managed_identity_credential = ManagedIdentityCredential(
|
||||
client_id = globals.MANAGED_IDENTITY_ID
|
||||
)
|
||||
|
||||
cosmos_client = CosmosClient(
|
||||
url=globals.COSMOS_URL,
|
||||
credential=globals.COSMOS_KEY,
|
||||
credential=(globals.COSMOS_KEY if globals.COSMOS_KEY else managed_identity_credential),
|
||||
transport=globals.SHARED_TRANSPORT
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"version": "3.0.0"
|
||||
}
|
|
@ -30,8 +30,8 @@
|
|||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-router-dom": "^6.21.2",
|
||||
"spinners-react": "^1.0.7",
|
||||
"web-vitals": "^3.5.1"
|
||||
},
|
||||
|
@ -1642,9 +1642,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.1.tgz",
|
||||
"integrity": "sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==",
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz",
|
||||
"integrity": "sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
|
@ -5235,9 +5235,9 @@
|
|||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.0.4.tgz",
|
||||
"integrity": "sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.0.tgz",
|
||||
"integrity": "sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ==",
|
||||
"dependencies": {
|
||||
"@types/use-sync-external-store": "^0.0.3",
|
||||
"use-sync-external-store": "^1.0.0"
|
||||
|
@ -5270,11 +5270,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.1.tgz",
|
||||
"integrity": "sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==",
|
||||
"version": "6.21.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.2.tgz",
|
||||
"integrity": "sha512-jJcgiwDsnaHIeC+IN7atO0XiSRCrOsQAHHbChtJxmgqG2IaYQXSnhqGb5vk2CU/wBQA12Zt+TkbuJjIn65gzbA==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.14.1"
|
||||
"@remix-run/router": "1.14.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -5284,12 +5284,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.1.tgz",
|
||||
"integrity": "sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==",
|
||||
"version": "6.21.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.2.tgz",
|
||||
"integrity": "sha512-tE13UukgUOh2/sqYr6jPzZTzmzc70aGRP4pAjG2if0IP3aUT+sBtAKUJh0qMh0zylJHGLmzS+XWVaON4UklHeg==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.14.1",
|
||||
"react-router": "6.21.1"
|
||||
"@remix-run/router": "1.14.2",
|
||||
"react-router": "6.21.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -7465,9 +7465,9 @@
|
|||
}
|
||||
},
|
||||
"@remix-run/router": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.1.tgz",
|
||||
"integrity": "sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow=="
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.2.tgz",
|
||||
"integrity": "sha512-ACXpdMM9hmKZww21yEqWwiLws/UPLhNKvimN8RrYSqPSvB3ov7sLvAcfvaxePeLvccTQKGdkDIhLYApZVDFuKg=="
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
"version": "5.0.5",
|
||||
|
@ -10049,9 +10049,9 @@
|
|||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "9.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.0.4.tgz",
|
||||
"integrity": "sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA==",
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.0.tgz",
|
||||
"integrity": "sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ==",
|
||||
"requires": {
|
||||
"@types/use-sync-external-store": "^0.0.3",
|
||||
"use-sync-external-store": "^1.0.0"
|
||||
|
@ -10064,20 +10064,20 @@
|
|||
"dev": true
|
||||
},
|
||||
"react-router": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.1.tgz",
|
||||
"integrity": "sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==",
|
||||
"version": "6.21.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.2.tgz",
|
||||
"integrity": "sha512-jJcgiwDsnaHIeC+IN7atO0XiSRCrOsQAHHbChtJxmgqG2IaYQXSnhqGb5vk2CU/wBQA12Zt+TkbuJjIn65gzbA==",
|
||||
"requires": {
|
||||
"@remix-run/router": "1.14.1"
|
||||
"@remix-run/router": "1.14.2"
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "6.21.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.1.tgz",
|
||||
"integrity": "sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==",
|
||||
"version": "6.21.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.2.tgz",
|
||||
"integrity": "sha512-tE13UukgUOh2/sqYr6jPzZTzmzc70aGRP4pAjG2if0IP3aUT+sBtAKUJh0qMh0zylJHGLmzS+XWVaON4UklHeg==",
|
||||
"requires": {
|
||||
"@remix-run/router": "1.14.1",
|
||||
"react-router": "6.21.1"
|
||||
"@remix-run/router": "1.14.2",
|
||||
"react-router": "6.21.2"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-redux": "^9.0.4",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-router-dom": "^6.21.2",
|
||||
"spinners-react": "^1.0.7",
|
||||
"web-vitals": "^3.5.1"
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче