Secure Application Model

Secure Application Model: Azure AD, Az, Exchange Online & MS Online

I’ve made a PowerShell script you can use to connect to multiple Microsoft 365 services using the Secure Application Model. I’ve stored the Refresh Tokens and my Application Secrets in an Azure Key Vault (Secrets). This is the best and most secure way. Never hard-code these secrets in your PowerShell scripts.

You can have a look at this blogpost on how to create the Secure Application Azure AD application and generting the values above.

Usage is very simple. Just start the script with the following arguments.

-tenantDomain azurescene.onmicrosoft.com
-vaultName theNameOfYourKeyVault
-AzureADCon $true (connect with AzureAD?)
-AzCon $true (connect with Az?)
-MSolCon $true (connect to MS Online?)
-ExchangeCon $true (connect to Exchange Online?)

[cmdletbinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage="customer.onmicrosoft.com",
            Position=1
        )][string] $TenantDomain,
        [Parameter(
            Mandatory = $true,
            HelpMessage="Name of the Azure Key Vault",
            Position=2
        )][string] $vaultName,
        [Parameter(
            Mandatory = $false,
            HelpMessage="Connect to AzureAD?",
            Position=3  
        )][bool] $AzureADCon = $false,
        [Parameter(
            Mandatory = $false,
            HelpMessage="Connect to Az?",
            Position=4  
        )][bool] $AzCon = $false,
        [Parameter(
            Mandatory = $false,
            HelpMessage="Connect to MS online?",
            Position=5 
        )][bool] $MSolCon = $false,
        [Parameter(
            Mandatory = $false,
            HelpMessage="Connect to Exchange Online?",
            Position=6
        )][bool] $ExchangeCon = $false

    )

if (!$AzCon -and !$AzureADCon -and !$MSolCon -and !$ExchangeCon) {
    Write-Host
    Write-Host "Nothing to connect." -ForegroundColor Magenta
    Break
}
#endregion

#region Test Customer Tenant
Try {
    $customerTenantID = (Invoke-WebRequest https://login.windows.net/$TenantDomain/.well-known/openid-configuration|ConvertFrom-Json).token_endpoint.Split('/')[3]
} Catch {
    # Can't find customer tenant
    Break
}
#endregion

#region PartnerCenter
$PCModule = Get-Module -Name "PartnerCenter" -ListAvailable
if ($PCModule -eq $null) {
    Write-Host "PartnerCenter PowerShell Module not installed." -ForegroundColor Red
    Write-Host "Install with 'Install-Module -Name PartnerCenter'" -ForegroundColor Red
    break
}

Write-Host
Write-Host "PartnerCenter installed." -ForegroundColor Green
#endregion

#region AzureAD
$AADModule = Get-Module -Name "AzureAD" -ListAvailable
## AzureADPreview Installed?
if ($AADModule -eq $null) {
    $AADModule = Get-Module -Name "AzureADPreview" -ListAvailable
}

## No AzureAD or AzureADPreview installed?
If ($AADModule -eq $null) {
    Write-Host "AzureAD PowerShell Module not installed." -ForegroundColor Red
    Write-Host "Install with 'Install-Module -Name AzureAD'" -ForegroundColor Red
    break
}

Write-Host "AzureAD installed." -ForegroundColor Green
#endregion

#region Az
$AZModule = Get-Module -Name "Az.Accounts" -ListAvailable
if ($AZModule -eq $null) {
    Write-Host "Az PowerShell Module not installed." -ForegroundColor Red
    Write-Host "Install with 'Install-Module -Name Az'" -ForegroundColor Red
    break
}

Write-Host "Az Module installed." -ForegroundColor Green
#endregion

#region MSOnline
$MSOnline = Get-Module -Name "MSOnline" -ListAvailable
if ($MSOnline -eq $null) 
    {
    Write-Host "MSOnline PowerShell Module not installed." -ForegroundColor Red
    Write-Host "Install with 'Install-Module -Name MSOnline'" -ForegroundColor Red
    break
    }

Write-Host "MSOnline installed." -ForegroundColor Green
#endregion

#region Connect to services
if ($AADModule -and $PCModule -and $MSOnline -and $AZModule) {
    Write-Host
    Write-Host "Log on with your CSP Admin" -ForegroundColor Yellow
    Write-Host "Be sure you have read permission on Key Vault" -ForegroundColor Yellow

    Try { 
        Connect-AzAccount | Out-Null
        $AdminUser = (Get-AzContext).Account.Id
    } Catch {
        ## Connection went wrong
        break
    }

    #region Get Refresh Token from Azure Key Vault
    Try {
        $applicationID = (Get-AzKeyVaultSecret -vaultName $vaultName -name "applicationID").SecretValueText
        $applicationSecret = (Get-AzKeyVaultSecret -vaultName $vaultName -name "ApplicationSecret").SecretValueText
        $applicationSecret = ConvertTo-SecureString $applicationSecret -AsPlainText -Force
        $tenantID = (Get-AzKeyVaultSecret -vaultName $vaultName -name "tenantID").SecretValueText
        $refreshToken = (Get-AzKeyVaultSecret -vaultName $vaultName -name "RefreshToken").SecretValueText
        $ExchangerefreshToken = (Get-AzKeyVaultSecret -vaultName $vaultName -name "ExchangeRefreshToken").SecretValueText
    } Catch {
        Write-Host
        Write-Host "Something went wrong while fetching all information..." -ForegroundColor Red
        Break
    }
    #endregion

    #region Create Credentials with Tokens
    Try {
        $Credential = New-Object System.Management.Automation.PSCredential ($applicationID, $applicationSecret)
    } Catch {
        write-Host "Something went wrong while creating the credentials..." -ForegroundColor Red
        break       
    }
    #endregion

    #region Connect to Azure AD
    if ($AzureADCon) {
        $aadGraphToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $customerTenantID
        $graphToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $customerTenantID
        Connect-AzureAD -AadAccessToken $aadGraphToken.AccessToken -AccountId $AdminUser -MsAccessToken $graphToken.AccessToken -TenantId $customerTenantID
    }
    #endregion

    #region Connect to Az
    if ($AzCon) {
        $azureToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://management.azure.com/user_impersonation' -ServicePrincipal -Tenant $customerTenantID
        $graphToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $customerTenantID
        Connect-AzAccount -AccessToken $azureToken.AccessToken -AccountId $AdminUser -GraphAccessToken $graphToken.AccessToken -TenantId $customerTenantID
    }
    #endregion

    #region Connect to MS Online
    if ($MSolCon) {
        $aadGraphToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID
        $graphToken = New-PartnerAccessToken -ApplicationId $applicationID -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID
        Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken

        Write-Host 
        Write-Host "You should use " -NoNewline -ForegroundColor Yellow
        Write-Host "-TenantID 'CustomerTenantID' " -NoNewline -ForegroundColor Green
        Write-Host "when using MS Online commands" -ForegroundColor Yellow
    }
    #endregion

    #region Connect to Exchange Online
    if ($ExchangeCon) {
       $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customerTenantID
       $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force
       $credential = New-Object System.Management.Automation.PSCredential($AdminUser, $tokenValue)
       $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection 
       Import-PSSession $session -DisableNameChecking | Out-Null 
    }
}
#endregion

4 comments

  1. Hello and thanks for the script and article. I’m wondering what is required to be able to access your Azure Key Vault so easily. Are you needing to use Azure Cloud Shell for this to work?

    Thanks,
    Jeremy

    1. Hi Jeremy! You can you ‘Connect-AzAccount’ to connect to your Azure tenant with a user that has access to your key vault. After connecting, you can use ‘Get-AzKeyVaultSecret’ to get all the secrets out of the key vault – no Azure Cloud Shell needed.

      If you want to run this script without loggin in every time, you can use a certificate (https://docs.microsoft.com/en-us/powershell/module/az.accounts/connect-azaccount?view=azps-3.5.0).

      Of course its possible to hardcode the tokens/passwords in your script, but you shouldn’t do that.

      Thank you!

  2. Is it possible to use MSAL to get a token and then use it with Connect-MsolService ? thanks

Leave a Reply

Your email address will not be published. Required fields are marked *