Category Archives: Azure

Azure AD sign in and audit log retention

Often we, as cloud admins, need our audit or sign in logs. Usually, we need real-time data because, for example, we’re debugging why that one user has conditional access issues. But sometimes, we need to go back further than 30 days. And that is not something Azure does by default, but can be enabled:


Our options when exporting logs are limited to a Storage account, Log Analytics or an Event Hub. All these options offer multiple extraction methods to cover your transport needs to other systems. The default retention period is then forever, which is nice as we might need audit info going back a bit as hacks are usually discovered after about 206 days.

If you don’t have specific tools or requirements, I recommend setting up a Log Analytics workspace and connecting that to Azure AD:

Whichever method you choose, a P1 or P2 license is required. You only need a single license for the entire tenant when using the export audit / singin log functionality of AzureAD. Once configured, the Logs option directly bring you to the Log Analytics workspace search results:

I’ve briefly shown how to configure AzureAD to send audit and sign in logs to Log Analytics so you can go back further than 30 days. Stay tuned for the next post that will utilize these logs to dive deeper into Guest User activity.

Programmatically triggering a group licenses refresh for AzureAD

Azure AD allows us to assign licenses to groups, a nifty feature that has made a host of automation scripts dealing with bulk license assignment obsolete.

A problem I’ve encountered is that when you assign users to a group, license assignments are not processed right away, especially if you didn’t have enough licenses when you assigned the user to the group (and added licenses to the tenant later).

Azure AD has a button to trigger an update manually:

But of course, this can also be automated with PowerShell!

function Invoke-AzHAPIReprocessGroupLicenses{
    <#
        .SYNOPSIS
        reprocesses group license assignment

        .NOTES
        Author: Jos Lieben

        .PARAMETER AzureRMToken
        Use Get-azureRMToken to get a token for this parameter

        .PARAMETER groupGUID
        GUID of the group to reprocess licenses of
    
        Requires:
        - Global Administrator Credentials (non-CSP!)
        - AzureRM Module
        - supply result of get-azureRMToken function
    #>
    param(
        [Parameter(Mandatory = $true)]$AzureRMToken,
        [Parameter(Mandatory = $true)]$groupGUID
    )
    $header = @{
        'Authorization' = 'Bearer ' + $AzureRMToken
        '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/AccountSkus/Group/$groupGUID/Reprocess"
    Invoke-RestMethod –Uri $url –Headers $header –Method POST -Body $Null -UseBasicParsing -ErrorAction Stop -ContentType "application/json"
}

Source on GIT: https://gitlab.com/Lieben/assortedFunctions/blob/master/invoke-AzHAPIReprocessGroupLicenses.ps1https://gitlab.com/Lieben/assortedFunctions/blob/master/invoke-AzHAPIReprocessGroupLicenses.ps1

Disclaimer: the ‘hidden azure api’ is not officially supported.

Requires output from the Get-AzureRMToken function

Get an Office365 / Azure AD tenant ID from a user’s login name or domain

I often need a tenant ID for a given customer, the usual method to get it is to log in to the Azure portal and find it there. But what if you want to get the tenant ID programmatically? Without actually logging in? And you only know the log in name of a user? Or just one of the customer’s domain names?

Then this’ll help you out!

function get-tenantIdFromLogin(){
    <#
      .SYNOPSIS
      Retrieves an Office 365 / Azure AD tenant ID for a given user login name (email address)
      .EXAMPLE
      $tenantId = get-tenantIdFromLogin -Username you@domain.com
      .PARAMETER Username
      the UPN of a user
      .NOTES
      filename: get-tenantIdFromLogin.ps1
      author: Jos Lieben
      blog: www.lieben.nu
      created: 8/3/2019
    #>
    Param(
        [Parameter(Mandatory=$true)]$Username
    )
    $openIdInfo = Invoke-RestMethod "https://login.windows.net/$($Username.Split("@")[1])/.well-known/openid-configuration" -Method GET
    return $openIdInfo.userinfo_endpoint.Split("/")[3]
}

Obviously, you can also get the tenant ID by just filling out bogus info in front of the user’s login (e.g. bogus@ogd.nl), it’ll still work as only the domain part of the login is really used.

Hope this helps someone 🙂

Git link: https://gitlab.com/Lieben/assortedFunctions/blob/master/get-tenantIdFromLogin.ps1

Full AzureAD Applications Permission overview

So you’d like to know which applications are living in your AzureAD?

And you’d like to know which of those were added by your admins, and what permissions those applications have?

And you’d also like to know which applications your users are consenting to, and what rights those applications have on your users?

Look no further, I wrote a script to export all of that to Excel for you!

Application overview

Apps an admin has consented to and the type of rights it needs

Apps a user has consented to and the type of rights it needs

Apps to user mapping, for an easy overview of which user has consented to which app

Get it at:

Credits to Doug Finke for the Excel module I’m using!

 

Retrieving ALL Azure AD registered applications that Get-AzureRMAdApplication does not return

The Microsoft supplied Get-AzureRMADApplication Powershell cmdlet does not return all applications you can see in the Enterprise Applications and App registrations blades in Azure AD.

In addition, Get-AzureRmAdApplication also does not return information such as:

  • Publisher Name
  • logoUrl
  • tags
  • enabled/disabled status
  • if it is a MicrosoftFirstParty application

So, here’s a custom PS function to help you out: https://gitlab.com/Lieben/assortedFunctions/blob/master/get-azureRMADAllApplications.ps1

It requires a special token generated by my get-AzureRMtoken function to log in.

As usual when using unsupported API’s, be careful!

Retrieving a headless silent token for main.iam.ad.ext.azure.com using Powershell

A lot of the things we can click on in the Azure Portal cannot be done through Powershell Cmdlets published by Microsoft.

However, using Fiddler, we can see that there is a ‘hidden’ API we can use, for example, to set permissions. I’ve written a ‘clean’ function to retrieve this token silently that you can use in your scripts, it is not compatible with MFA.

https://gitlab.com/Lieben/assortedFunctions/blob/master/get-azureRMtoken.ps1

Please be careful using this for production workflows as this is NOT supported by Microsoft.

Getting remoteapps through vm custom extension on Azure session brokers

So I wanted to retrieve the remoteapps present on VM’s in a uniform way, without logging in to either VM’s or database.

Using a custom extension, I tried to execute the Get-RDRemoteApp command and got the following:

Get-RDRemoteApp : A Remote Desktop Services deployment does not exist on server. This operation can be perfor
med after creating a deployment. For information about creating a deployment

Apparently, all the powershell commands for RDS require that you DON’T run them under SYSTEM. Of course VMExtensions run under SYSTEM. So, to get all remoteapps in a RDS deployment, execute the following Powershell script as VMExtension on a connection broker VM:

 

$farms = get-childitem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\CentralPublishedResources\PublishedFarms"
foreach($farm in $farms){
    (get-childItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\CentralPublishedResources\PublishedFarms\$($farm.PSChildName)\Applications").PSChildName
}

To register this Powershell script as a VM extension and retrieve the results

  1. Save the above PS code to a file
  2. Upload the file somewhere (e.g. public blob storage)
  3. Get the URL of the File
  4. Use Login-AzureRMAccount
  5. Execute Set-AzureRmVMCustomScriptExtension -FileUri URL TO SCRIPT -Run FILENAME OF SCRIPT -VMName VMNAME -Name “RetrieveRemoteApps” -ResourceGroupName RESOURCEGROUP NAME -location “westeurope” -ForceRerun $(New-Guid).Guid
  6. To retrieve the list (after execution): [regex]::Replace(((Get-AzureRmVMDiagnosticsExtension -ResourceGroupName RESOURCEGROUP NAME -VMName VM NAME -Name “RetrieveRemoteApps” -Status).SubStatuses[0].Message), “\\n”, “`n”)

Running an Azure runbook on a System hybrid worker

Azure Runbooks are usually run in the cloud (on an automatically assigned ‘Microsoft’ host) or on a Hybrid Worker Group.

Hybrid Worker Groups consist of 1 or more machines, but there are also ‘System hybrid workers’, which are machines monitored by OMS. If you want to execute a Powershell script directly on a specific System hybrid worker, or on a specific group member of a worker group, you can use Powershell and specify the host instead of the group:

Start-AzureRmAutomationRunbook -Name “RunbookName” -RunOn hybridWorkerName -AutomationAccountName “automationaccount” -ResourceGroupName “resourcegroup”

If you try this on a System Hybrid Worker, you’ll get an error on the device itself and in the runbook results:

“Invalid Runbook xxx Authenticode signature status – NotSigned”.

This can be ‘fixed’ by setting the following registry key to ‘False’:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HybridRunbookWorker\GuidOfYourWorker\EnableSignatureValidation

Et voila, the runbook runs nicely. I do not recommend disabling this key in production, this article is purely to share knowledge, and if someone knows how to do this without disabling this key, I’d love to hear it!

Remove-StaleIntuneDevices using a scheduled Azure Runbook

I recently came upon a really cool post by Josh and Sarah that explains how to clean up stale devices in Intune using the Graph API.

As I want to run this from an Azure runbook, silently, I had to modify it a little so it automatically consents to azure app permissions and logs in silently. If you’d like to use it, feel free to add it from the Azure gallery (search for Lieben) or download it yourself.

Make sure you’ve also imported the AzureAD and AzureRM modules into your automation account, and configured a credential object for the script to use.

GitLab: Remove-StaleIntuneDevicesForAzureAutomation.ps1

Technet: Remove-Stale-Intune-4b07488a

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&amp;amp;refresh_token=$($refreshToken)&amp;amp;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