Category Archives: Automation

How to grant OAuth2 permissions to an Azure AD Application using PowerShell unattended / silently

You may know this button:There is no native Powershell command to grant OAuth permissions to an Azure AD Application, so I wrote a function for that. Note that this is NOT a supported way to grant permissions to an application because it does not follow the proper admin consent flow that applications normally use.

The great advantage of my method is that it can be used to grant permissions silently, AND to ‘hidden’ and/or multi-tenant applications that companies like Microsoft use for backend stuff like the Intune API. (e.g. the ‘Microsoft Intune Powershell’ multi-tenant application).

The function requires AzureAD and AzureRM modules installed!


Function Grant-OAuth2PermissionsToApp{
Param(
[Parameter(Mandatory=$true)]$Username, #global administrator username
[Parameter(Mandatory=$true)]$Password, #global administrator password
[Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to
)

Function Grant-OAuth2PermissionsToApp{
    Param(
        [Parameter(Mandatory=$true)]$Username, #global administrator username
        [Parameter(Mandatory=$true)]$Password, #global administrator password
        [Parameter(Mandatory=$true)]$azureAppId #application ID of the azure application you wish to admin-consent to
    )

    $secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
    $mycreds = New-Object System.Management.Automation.PSCredential ($Username, $secpasswd)
    $res = login-azurermaccount -Credential $mycreds
    $context = Get-AzureRmContext
    $tenantId = $context.Tenant.Id
    $refreshToken = @($context.TokenCache.ReadItems() | Where-Object {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
    $body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
    $apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
    $header = @{
    'Authorization' = 'Bearer ' + $apiToken.access_token
    'X-Requested-With'= 'XMLHttpRequest'
    'x-ms-client-request-id'= [guid]::NewGuid()
    'x-ms-correlation-id' = [guid]::NewGuid()}
    $url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$azureAppId/Consent?onBehalfOfAll=true"
    Invoke-RestMethod –Uri $url –Headers $header –Method POST -ErrorAction Stop
}

GITLAB: Grant-OAuth2PermissionsToApp.ps1

Exchange Online reconnect script v2

A few weeks ago I posted a script that would automatically, periodically, reconnect to Exchange Online. In field testing it would still prompt for credentials after 1-2 days, whatever I did.

So I took a different route and am now rewriting Microsofts’ module on the fly to no longer prompt for credentials. If you use below function to connect to Exchange Online, you should never receive reconnect prompts 🙂

disclaimer: don’t overwrite $o365Creds with invalid creds elsewhere in your script as those are used globally.


function buildResilientExchangeOnlineSession {
    Param(
        [Parameter(Mandatory=$true)]$o365Creds,
        $commandPrefix
    )
    Write-Verbose "Connecting to Exchange Online"
    Set-Variable -Scope Global -Name o365Creds -Value $o365Creds -Force
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $o365Creds -Authentication Basic -AllowRedirection
    Import-PSSession $Session -AllowClobber -DisableNameChecking
    Write-Verbose "Connected to Exchange Online, exporting module..."
    $temporaryModulePath = (Join-Path $Env:TEMP -ChildPath "temporaryEXOModule")
    $res = Export-PSSession -Session $Session -CommandName * -OutputModule $temporaryModulePath -AllowClobber -Force
    $temporaryModulePath = Join-Path $temporaryModulePath -ChildPath "temporaryEXOModule.psm1"
    Write-Verbose "Rewriting Exchange Online module, please wait..."
      $regex='^.*\bhost\.UI\.PromptForCredential\b.*$'
    (Get-Content $temporaryModulePath) -replace $regex, "-Credential `$global:o365Creds ``" | Set-Content $temporaryModulePath
    $Session | Remove-PSSession -Confirm:$False
    Write-Verbose "Module rewritten, re-importing..."
    if($commandPrefix){
        Import-Module -Name $temporaryModulePath -Prefix $commandPrefix -DisableNameChecking -WarningAction SilentlyContinue -Force
        Write-Verbose "Module imported, you may now use all Exchange Online commands using $commandPrefix as prefix"
    }else{
        Import-Module -Name $temporaryModulePath -DisableNameChecking -WarningAction SilentlyContinue -Force
        Write-Verbose "Module imported, you may now use all Exchange Online commands"
    }
    return $temporaryModulePath
}

download: https://gitlab.com/Lieben/assortedFunctions/blob/master/buildResilientExchangeOnlineSession.ps1

Redirecting My Documents to Box Drive, using Intune (Windows 10 MDM)

One of my customers is doing a full cloud-only pilot of Windows 10, Mobile (MDM) managed through Intune to leverage a least-infrastructure solution worldwide.

They’re using Azure AD, but opted out of Onedrive for Business and are using Box Drive instead.

To encourage their users to actually save data to Box instead of Onedrive or locally, I wrote a little Powershell script (since Intune native PS script deployment isn’t live yet).  This script checks if Box has been configured, if not it throws a little popup to the user. If it has, it redirects My Documents, and copies any existing content from it to Box.

I used Advanced Installer to wrap this in an MSI for easy deployment through Intune, and would like to share this with you 🙂

ZIP download: configureBoxRedirection_v1.02.zip

Zip contents:

  1. ps1 file which does the actual work
  2. vbs wrapper to run it silently (hidden windows)
  3. .aip file (advanced installer)
  4. .msi file (to roll out with Intune or other tools)

Update 10/10:

  1. added a caching mechanism to force Box Drive to locally cache files (normally Box only does this when they are opened)
  2. added a caching filter to prevent caching of files above 25MB to reduce initial bandwidth overhead

Update 04/12:

  1. added a 5 minute loop / wait cycle to allow box to initialize, as the script may otherwise run before Box can initialize

On-Demand MSI customization using Azure Functions

This post describes how you can use the WIX Toolkit or any DLL file in an Azure Function, in this case to edit an MSI file on the fly. The WIX Toolkit is free, but only runs on Windows. Azure Functions run on Windows too, isn’t that nice 🙂

So, an example use case could be my OnedriveMapper MSI file, which is installed with a configuration GUID property by an admin to customize OnedriveMapper. If that GUID was already in the MSI, no such parameter would be necessary.

Using an Azure function in a download link or http request, we could insert a GUID on the fly and create personalized MSI files on demand.

I’ll leave other applications to your imagination, let’s get started!

  1. Download the WIX toolkit (binaries)
  2. Extra Microsoft.Deployment.WindowsInstaller.dll
  3. Add it to the function files or host it at an URL somewhere. In my example, I’m hosting it at http://www.lieben.nu/wix/wix.dll
  4. Add your MSI file to your function files or host it at an URL somewhere. In my example, I’m hosting it at http://www.lieben.nu/wix/OnedriveMapper.msi
  5. Add the following code to the Azure Function:

Continue reading On-Demand MSI customization using Azure Functions