azureappproxxy
(
圖片來源: MS Docs )

Azure AD Application Proxy ( 後簡稱 AppProxy ) 是我真心覺得微軟非常棒的一個 Azure 服務,IT 管理者可以簡單完成串接企業內部應用程式,使用者在無 VPN 的環境下就能從 Internet 直接存取到內部系統,並且搭配 Azure AD 及 Conditional Access 提供企業強大及完整的安全防護。

一般在佈建 AppProxy 服務時,IT 管理者為方便後期快速部署,大都會使用萬用字元憑證 ( Wildcard SSL Certificate ) 進行設定。但如果遇到萬用字元憑證快到期時,已經大量部署的 AppProxy 應該沒人想一個個手動更換憑證,老闆又不是請你來做工讀生的工作 (但可能給工讀生的薪水!?),這時可以利用後述的指令進行大量更換。

本篇指令主要參考國外分享,然後將其拆分成兩段做小部改寫,因為我認為任何的直接快速大量修改都是有風險的,必須要測試及確認後才能進行變更,甚至有機會做還原動作。

開始前,請準備好
1. Azure AD PowerShell
2. 預備更新的萬用字元憑證帶私鑰 ( Private Key ) 的檔案 。PFX 檔案格式,如果檔案小於 4 KB ,一般我會懷疑該檔案完全沒有含私鑰。
3. 預備更新的憑證匯入密碼

兩段的 PowerShell 程式如下,並已放在 GitHub 提供下載使用
GitHub - AskaSu - Bulk update Certificate for AAD AppProxy

第一段,產生與指定網域名稱有關的 AppProxy 資訊 CSV 報表。
藉此瞭解現有相關設定外,同時可以工人模式檢視哪些 App Proxy 真的需要進行憑證更換。

[提醒]
a. 預設產生的報表指定放入 C:\Temp 目錄中,如有其他需求請自行修改。
b. 假設這個報表主要作為憑證更新使用,可以參考程式碼中 ExternalUrl -nolike 那段進行編修,對子網域直接做過濾。主要是一般萬用字元憑證並不支援子網域 ,除非買的是 Multi-Domain 等級的憑證,不過那價格應該不是一般企業會採購的了。

執行語法範例

.\AAD_Create_AppProxy_Checking_List.ps1 -CustomDomain 網域名稱

完整指令碼

Param(
    [Parameter(Mandatory = $true)]
    [string] $CustomDomain
)

$QueryDomain = "*"+ $CustomDomain + "/*"

#Connect to AzureAD
Write-Host 'Connecting to AzureAD' -ForegroundColor Yellow
Connect-AzureAD

#ExportFile
$AADName = @((@((Get-AzureADDomain | Where-Object {$_.Name -like "*.onmicrosoft.com"}).Name )[0]).Split("."))[0]
$ExportFilePath = "C:\Temp\" + $AADName + "_AADAppProxy_Checking_list_" + "$((Get-Date -format yyyy-MMdd).ToString()).csv"

#Get all Azure AD App Proxy Proxy Applications checking the ObjectID of all apps.
Write-Host 'Obtaining Azure AD App Proxy Applications for your Domain' -ForegroundColor Yellow
$ProxyApps = foreach ($a in (Get-AzureADApplication -All:$true | Sort-Object DisplayName))
 {
     try
     {
         $p = Get-AzureADApplicationProxyApplication -ObjectId $a.ObjectId
         [pscustomobject]@{DisplayName=$a.DisplayName; ObjectID=$a.ObjectId; ExternalUrl=$p.ExternalUrl; InternalUrl=$p.InternalUrl; AuthenticationType=$p.ExternalAuthenticationType; ServerTimeout=$p.ApplicationServerTimeout; CertSubjectName = $p.VerifiedCustomDomainCertificatesMetadata.SubjectName; CertThumbprint = $p.VerifiedCustomDomainCertificatesMetadata.Thumbprint; CertExpiryDate = $p.VerifiedCustomDomainCertificatesMetadata.ExpiryDate ; CertificateInfo=$p.VerifiedCustomDomainCertificatesMetadata}
     }
     catch
     {
         continue
     }
}

#Filter the proxy applications with the ExternalURL of your domain to askacloud.com only, and not including Jira site series, rd.askacloud.com and iot.askacloud.com etc.
$AppsOnCustomDomain = $ProxyApps | Where-Object {$_.ExternalUrl -like $QueryDomain `
-and $_.ExternalUrl -notlike "*.jira*" `
-and $_.ExternalUrl -notlike "*rd.askacloud.com*" `
-and $_.ExternalUrl -notlike "*iot.askacloud.com*" `
}  | Sort-Object DisPlayName

Write-Host 'The following applications need to been reviewed for Certificate Update' -ForegroundColor Yellow
$AppsOnCustomDomain | Out-Host

#Export list for checking again and updating
$AppsOnCustomDomain | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $ExportFilePath

執行後的報表呈現如下
AskaBlog_AAD_AppProxy_Report

第二段,依據前面的列表作為大量更換的依據,接著進行憑證更新。
可以嘗試先將列表清單修改到僅剩下一兩項,也就是前面提到的先進行測試,確認後再進行大量部署。

[提醒]
a.本段語法假設已透過 PowerShell 完成連接 Azure AD,並且預備更新的 AppProxy 清單檔案,儲存在執行前段指令當天所產生的位置裡。
b. 執行過程如下圖會需要輸入匯入密碼,請先準備。

AskaBlog_AAD_AppProxy_Update_Cert_PWinput

執行語法範例

.\AAD_Update_Cert_to_AppProxy.ps1 -PFXLocation 檔名及完整路徑

完整指令碼

Param(
    [Parameter(Mandatory=$True)]
    [String] $PFXLocation
)

#Connect to AzureAD
#Write-Host 'Connecting to AzureAD' -ForegroundColor Yellow
#Connect-AzureAD

# $PFXLocation = "C:\Temp\wildcard_askacloud_com.pfx"

#Enter the PFX password
Write-Host 'Enter the password for PFX file' -ForegroundColor Yellow
$PFXPassword = Read-Host -AsSecureString

$AADName = @((@((Get-AzureADDomain | Where-Object {$_.Name -like "*.onmicrosoft.com"}).Name )[0]).Split("."))[0]
$ImportFilePath = "C:\Temp\" + $AADName + "_AADAppProxy_Checking_list_" + "$((Get-Date -format yyyy-MMdd).ToString()).csv"

$AppsOnCustomDomain = Import-CSV -path $ImportFilePath

Write-Host 'Are you sure you wish to change certificate on the above applications? (Y/N)' -ForegroundColor Yellow
$Answer = Read-Host

If ($Answer -eq 'Y'){
  #Loop through the custom domain apps and upload new TLS certificate
  foreach ($CustomDomainApp in $AppsOnCustomDomain) {
      Write-Host "Setting Certificate on Application" $CustomDomainApp.DisplayName -ForegroundColor Green
      Set-AzureADApplicationProxyApplicationCustomDomainCertificate -ObjectId $CustomDomainApp.ObjectID -PfxFilePath $PFXLocation -Password $PFXPassword
    }

}

else {
  Write-Host 'Apps not approved for change' -ForegroundColor Red
}

另外,我觀察測試及確認
1. 一旦完成更新與這個網域有關的憑證後,再新增加與該網域有關的 AppProxy,就會自動使用這張最新更換的。
2. 憑證匯入密碼打錯,不會做任何動作,只是會跳錯誤訊息而已。

AskaBlog_AAD_AppProxy_Update_Cert_PWinput_error

3. 更新過程會去檢查憑證是否有對應到使用的網域名稱,如果沒有對應到,就會報錯,但不會影響該 App Proxy 的原始憑證設定,也就是不會被搞到壞掉。

舉例來說,下圖報錯的項目「AppProxy - POC - Graylog」,實際的 FQDN 為 log.app.askacloud.com 是在子網域中 ,也就是前面提及的狀況,一般萬用字元憑證無法使用到再下一層的子網域,所以報錯無法更新是正常情形,而且,錯誤訊息也很明顯地告知:
" Set-AzureADApplicationProxyApplicationCustomDomainCertificate : Error occurred while executing SetApplicationProxyApplication
Code: InvalidCertificate_HostNameMismatch
Message: Bad Certificate Request "

AskaBlog_AAD_AppProxy_Update_Cert_complete

資料參考
Declan Turley Tech Blog - Azure AD App Proxy: Bulk Update TLS Certificate for a Custom Domain
MS Docs - Get all Azure Active Directory Application Proxy applications published with the identical certificate and replace it