Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 55 additions & 55 deletions Installation/ImportAzureADApplicationProxy.ps1
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
<#
.SYNOPSIS
Imports a cert from simple-acme (WACS) renewal into Azure AD Application Proxy for all applications that are using it. You likely want to use a wildcard certificate for this purpose.
Imports a cert from simple-acme renewal into Azure AD Application Proxy for all applications that are using it. You likely want to use a wildcard certificate for this purpose.

.DESCRIPTION
Note that this script is intended to be run via the install script plugin from simple-acme (WACS) via the batch script wrapper. As such, we use positional parameters to avoid issues with using a dash in the cmd line.
Note that this script is intended to be run via the install script plugin from simple-acme via the batch script wrapper. As such, we use positional parameters to avoid issues with using a dash in the cmd line.

Proper information should be available here

https://github.com/simple-acme/simple-acme/wiki/Install-Script

or more generally, here

https://github.com/simple-acme/simple-acme/wiki/Example-Scripts
Uses Microsoft Graph API instead of deprecated AzureAD module.

.PARAMETER PfxPath
The absolute path to the pfx file that will be uploaded to Azure. Typically use '{CacheFile}'
Expand All @@ -20,77 +14,83 @@ The absolute path to the pfx file that will be uploaded to Azure. Typically use
The password for the pfx file. Typically use '{CachePassword}'

.PARAMETER Username
Username of account to login with Connect-AzureAD. This account must have the "Application administrator" role to allow it to change the proxy certificate.
Username of account to login with Connect-MgGraph. This account must have the "Application administrator" role.

.PARAMETER Password
Password for the azure account
Password for the account

.EXAMPLE

ImportAzureApplicationGateway.ps1 <PfxPath> <CertPass>
ImportAzureApplicationGateway.ps1 <PfxPath> <CertPass> <Username> <Password>

.NOTES
Wanted to use a service principal instead of an account for this, but since there is a bug with the cmdlets used, we can't. Instead a regular account must be specified.
https://github.com/Azure/azure-docs-powershell-azuread/issues/200


This uses the Microsoft Graph API instead of the deprecated Azure AD module which no longer works.
Unfortunately, the graph API doesn't have good (or any really) documentation about managing certificates.
#>

param(
[Parameter(Position=0,Mandatory=$false)][string]$PfxPath,
[Parameter(Position=0,Mandatory=$true)][string]$PfxPath,
[Parameter(Position=1,Mandatory=$true)][string]$CertPass,
[Parameter(Position=2,Mandatory=$true)][string]$Username,
[Parameter(Position=3,Mandatory=$true)][string]$Password


)

# Convert the password for the certificate to a secure string
$SecureCertPass = ConvertTo-SecureString -String $CertPass -AsPlainText -Force
if (!(Get-Command "Get-MGBetaApplication" -ErrorAction SilentlyContinue)) {
Throw "Missing Microsoft.Graph.Beta module, install with 'Install-Module -Name Microsoft.Graph.Beta -Scope AllUsers'"
}

# Connect to Microsoft Graph using username/password
$SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
$ClientId = "1950a258-227b-4e31-a9cf-717495945fc2" # Microsoft Azure PowerShell app ID

# Get access token
$body = @{
grant_type = "password"
client_id = $ClientId
username = $Username
password = $Password
scope = "https://graph.microsoft.com/.default"
}

$tokenResponse = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" -Body $body
$accessToken = $tokenResponse.access_token | ConvertTo-SecureString -AsPlainText -Force

if (!(Get-Command "Set-AzureAdApplicationProxyApplicationCustomDomainCertificate" -erroraction SilentlyContinue)) {
Throw "Missing AzureAD module, install with 'Install-Module -name AzureAD -Scope AllUsers'"
}
Connect-MgGraph -AccessToken $accessToken -NoWelcome

# Connect to Azure
$Pass = ConvertTo-SecureString -String $Password -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $Pass
$null = Connect-AzureAD -Credential $Credential


# It's easier, apparently, to search for the service principals that are tagged with WindowsAzureActiveDirectoryOnPremApp,
# then match them to the Get-AzureADApplication output by AppId.
# Get-AzureADApplication doesn't have any way to filter only for ones using the application proxy, and
# Get-AzureADApplicationProxyApplication requires an ObjectId, there's no way to just list them all.
$aadapServPrinc = Get-AzureADServicePrincipal -Top 100000 | where-object {$_.Tags -Contains "WindowsAzureActiveDirectoryOnPremApp"}

# Now we get a list of all Azure AD Applications
$aadapps = Get-AzureADApplication -All $true

# The AppId between $aadapServPrinc and $aadapps is the same for each of the applications using Azure AD Application Proxy.
# What we need to get is the ObjectId from $aadapps for each application that was in $aadapServPrinc
$aadproxyapps = $aadapServPrinc | Foreach-Object { $aadapps -match $_.AppId}
# Now $aadaproxyapps has just the Get-AzureADApplication objects for applications that use the Azure AD Application Proxy.
# Get service principals tagged with WindowsAzureActiveDirectoryOnPremApp
$aadapServPrinc = Get-MgBetaServicePrincipal -All | Where-Object {$_.Tags -Contains "WindowsAzureActiveDirectoryOnPremApp"}

"Found $($aadproxyapps.count) applications to update"
# Get all applications
$aadapps = Get-MgBetaApplication -All

# Get the matching objects from Get-AzureADApplicationProxyApplication and show the certificate being used
#$aadproxyapps | Foreach-Object {
# $proxyapp = Get-AzureADApplicationProxyApplication -ObjectId $_.ObjectId
# Write-Host "Checking $($proxyapp.ExternalUrl)"
# Write-Host "Existing certificate is: $($proxyapp.VerifiedCustomDomainCertificatesMetadata)"
#
#}
# Match by AppId to get proxy applications
$aadproxyapps = $aadapServPrinc | ForEach-Object { $aadapps | Where-Object AppId -eq $_.AppId }

# The documentation says "If you have one certificate that includes many of your applications, you only need to upload it with one application and it will also be assigned to the other relevant applications."
# That does not seem to be the case. Updating the certificate for one application only updated that single application, the rest keep using the old certificate.
# Perhaps it just takes a bit to update, but I thought it safer to just update all of them.
"Found $($aadproxyapps.count) applications to update"

$aadproxyapps | Foreach-Object {
# Read certificate and convert to Base64
$certBytes = [System.IO.File]::ReadAllBytes($PfxPath)
$certBase64 = [System.Convert]::ToBase64String($certBytes)

# Update each application
$aadproxyapps | ForEach-Object {
"Updating certificate for $($_.DisplayName)"
Set-AzureADApplicationProxyApplicationCustomDomainCertificate -ObjectId $_.ObjectId -PfxFilePath $PfxPath -Password $SecureCertPass

$body = @{
onPremisesPublishing = @{
verifiedCustomDomainKeyCredential = @{
type="X509CertAndPassword";
value = $certBase64
};

verifiedCustomDomainPasswordCredential = @{ value = $CertPass };
}
} | ConvertTo-Json -Depth 10

Update-MgBetaApplication -applicationid $_.Id -BodyParameter $body
}


Disconnect-AzureAD
$null = Disconnect-MgGraph