User Profile
Mark_Wahl
Joined 9 years ago
User Widgets
Recent Discussions
Intro to querying Azure AD sign-in and audit logs held in Azure Monitor from PowerShell
Some questions I'm asked frequently about Azure AD - how can I see and retain more than 30 days of audit events from Azure AD features? And how can I get that audit history programmatically, without needing to sign in as a highly-privileged Azure AD administrator, in order to download records for a report to or answer an auditor’s inquiry? Last year we announced that organizations with Azure AD Premium and an Azure subscription could start to build custom reports on their Azure AD audit and sign in logs, by configuring Azure AD to send those logs to Azure Monitor. We also built several reports for sign in analysis as Azure AD workbooks, and showed to set triggers for alert notifications. Once you've configured Azure AD to send logs to Azure Monitor, you can also access those logs through PowerShell, sending queries from scripts or from the PowerShell command line, without needing to be a Global Admin in the tenant. Because Azure services have changed their name in the past few years, it's sometimes challenging to figure out which PowerShell command to use. So I've written down a few steps I used when learning how to query Azure AD logs that have been sent to Azure Monitor. Before you begin, if you haven't already configured this integration between Azure AD and Azure Monitor, you'll need to follow the steps to Integrate Azure AD logs with Azure Monitor logs. Next, you'll want to ensure you (or the user or service principal who will be authenticating to Azure AD) are in the appropriate Azure role in the in the Log Analytics workspace, either the Log Analytics Reader role, or the Log Analytics Contributor role. You can set this role assignment in the Azure Portal by locating the Log Analytics workspace, clicking on "Access Control (IAM)" and clicking Add to add a role assignment. Then, launch PowerShell, and then install the Azure PowerShell module, if you haven’t already, by typing install-module -Name az -allowClobber -Scope CurrentUser Now you're ready to authenticate to Azure AD, and retrieve the id of the Log Analytics workspace you’ll be querying. If you have only a single Azure subscription, and a single Log Analytics workspace, then authenticate to Azure AD, connecting to that subscription and retrieving that workspace, by typing Connect-AzAccount $wks = Get-AzOperationalInsightsWorkspace It's important to note that Get-AzOperationalInsightsWorkspace operates in one subscription at a time. If you have multiple Azure subscriptions, then you'll want to ensure you connect to the one which has the Log Analytics workspace with the Azure AD logs. The cmdlets Connect-AzAccount $subs = Get-AzSubscription $subs | ft displays a list of subscriptions, and then find the id of the subscription which has the Log Analytics workspace. You can re-authenticate and associate your PowerShell session to that subscription using a command such as Connect-AzAccount –Subscription $subs[0].id . (And to learn more about how to authenticate to Azure from PowerShell, including non-interactively, see Sign in with Azure PowerShell. ) If you have multiple Log Analytics workspaces in that subscription, then the cmdlet Get-AzOperationalInsightsWorkspace returns the list of workspaces, so you can find the one which has the Azure AD logs. The CustomerId field returned by this cmdlet is the same as the value of the "Workspace id" displayed in the Azure Portal in the Log Analytics workspace overview. $wks = Get-AzOperationalInsightsWorkspace $wks | ft CustomerId, Name Finally, once you have a workspace identified, you can use Invoke-AzOperationalInsightsQuery to send a Kusto query to that workspace. These queries are written Kusto query language. For example, you can retrieve old sign in records from the Log Analytics workspace, with a query like $sQuery = "SigninLogs | where TimeGenerated > ago(3653d) | order by TimeGenerated asc | limit 10" $sResponse = Invoke-AzOperationalInsightsQuery -WorkspaceId $wks[0].CustomerId -Query $sQuery $sResponse.Results | ft TimeGenerated,userDisplayName,UserPrincipalName Or audit records with $aQuery = "AuditLogs | where TimeGenerated > ago(3653d) | order by TimeGenerated asc | limit 10" $aResponse = Invoke-AzOperationalInsightsQuery -WorkspaceId $wks[0].CustomerId -Query $aQuery $aResponse.Results |ft TimeGenerated,Category,OperationName,Result I hope to find more interesting examples of Kusto queries for the Sign in and Audit logs in future. Thanks!Example of retrieving Azure AD access reviews via Microsoft Graph
The Azure AD access reviews feature now has an API in the Microsoft Graph beta endpoint. The list of API methods is at https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/resources/accessreviews_root. While we are in progress of adding access reviews to Azure AD PowerShell and examples of using access reviews from other development platforms to our documentation, the following code sample may be of interest. Azure AD access reviews data model The Azure AD access reviews feature adds the following resource types: accessReview: represents an access review. This can be a one-time review, a recurring review series, or an instance of a recurring review. businessFlowTemplate: the business flow template determines the type resource on which an access review is to be performed. The identifier of a template, such as to review guest members of a group, is supplied by the caller when creating an access review. (The business flow template objects are read only, they are automatically generated when the global administrator onboards the tenant to use the access reviews feature. No additional business flow templates can be created.) program: represents an Azure AD access review program. A program is a container, holding program controls. A tenant can have one or more programs. Each control links an access review to a program, to make it easier to locate related access reviews. Each tenant that has onboarded Azure AD access reviews has one program, `Default program`. A global administrator can create additional programs, for example to represent compliance initiatives. programControl: represents a control, which links an access review to a particular program. programControlType: the program control type is used when associating a control to a program, to indicate the type of access review the control is for. (The program control type objects are read only, they are automatically generated when the global administrator onboards the tenant to use the access reviews feature. No additional program control types can be created.) Understanding authorization requirements The Azure AD access reviews API performs three checks: First, has the tenant onboarded to the feature – Azure AD access reviews or, in the case of access reviews of Azure AD roles, Azure AD PIM. Both of these features are included in Azure AD Premium P2, and require the administrator to have used the features at least once in order to permit the APIs to be called. If you have not already used Azure AD access reviews, the section “Enable Azure AD access reviews in your tenant” below onboards the Azure AD access reviews feature so you can try out the APIs. Second, does the application have the necessary permissions. The permissions available for these APIs are: AccessReview.Read.All: read access reviews AccessReview.ReadWrite.All: read, create, update and delete access reviews ProgramControl.Read.All: read programs and controls ProgramControl.ReadWrite.All: read, create, update and delete programs and controls If you do not already have those permissions on an application, the section “Register an Azure AD application which can call the access reviews Graph API” below creates a new application and assigns it read permissions. (You can change the scenario to assign it read and write permission). Third, does the user have the necessary permissions. This is determined by the calling user’s directory role: Target Resource Desired Operation Required directory role of the user, in addition to the application permission Access review of an Azure AD role Read Global Administrator, Security Administrator, Security Reader or Privileged Role Administrator Create, Update or Delete Global Administrator or Privileged Role Administrator Access review of a group or app Read Global Administrator, Security Administrator, Security Reader or User Administrator Create, Update or Delete Global Administrator or User Administrator Programs or controls Read Global Administrator, Security Administrator, Security Reader or User Administrator Create, Update or Delete Global Administrator or User Administrator Enable Azure AD access reviews in your tenant This example assumes you have already onboarded Azure AD access reviews in your tenant directory. If you have already done so, then skip to the next section “Register an Azure AD application which has permissions to call the access reviews API in Graph”. Otherwise, continue with these steps to ensure the feature is onboarded so the APIs will return some data. Log into the Azure portal as a global administrator. Ensure that your organization has Azure AD Premium P2 or EMS E5 subscription active. If not, click on https://portal.azure.com/#blade/Microsoft_AAD_IAM/TryBuyProductBlade and activate a trial of Enterprise Mobility + Security E5. Otherwise, if your organization has an active subscription, continue at the next step. Navigate to the Azure AD extension, and click on “Access reviews” on the right hand side under “Other capabilities”. If you have not already onboarded Azure AD access reviews in your organization, onboard it now. Click on “Programs” and ensure there is at least one program listed. At this point you can create additional access reviews if you wish. Register an Azure AD application which has permissions to call the access reviews API in Graph The Graph authorization model requires that an application must be consented by a user or administrator prior to accessing an organization’s data. Log into the Azure portal as a global administrator. Navigate to the Azure AD extension, and click on “App registrations” in the MANAGE section, to land at the page https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps Click on “New application registration” button at the top of the page. Provide a name for the application that is different from any other application in your tenant’s directory (e.g., “graphsample”), change the Application type to Native, and provide the following as the Redirect URI: urn:ietf:wg:oauth:2.0:oob Click “Create”. When the application is registered, copy the Application ID value, and save the value for later. Click on Settings, then click on “Required permissions”. Click on “Add”. Click on “Select an API”, click on “Microsoft Graph”, and then click “Select”. Azure AD access reviews uses the following delegated permissions: Read all access reviews that use can access, Manage all access reviews that user can access, Read all programs that user can access, and Manage all programs that user can access. This example application requires only the permissions: Read all access reviews that user can access Read all programs that user can access Put a checkbox by those two permissions, and click “Select”. Click “Done”. (Sample only) Ensure PowerShell and the ADAL libraries are on your computer The Microsoft Graph requires the application calling it to have an access token. In this example, the sample code to use the API will leverage the ADAL library which is automatically installed when using Azure AD PowerShell cmdlets. Ensure that you have PowerShell 3.0 or later, and .NET Framework 4.5 installed on your computer. Ensure that you have either the Azure AD PowerShell v2 General Availability or Preview modules installed on your computer. If not, more information on how to install them is at https://docs.microsoft.com/en-us/powershell/azure/active-directory/install-adv2?view=azureadps-2.0. Try using Connect-AzureAD to ensure that you can authenticate to Azure AD as a global administrator. Retrieving the token and calling the API Create a file named “access-reviews-example1.ps1” whose context in the sample PowerShell from the end of this post. Start PowerShell. Change to the directory where the access-reviews-example1.ps1 script is located. Invoke the script, providing on the command line -User with the User principal name (UPN) of a global administrator, and -ClientId with the application ID value from earlier. For example, .\access-reviews-example1.ps1 -User ga@contoso.onmicrosoft.com -ClientId 280d7b83-8d0a-4ee7-8f1a-064ec36d1fa1 When the script is run for the first time in a PowerShell session, you will be asked to authenticate. For the purposes of this example, ensure that you sign in as a global administrator. After authenticating, the first time the script is run for a particular application, you will be prompted to consent the application use of permissions. Once consented, the script will use the token to call Microsoft Graph and retrieve programs, controls, business flow templates and access reviews, and write a summary of them to the PowerShell window. Note that the authorization is only applicable to the global administrator who consented to the application, and only for that tenant. If other users in the organization or in other applications also wish to use the application, additional steps are required for admin consent. See the article https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-devhowto-multi-tenant-overview for more information on admin consent in applications. Sample (PowerShell) # Example for using Azure AD access reviews in Microsoft Graph # # This material is provided "AS-IS" and has no warranty. # # Last updated August 22, 2018 # # This example is adapted from the documentation example located at # https://docs.microsoft.com/en-us/intune/intune-graph-apis # # Param( [Parameter(Mandatory=$true)][string]$User, [Parameter(Mandatory=$true)][string]$ClientId ) # from Intune graph API samples function Get-GraphExampleAuthToken { [cmdletbinding()] param ( [Parameter(Mandatory = $true)] $User, [Parameter(Mandatory = $true)] $ClientId, [Parameter()] $TenantDomain ) $userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User $tenant = $userUpn.Host if ($TenantDomain -ne $null) { $tenant = $TenantDomain } Write-Verbose "Checking for AzureAD module..." $AadModule = Get-Module -Name "AzureAD" -ListAvailable if ($AadModule -eq $null) { Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview" $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable } if ($AadModule -eq $null) { write-host write-host "AzureAD Powershell module not installed..." -f Red write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow write-host "Script can't continue..." -f Red write-host exit } # Getting path to ActiveDirectory Assemblies # If the module count is greater than 1 find the latest version if ($AadModule.count -gt 1) { $Latest_Version = ($AadModule | select version | Sort-Object)[-1] $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version } $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } else { $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null $redirectUri = "urn:ietf:wg:oauth:2.0:oob" $resourceAppIdURI = "https://graph.microsoft.com" $authority = "https://login.microsoftonline.com/$Tenant" try { $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority # https://msdn.microsoft.com/library/azure/microsoft.identitymodel.clients.activedirectory.promptbehavior.aspx # Change the prompt behaviour to force credentials each time: Auto, Always, Never, RefreshSession $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto" $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId") $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $ClientId, $redirectUri, $platformParameters, $userId).Result # If the accesstoken is valid then create the authentication header if ($authResult.AccessToken) { # Creating header for Authorization token $authHeader = @{ 'Content-Type' = 'application/json' 'Authorization' = "Bearer " + $authResult.AccessToken 'ExpiresOn' = $authResult.ExpiresOn } return $authHeader } else { Write-Host Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red Write-Host break } } catch { write-host $_.Exception.Message -f Red write-host $_.Exception.ItemName -f Red write-host break } } # start of access review specific example function Get-GraphExampleProgramControls($authHeaders,$programId) { $uri1 = "https://graph.microsoft.com/beta/programs('" + $programId + "')/controls" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($c in $val1.Value) { $cid = $c.controlId $displayname = '"' + $c.displayName + '"' Write-Host "control $cid $displayname" } } function Get-GraphExamplePrograms($authHeaders) { $uri1 = "https://graph.microsoft.com/beta/programs" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($program in $val1.Value) { $id = $program.id $displayname = '"' + $program.displayName + '"' Write-Host "program $id $displayName" Get-GraphExampleProgramControls $authHeaders $id Write-Host "" } } function Get-GraphExampleAccessReviewDecisions($authHeaders,$arid) { $uri1 = 'https://graph.microsoft.com/beta/accessReviews(' + "'" + $arid + "')/decisions" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($ard in $val1.Value) { $rr = $ard.reviewResult $upn = $ard.userPrincipalName Write-Host "access review decision $upn $rr" } Write-Host "" } function Get-GraphExampleAccessReviewInstances($authHeaders,$arid) { $uri1 = 'https://graph.microsoft.com/beta/accessReviews(' + "'" + $arid + "')/instances" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($ard in $val1.Value) { $iid = $ard.id $start = $ard.startDateTime $end = $ard.endDateTime $status = $ard.status Write-Host "access review instance $start $end $status" if ($status -ne "NotStarted") { Get-GraphExampleAccessReviewDecisions $authHeaders $iid } } Write-Host "" } function Get-GraphExampleAccessReviews($authHeaders,$bftid) { $uri1 = 'https://graph.microsoft.com/beta/accessReviews?$filter=businessFlowTemplateId%20eq%20' + "'" + $bftid + "'" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($ar in $val1.Value) { $id = $ar.id $displayname = '"' + $ar.displayName + '"' $startDateTime = $ar.startDateTime $status = $ar.status Write-Host "access review $id $displayName $startDateTime $status" Get-GraphExampleAccessReviewDecisions $authHeaders $id Get-GraphExampleAccessReviewInstances $authHeaders $id } } function Get-GraphExampleBusinessFlowTemplates($authHeaders) { $uri1 = "https://graph.microsoft.com/beta/businessFlowTemplates" Write-Host "GET $uri1" $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get $val1 = ConvertFrom-Json $resp1.Content foreach ($bft in $val1.Value) { $id = $bft.id Write-Host "business flow template $id" Get-GraphExampleAccessReviews $authHeaders $id Write-Host "" } } $authHeaders = Get-GraphExampleAuthToken -User $User -ClientId $ClientId Get-GraphExamplePrograms $authHeaders Get-GraphExampleBusinessFlowTemplates $authHeadersExample how to create Azure AD access reviews using Microsoft Graph app permissions with PowerShell
The Azure AD access reviews feature is part of Microsoft Graph, with a list of methods at https://docs.microsoft.com/en-us/graph/api/resources/accessreviews-root?view=graph-rest-beta. An earlier blog post included an example of how a user, such as a Security Reader, could retrieve all the programs, controls and access reviews via Microsoft Graph, in PowerShell. In this post, I'll show an example PowerShell script that uses the new application permission AccessReview.ReadWrite.Membership. . Application permissions don’t need the app to have a logged in user to call Graph, so you can use this to automatically to create and retrieve access reviews from scheduled jobs or as part of your existing automation. Code Sample Prerequisite #1: Azure AD PowerShell To set up this code sample, you’ll need an Azure AD tenant where you’re a global administrator. In this example, as with the previous blog post, the sample code to use the API leverages the ADAL library to retrieve an access token used by Microsoft Graph. The ADAL library is automatically installed when you install Azure AD PowerShell cmdlets. So before you begin, ensure that you have PowerShell 3.0 or later,.NET Framework 4.5 and either the Azure AD PowerShell v2 General Availability or Preview modules installed on your computer. If you don’t have Azure AD PowerShell installed yet, see https://docs.microsoft.com/en-us/powershell/azure/active-directory/install-adv2?view=azureadps-2.0. Try the Connect-AzureAD command to ensure that you can authenticate to Azure AD as a global administrator. Also try Get-AzureADUser to make sure you can retrieve users, as you'll need a user ID later on. Code Sample Prerequisite #2: Azure AD access reviews This example assumes you have already onboarded Azure AD access reviews in your tenant directory. If you have already done so, then skip to the next section “Register an Azure AD application”. Otherwise, continue with these steps to ensure the feature is onboarded so the APIs will allow access reviews to be created and return some data. Log into the Azure portal as a global administrator. Ensure that your organization has Azure AD Premium P2 or EMS E5 subscription active. If not, click https://portal.azure.com/#blade/Microsoft_AAD_IAM/TryBuyProductBlade and activate a trial of Enterprise Mobility + Security E5. Otherwise, if your organization has an active subscription, continue at the next step. Navigate to Azure Active Directory, and then click Identity Governance on the left hand side. Click Access reviews. If no one has onboarded Azure AD access reviews in your organization, onboard it now. Register an Azure AD application Next, you'll need to create an app registration with the new permission, to allow your application to call the access reviews API in Microsoft Graph. If you haven’t created an app registration lately, the user interface in the Azure portal has changed. Here’s how to create an app with the updated UI. Log into the Azure portal as a global administrator. In the Azure portal, go to Azure Active Directory, and then click App registrations on the left. Click New registration. Give your app a name, and then click Register. Copy and save for later the application (client) ID that appears after the app is registered. On the left, click API permissions. Click Add a permission, click Microsoft Graph, and then click Application permissions. In the Select permissions list, expand AccessReview and select AccessReview.ReadWrite.Membership. While here, though not required for this sample, you might want to expand Group and give the app the permission Group.Read.All, and expand User and give the app the User.Read.All permission, Click Add permissions. Click to Grant admin consent for <your tenant> and then click Yes. The status for each permission the app needs should change to a green checkmark, indicating consent was granted. On the left, click Certificates & secrets. Click New client secret and then for Expires select Never. Click Add. Copy and save locally the value of the secret that appears- you won’t see it again after you leave this part of the UI. At this point, you’ll have a client app ID and a client secret. In real life you'd probably want to store the secret in Azure Automation, Azure Key Vault, or similar. Create and retrieve access reviews using Graph Next, here's how to try out Microsoft Graph API requests when authenticated as an application, using a PowerShell script to be your application. I'll assume you have Azure AD v2 PowerShell cmdlets already installed - the script uses the Azure AD library included in those modules for authentication. Save the PowerShell below to a file named sample-ar-app-permissions.psm1. Open a new PowerShell window, change to the directory where the file is located and type Import-Module .\sample-ar-app-permissions.psm1 Type Connect-AzureADMSARSample. This obtains a token needed for the service principal to call Graph. You’ll be prompted to provide the following information: ClientApplicationId ClientSecret TenantDomain (e.g. demo….onmicrosoft.com) To create a new access review, use the command New-AzureADMSARSampleAccessReview. To try out this command, you’ll need to have an Azure AD group with members and owners – the owners will be the reviewers. You’ll be prompted to provide the following information: DisplayName: (a display name for the access review) ReviewedEntityId: (the object ID of a group whose members are to be reviewed) OwnerUserId: (the object ID of a user such as an admin who will be listed as the owner of a review – since apps can’t own access reviews) If successful, the command will return the ID of the new access review. If you see an error about permissions, please note that app registration may take a few minutes to be set up in the directory – if the Graph calls don’t work right away, try again an hour later. Type Get-AzureADMSARSampleAccessReview to see the access review that you've created. I expect we’ll bring commands to create and query access reviews into the Azure AD PowerShell module in the future, which will remove the need for Connect-AzureADMSARSample. Please let us know about any other feedback/suggestions you have for more code samples. Thanks, Mark Wahl # Example for creating and retrieving the results of an Azure AD access review via Microsoft Graph using application permissions # # This material is provided "AS-IS" and has no warranty. # # Last updated August 2019 # # This example is adapted from the documentation example located at # https://docs.microsoft.com/en-us/intune/intune-graph-apis # # # the following functions are from Intune graph API samples, adapted for service principal authentication function Get-GraphExampleAuthTokenServicePrincipal { [cmdletbinding()] param ( [Parameter(Mandatory = $true)] $ClientId, [Parameter(Mandatory = $true)] $ClientSecret, [Parameter(Mandatory = $true)] $TenantDomain ) $tenant = $TenantDomain Write-Verbose "Checking for AzureAD module..." $AadModule = Get-Module -Name "AzureAD" -ListAvailable if ($AadModule -eq $null) { write-verbose "AzureAD PowerShell module not found, looking for AzureADPreview" $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable } if ($AadModule -eq $null) { write-output write-error "AzureAD Powershell module not installed..." write-output "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" write-output "Script can't continue..." write-output return "" } # Getting path to ActiveDirectory Assemblies # If the module count is greater than 1 find the latest version if ($AadModule.count -gt 1) { write-verbose "multiple module versions" $Latest_Version = ($AadModule | select version | Sort-Object)[-1] $aadModule = $AadModule | ? { $_.version -eq $Latest_Version.version } $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } else { write-verbose "single module version" $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll" } Write-verbose "loading $adal and $adalforms" [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null write-verbose "DLLs loaded" # $redirectUri = "urn:ietf:wg:oauth:2.0:oob" $resourceAppIdURI = "https://graph.microsoft.com" $authority = "https://login.microsoftonline.com/$Tenant" try { write-verbose "instantiating ADAL objects for $authority" $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority write-verbose "client $ClientId $clientSecret" $clientCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential" -ArgumentList ($ClientId,$ClientSecret) write-verbose "acquiring token for $resourceAppIdURI" # AuthenticationResult authResult = await authContext.AcquireTokenAsync(BatchResourceUri, new ClientCredential(ClientId, ClientKey)); # if you get an error about PowerShell not being able to find this method with 2 parameters, it means there is another version of ADAL DLL already in the process space of your PowerShell environment. $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $clientCredential).Result # If the accesstoken is valid then create the authentication header if ($authResult.AccessToken) { write-verbose "acquired token" # Creating header for Authorization token $authHeader = @{ 'Content-Type' = 'application/json' 'Authorization' = "Bearer " + $authResult.AccessToken 'ExpiresOn' = $authResult.ExpiresOn } return $authHeader } else { write-output "" write-output "Authorization Access Token is null, please re-run authentication..." write-output "" break } } catch { write-output $_.Exception.Message write-output $_.Exception.ItemName write-output "" break } } $_SampleInternalAuthNHeaders = @() # exported module member function Connect-AzureADMSARSample { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$ClientApplicationId, [Parameter(Mandatory=$true)] [string]$ClientSecret, # base64 client secret. Note this as a command line parameter is for testing purposes only [Parameter(Mandatory=$true)] [string]$TenantDomain # e.g., microsoft.onmicrosoft.com ) $script:_SampleInternalAuthNHeaders = @() $authHeaders = Get-GraphExampleAuthTokenServicePrincipal -ClientId $ClientApplicationId -ClientSecret $ClientSecret -TenantDomain $TenantDomain $script:_SampleInternalAuthNHeaders = $authHeaders } function Get-InternalAuthNHeaders { [CmdletBinding()] param() try { $authResult = $script:_SampleInternalAuthNHeaders if ($authResult.Length -eq @()) { Throw "Connect-AzureADMSARSample must be called first" } } catch { Throw # "Connect-AzureADMSControls must be called first" } return $authResult } function New-GraphExampleAccessReview($authHeaders,$displayName,$reviewedObjectId,$reviewerType,$businessFlowTemplateId,$description,$durationInDays,$ownerUserId) { $recurrenceSettings = @{ recurrenceType = "onetime"; recurrenceEndType = "endBy"; durationInDays = 0; recurrenceCount = 0; } $autoReviewSettings = @{ notReviewedResult = "Approve" # also use "Deny" or "Recommendation" } $settings = @{ mailNotificationsEnabled = $true; remindersEnabled = $true; justificationRequiredOnApproval = $false; autoReviewEnabled = $true; activityDurationInDays = 30; autoApplyReviewResultsEnabled = $false; accessRecommendationsEnabled = $true; recurrenceSettings = $recurrenceSettings; autoReviewSettings = $autoReviewSettings; } $reviewedEntity = [pscustomobject]@{ id = $reviewedObjectId } $owner = [pscustomobject]@{ id = $ownerUserId } $now = Get-Date $ts = Get-Date $now.ToUniversalTime() -format "s" $startDate = $ts + "Z" $ts = Get-Date $now.AddDays($durationInDays).ToUniversalTime() -format "s" $endDate = $ts + "Z" $bodyObj = [pscustomobject]@{ displayName = $displayName; startDateTime = $startDate; endDateTime = $endDate; reviewedEntity = $reviewedEntity; reviewerType = $reviewerType; businessFlowTemplateId = $businessFlowTemplateId; description = $description; settings = $settings; createdBy = $owner; } # $body = ConvertTo-Json $bodyObj -compress # ensure it contains "Content-Type" = "application/json"; $requestHeadersp = @{ "Content-Length" = $body.Length } $requestHeadersp += $authHeaders $uri1 = "https://graph.microsoft.com/beta/accessReviews" $resp1 = Invoke-WebRequest -UseBasicParsing -Headers $requestHeadersp -Uri $uri1 -Method Post -Body $body if ($resp1 -eq $null -or $resp1.Content -eq $null) { throw "error repsonse from $uri1" } $val1 = ConvertFrom-Json $resp1.Content return $val1.id } function New-AzureADMSARSampleAccessReview { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$DisplayName, # of the review to create [Parameter()] [string]$Description = "", # of the review to create [Parameter()] [int]$DurationInDays=30, [Parameter(Mandatory=$true)] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$ReviewedEntityId, [Parameter()] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$BusinessFlowTemplateId = "6e4f3d20-c5c3-407f-9695-8460952bcc68", #business flow template 6e4f3d20-c5c3-407f-9695-8460952bcc68 for Access reviews of memberships of a group [Parameter()] [string]$ReviewerType = "entityOwners", [Parameter(Mandatory=$true)] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$OwnerUserId # a user who has an email address ) $authHeaders = Get-InternalAuthNHeaders $reviewId = New-GraphExampleAccessReview $authHeaders $DisplayName $ReviewedEntityId $ReviewerType $BusinessFlowTemplateId $Description $durationInDays $OwnerUserId return $reviewId } function Get-GraphExampleAccessReviews($authHeaders,$businessFlowTemplateId) { $uri1 = 'https://graph.microsoft.com/beta/accessReviews?$filter=businessFlowTemplateId%20eq%20' + "'" + $businessFlowTemplateId + "'" $results = @() do { $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get if ($resp1 -eq $null -or $resp1.Content -eq $null) { throw "error response from $uri1" } $val1 = ConvertFrom-Json $resp1.Content foreach ($i in $val1.value) { $results += $i } $uri1 = $val1.'@odata.nextLink' # Odata list may have more } while ($uri1 -ne $null) return $results } function Get-GraphExampleAccessReview($authHeaders,$reviewId) { $uri1 = "https://graph.microsoft.com/beta/accessReviews/" + $reviewId $resp1 = Invoke-WebRequest -UseBasicParsing -headers $authHeaders -Uri $uri1 -Method Get if ($resp1 -eq $null -or $resp1.Content -eq $null) { throw "error response from $uri1" } $val1 = ConvertFrom-Json $resp1.Content return $val1 } function Get-AzureADMSARSampleAccessReview { [CmdletBinding()] param( [Parameter()] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$ReviewId, [Parameter()] [ValidateScript({ try { [System.Guid]::Parse($_) | Out-Null $true } catch { throw "$_ is not a valid GUID" } })] [string]$BusinessFlowTemplateId = "6e4f3d20-c5c3-407f-9695-8460952bcc68" ) #business flow template 842169fe-e1b7-4ce9-98b6-6a9db02eec6b for Access reviews of guest user memberships of a group #business flow template 7fbc909b-efe1-4c72-8ae6-99cb30b882de for Access reviews of guest user assignments to an application #business flow template 50839a84-e23c-44a7-a8cc-16e162afc656 for Access reviews of assignments to an application #business flow template 6e4f3d20-c5c3-407f-9695-8460952bcc68 for Access reviews of memberships of a group $authHeaders = Get-InternalAuthNHeaders if ($ReviewId -ne $null) { if ($ReviewId.Length -ge 1) { $reviewObj = Get-GraphExampleAccessReview $authHeaders $ReviewId $reviews = @() $reviews += $reviewObj return $reviewObj } } $res = Get-GraphExampleAccessReviews $authHeaders $BusinessFlowTemplateId return $res } ### export-modulemember -function Connect-AzureADMSARSample export-modulemember -function Get-AzureADMSARSampleAccessReview export-modulemember -function New-AzureADMSARSampleAccessReviewRe: Access reviews for attestation requires Azure AD Premium 2
Yes, Azure AD access reviews will, when it GAs, likely be included in Azure AD Premium P2. Azure AD Premium P2 today includes identity protection and privileged identity management, as well as the features of Azure AD Premium P1. (See https://azure.microsoft.com/en-us/pricing/details/active-directory/ ; Azure AD Premium P2 can be purchased as its own SKU, and is part of EMS E5.)3.5KViews2likes0CommentsRe: AAD Application Proxy and B2B Users
It is correct that the user does not need to authenticate themselves to the AD DS domains. However the documentation on the combination of AD user account attributes that will be supported with Azure AD App Proxy for Windows integrated auth applications will need to be updated for the B2B guest scenario. (Typically for employees accessing on-premises apps, the users have been enabled on-premises as well as in Azure AD, so this issue has not yet showed up -- until now when we have guests). We'll need the Azure AD app proxy for guest scenario validation to be completed so we can ensure the combination of attributes work together, since KCD scenarios are often difficult to get working on first try particularly for multi-domain/multi-forest scenarios. Feel free to reach out to Sarat or I offline via email if you wish to discuss further. Thanks, Mark19KViews0likes3CommentsRe: AAD Application Proxy and B2B Users
Hi, I assume you are looking to use the app proxy for organizations with on-premises apps which are using Windows authentication. We are currently working on the documentation for this scenario which should be posted to docs.microsoft.com shortly. In principle, creating an account in a AD domain corresponding to a user in Azure AD, including a guest user, would enable the app proxy to match the user coming in from Azure AD and use KCD for impersonation and permit that user to then access Windows integrated authentication, however there are a number of account lifecycle subtleties here. So even if you see the app proxy is able to permit the communication flow, I'd suggest waiting for the documentation as there are a number of deloyment considerations and best practices to look at (e.g., what container to put the users in, when to deprovision the user from AD, how to avoid RID exhaustion and end user confusion about "All authenticated users" etc) Feel free to reach out to us if you have additional questions. Thanks, Mark19KViews3likes10CommentsRe: Attestation/account verification for Viral/Just in time users
Hi Bally, we have heard this ask from several customers and it is definitely on our roadmap. For background, we have access reviews today in Azure AD as part of Azure AD PIM for a different scenario - attestation of users who have privileged roles assigned to them. Currently we plan to leverage this access reviews approach to enable organizations to ensure their invited guest users confirm they have a continued need for access. This is particularly important for organizations engaging with guests which come from an un-managed tenant which has no user lifecycle process in place. No dates yet but when we have more updates in this area we'll post to the Enterprise Mobility blog: https://blogs.technet.microsoft.com/enterprisemobility/ Thanks, Mark2.6KViews3likes5CommentsRe: Welcome to the Azure Active Directory B2B Collaboration AMA!
Hi, I'm Mark Wahl, Sarat's colleague at Microsoft and principal program manager for identity governance and administration in MIM and Azure AD. We've heard many questions from customers about how B2B and guest collaboration fits into an enterprise identity management strategy and its impact on access risk management, looking forward to today's discussion!10KViews4likes1Comment
Recent Blog Articles
No content to show