Saturday, 20 July 2019

Get a list of all Azure AD Integrated Applications using Powershell

In this post, I am going to share powershell script to find and retrieve the list of Azure AD Integrated apps (Enterprise Applications) with their API permissions and users who are authorized to use the app. In Azure AD, the integrated apps or Enterprise applications are nothing but an instance (ServicePrincipal object) or mirror of the apps (Application object) which are generally published in other company tenants (or in your own tenant). We can use the Get-AzureADServicePrincipal cmdlet to fetch all the integrated apps.

Before proceed install Azure AD Powershell Module V2 and run the below command to connect the Powershell module:
Connect-AzureAD
By default the Get-AzureADServicePrincipal cmdlet returns all the service principal objects, we can filter the result by using the Tags property to list only integrated applications.
Get-AzureADServicePrincipal -All:$true | ? {$_.Tags -eq "WindowsAzureActiveDirectoryIntegratedApp"}
The below command returns limited fields alone.
Get-AzureADServicePrincipal -All:$true |?{$_.Tags -eq "WindowsAzureActiveDirectoryIntegratedApp"}|
Select-Object ObjectId,AppDisplayName,AppId,PublisherName
  • ObjectId - This is the unique id for the service principal object (ServicePrincipalId). We need to use this id to get resources related to the service principal object.
  • AppDisplayName - Name of the Application.
  • AppId - The id of the Application. The AppId is unique across all related Azure AD objects (Application object and ServicePrincipal object). If you are the owner or the app registered in your tenant, then you can use the Get-AzureADApplication cmdlet to get the registered apps (Application objects).This id will be used as ClientId while acquiring access token to access resources.

Get the list of Delegated permissions granted to the application

We can use the Get-AzureADServicePrincipalOAuth2PermissionGrant cmdlet to fetch OAuth delegated permissions which have been granted to the application either by end-user (User Consent) or Admin user (Admin Consent).
#$ServicePrincipalId = (Get-AzureADServicePrincipal -Top 1).ObjectId
#Provide ObjectId of your service principal object
$ServicePrincipalId = "5614c8c4-22bb-45c7-9be3-47491152703d"
Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId $ServicePrincipalId | FL
This command gets an oAuth2PermissionGrant object and it includes the following fields.
  • ClientId - The id of the service principal object.
  • ConsentType - Indicates if consent was provided by the administrator (on behalf of the organization) or by an individual. The possible values are AllPrincipals or Principal.
  • ObjectId - Unique id for this object.
  • PrincipalId - If ConsentType is AllPrincipals this value is null, and the consent applies to all users in the organization. If ConsentType is Principal, then this property specifies the id of the user that granted consent and applies only for that user. You can use this id with Get-AzureADUser cmdlet to get the user data.
  • ResourceId - Specifies the id of the resource service principal to which access has been granted.
  • Scope - Specifies the value of the scope claim that the resource application should expect in the OAuth 2.0 access token. For example, User.Read.

Get the list of Application permissions granted to the application

The Get-AzureADServicePrincipalOAuth2PermissionGrant cmdlet retrieves all delegated permissions for a service principal object, but you can't use this command to retrieve the application permissions. Application permission assignments are represented as AppRoleAssignments in the directory, you can use the Get-AzureADServiceAppRoleAssignedTo cmdlet to list what application permissions (AppRoleAssignment) have been assigned to the service principal object.
$AppPermissions =@()
$ResourceAppHash = @{}
#Provide ObjectId of your service principal object
$ServicePrincipalId = "5614c8c4-22bb-45c7-9be3-47491152703d"
$AppRoleAssignments = Get-AzureADServiceAppRoleAssignedTo -ObjectId $ServicePrincipalId
$AppRoleAssignments | ForEach-Object {
$RoleAssignment = $_
$AppRoles = {}
If ($ResourceAppHash.ContainsKey($RoleAssignment.ResourceId)) {
$AppRoles = $ResourceAppHash[$RoleAssignment.ResourceId]
} Else {
$AppRoles = (Get-AzureADServicePrincipal -ObjectId $RoleAssignment.ResourceId).AppRoles
#Store AppRoles to re-use.
#Probably all role assignments use the same resource (Ex: Microsoft Graph).
$ResourceAppHash[$RoleAssignment.ResourceId] = $AppRoles
}
$AppliedRole = $AppRoles | Where-Object {$_.Id -eq $RoleAssignment.Id}  
$AppPermissions += New-Object PSObject -property @{
DisplayName = $AppliedRole.DisplayName
Roles = $AppliedRole.Value
Description = $AppliedRole.Description
IsEnabled = $AppliedRole.IsEnabled
ResourceName = $RoleAssignment.ResourceDisplayName
}
}
$AppPermissions | FL

Get users who are associated with the application

You can get the list of users who are involved with the application by using the Get-AzureADServiceAppRoleAssignment cmdlet.
#Provide ObjectId of your service principal object
$ServicePrincipalId = "5614c8c4-22bb-45c7-9be3-47491152703d"
Get-AzureADServiceAppRoleAssignment -ObjectId $ServicePrincipalId | Select ResourceDisplayName,PrincipalDisplayName
Read More...

Wednesday, 17 July 2019

Fix : BadRequest - Invalid value specified for property 'mailNickname' of resource 'User'

Problem:

You might have received the error "Invalid value specified for property 'mailNickname' of resource 'User'" error when you create a new Azure AD user.

Cause/Solution:

As the error message indicates the provided mail alias name is not in correct format or it includes invalid special characters. So try the below cases to fix this problem.
  • Remove white spaces in mailNickname (mail alias, email address).
  • Remove special characters in mailNickname (Ex: Space and "(),:;<>@[\]).

Valid and Invalid characters in mailNickname/mailAlias:

The mailNickname or mail alias of the email address may use any of these ASCII characters RFC 5322 Section 3.2.3:
  • Uppercase and lowercase English letters (a–z, A–Z) (ASCII: 65-90, 97-122) - Digits 0 to 9 (ASCII: 48-57).
  • Characters !#$%&'*+-/=?^_`{|}~ (ASCII: 33, 35-39, 42, 43, 45, 47, 61, 63, 94-96, 123-126).
  • Character . (dot, period, full stop) (ASCII: 46) provided that it is not the first or last character, and provided also that it does not appear two or more times consecutively (e.g. John..Doe@example.com is not allowed.).

Special characters with restrictions:

Special characters are allowed with restrictions. They are: - Space and "(),:;<>@[\] (ASCII: 32, 34, 40, 41, 44, 58, 59, 60, 62, 64, 91-93).

The restrictions for special characters are that they must only be used when contained between quotation marks, and that 3 of them (The space, backslash \ and quotation mark " (ASCII: 32, 92, 34)) must also be preceded by a backslash \ (e.g. "\ \\\"").

Note : You can also apply the same fix for group creation issue (group mail alias issue) - Invalid value specified for property 'mailNickname' of resource 'Group'.

Source : What are valid and invalid email address characters
Read More...

Monday, 15 July 2019

Difference between App Registration and Enterprise Application in Azure AD

Applications that are registered through Azure Portal (or programmatically) in your Azure Tenant is App Registration apps or Home Tenant Apps. Enterprise Applications are generally registered at another tenant (the one their publisher uses), when you consume the other tenant apps your Azure AD instance just provides service principal object for this app in your directory, and adds required permissions to the service principal object, and then assigns users.

When you create the App Registration (Application) in your tenant, it will create an Application object in your tenant directory. Then when another tenant user wants to consume your app, they login and grant required permissions for your app and the Enterprise Application (Service Principal) is created in their tenant. This service principal object effectively mirrors your application in their tenant.

App Registration (Application):

  • Your own Applications that are registered in App registrations.
  • When you create a new app in your tenant, it will create an Application object in your tenant directory.
  • Registered or Owned Apps.
  • You can change Reply URL for this app as it registered and owned by you.
  • You can use the Get-AzureADApplication cmdlet to list all the registered apps.
Get-AzureADApplication -All:$true
#Web Apps
Get-AzureADApplication -All:$true | Where-Object { $_.PublicClient -ne $true } | FT
#Native Client (Desktop/Mobile device) Apps
Get-AzureADApplication -All:$true | Where-Object { $_.PublicClient -eq $true } | FT

Enterprise Application (Service Principal):

  • Enterprise Applications are Service Principal objects that mirror the apps which are generally published by other companies.
  • When you grant permission for other tenant application to access resources in your tenant (upon registration or consent), a service principal object (Enterprise Application) will be created.
  • You can also grant permission for your own apps which also creates a service principal object in your tenant.
  • Integrated or Consumed Apps.
  • You can't configure Reply URL for the apps that are registered in other company tenants.
  • You can use the Get-AzureADServicePrincipal cmdlet to list all the Enterprise Applications.
Get-AzureADServicePrincipal -All:$true | ? {$_.Tags -eq "WindowsAzureActiveDirectoryIntegratedApp"}
Read More...

Friday, 12 July 2019

How to find your tenant name in Office 365

You might have heard the terms tenant name and domain name in Office 365 and Azure AD. You may feel both are the same, but this is not correct. When you sign up for Office 365, you will be asked for your organization name (i.e., contoso). Based on this organization name, Azure AD service will allocate a "tenant name" and it will also create a default domain for your tenant, the domain name will be the same as your tenant name, but with the suffix domain ".onmicrosoft.com".

For example, if you have created organization with the name "contoso", then

Your tenant name is : contoso
Default domain name is : contoso.onmicrosoft.com

You can purchase and add custom domains with different names under this domain, but you can't change the tenant name.

Why tenant name is important :

The tenant name will be used in your SharePoint and OneDrive Site URL.
SharePoint URL : https://tenant_name.sharepoint.com
SPO Site URL : https://tenant_name.sharepoint.com/sites/site_name
SPO Admin Site URL : https://tenant_name-admin.sharepoint.com/
OneDrive URL : https://tenant_name-my.sharepoint.com/personal/username_domain_com/_layouts/15/onedrive.aspx
So if you planned to use these services, then you have to carefully choose your organization name. For example, if you chose "contoso" as your organization name during Office 365 signup, then contoso will become your "tenant name". Even if you add custom domain "contosotech.com", you cannot change tenant name in your SharePoint URL to "contosotech.sharepoint.com".

How to retrieve your Office 365 tenant name :

As of now (at least when I am writing this post), there is no direct Graph API or Powershell support to get this property. But you can extract this value from other properties, i.e., SharePoint URL, OneDrive URL and License SKU name.

From OneDrive Site URL :

Open your OneDrive for Business site in a web browser and your browser URL will look like below URL. The first part of the URL before -my.sharepoint is tenant name.
https://tenant_name-my.sharepoint.com/personal/username_domain_com/_layouts/15/onedrive.aspx
How to find your tenant name from OneDrive URL


From SharePoint Site URL :

Open one of your SharePoint Online site in a web browser and your browser URL will look like below URL. Here the first part of the URL before .sharepoint is tenant name.
https://tenant_name.sharepoint.com/sites/site_name
How to find my tenant name from SharePoint Site URL


From License SKU ID using Powershell:

The license SKU names are prefixed with your tenant name, so you can list the available license SKUs by running the Get-MsolAccountSku cmdlet and the first part of the AccountSkuID is the tenantname.

Open Powershell with "Run as administrator" privilege and run the below command to install the MSOnline Powershell module if you have not already installed.
Install-Module MSOnline
Run the below commands to list the available license SKUs.
Connect-MsolService
Get-MsolAccountSku
How to find my tenant name using Powershell

Read More...

Monday, 8 July 2019

Full Access Mailbox Permissions Report using Powershell

In this post, we will explore how to list users who have full access permission in other users' mailbox. We can use the Exchange Powershell command Get-MailboxPermission to extract assigned permissions from a particular mailbox.

Before proceed connect Exchange Online Powershell module or Exchange Management Shell for On-premise environment.

Run the below command to list specific mailbox permissions
Get-MailboxPermission "Aldo Muller" | Select Identity,User,AccessRights
The above command not only list explicitly assigned full access permissions to a mailbox, it will also list inherited permissions, built-in system groups and the mailbox's SELF access, these extra permissions are unnecessary entries for our current report, so we can filter them using Where logic operation.
Get-MailboxPermission "Aldo Muller" | Where { ($_.IsInherited -eq $False) -and ($_.AccessRights -like "*FullAccess*") -and -not ($_.User -like "NT AUTHORITY\SELF") } |
Select Identity, User, AccessRights
You can also list access rights only for a specific user on a specific mailbox. You can provide the required user account with the parameter -User in Get-MailboxPermission cmdlet.
$Mailbox = "Aldo Muller"
$UserToCheck = "Alex Wilber"
Get-MailboxPermission -Identity $Mailbox -User $UserToCheck | Select Identity,User,AccessRights

List all mailboxes in which a specific user has Full Access permissions

In some scenarios, you may need to extract all mailboxes in which a given user account has full access permission. For this need, first we have to fetch all mailboxes and pipe the result to Get-MailboxPermission cmdlet.
Get-Mailbox -ResultSize Unlimited | Get-MailboxPermission  -User "Alex Wilber" | Select Identity,User,AccessRights

List all mailboxes with Full Access permissions

The below command retrieves mailboxes and users with full access permission.
Get-Mailbox -ResultSize Unlimited | Get-MailboxPermission |
Where { ($_.IsInherited -eq $False) -and ($_.AccessRights -like "*FullAccess*") -and -not ($_.User -like "NT AUTHORITY\SELF") } |
Select-Object @{n="Mailbox"; e={$_.Identity}},@{n="UserHasFullAccess"; e={$_.User}},@{n="Access"; e={$_.AccessRights}} 

Export Result to CSV :

You can export the report to csv file by running below commands.
Get-Mailbox -ResultSize Unlimited | Get-MailboxPermission |
Where { ($_.IsInherited -eq $False) -and ($_.AccessRights -like "*FullAccess*") -and -not ($_.User -like "NT AUTHORITY\SELF") } |
Select-Object @{n="Mailbox"; e={$_.Identity}},@{n="UserHasFullAccess"; e={$_.User}},@{n="Access"; e={$_.AccessRights}}  |
Export-CSV "C:\\FullAccessPermissionsReport.csv" -NoTypeInformation -Encoding UTF8
Read More...

Thursday, 4 July 2019

Flow Tips: Create Folder In SharePoint Library Using Microsoft Flow

In this post, I am going to explain how to create a new folder under root directory (or sub folder) in a SharePoint document library using Microsoft Flow. You may have this requirement in automatic trigger from some other activity in Office 365 resource, here I have explained with manual trigger, but you can use the same action in your automatic flow trigger by just passing the required parameters.

Required Flow Action

We can use the Send an HTTP request to SharePoint action to create a folder in SharePoint List.

Required Input Parameters

  • Site Address
  • Relative Folder Path 
  • New Folder Name

Create Folder under Root Folder of the Document Library

In my case I am trying to add folder in default document library (Documents) and its root folder name is "Shared Documents".

1. Go to Microsoft Flow page.
2. Select My flows -> click New -> Instant - from blank.
3. Name your flow as "Create Folder" -> choose the trigger option From Microsoft Flow -> click Create.
4. Click Next step -> select SharePoint -> choose the action Send an HTTP request to SharePoint
5. In action parameter box, enter Site Address, select request method as POST, set Uri with your relative root folder path, set relative new folder path in request Body and set Headers.

Site Address : https://YourTenant.sharepoint.com/sites/TestSite
Method : POST

Uri :
_api/web/GetFolderByServerRelativePath(decodedurl='Shared Documents')/Folders
Headers :
accept : application/json;odata=verbose
content-type : application/json;odata=verbose
Body:
{ '__metadata': { 'type': 'SP.Folder' }, 'ServerRelativeUrl': 'Shared Documents/New Folder'}
Create List In SharePoint Library Using Microsoft Flow

Create Folder under Sub Folder of the Document Library

You just need to provide the relative path of your sub folder in Uri and request Body, other parameters are same.

Uri :
_api/web/GetFolderByServerRelativePath(decodedurl='Shared Documents/Sub Folder')/Folders
Body:
{ '__metadata':{ 'type':'SP.Folder' },'ServerRelativeUrl':'Shared Documents/Sub Folder/New Folder'}
Create Sub Folder In Document Library Using Microsoft Flow

Note: Ensure that your Sub Folder was already created, in my testing the flow runs continuously if the sub folder not available in the specified location.

Here I am using default document library (Documents), and its root folder name is "Shared Documents". If you try to create folder in another library (ex: DocumentLib1), then by default your root folder name is same as the library name. In this case you have to provide following parameters.

Uri :
_api/web/GetFolderByServerRelativePath(decodedurl='DocumentLib1')/Folders
Body:
{ '__metadata':{ 'type':'SP.Folder' },'ServerRelativeUrl': 'DocumentLib1/New Folder'}
Read More...

Wednesday, 3 July 2019

Get Drive Item and List Item by File Name, Path and Guid using Graph API

You might have already known the resources ListItem and File if you have worked with SharePoint files with CSOM. In Graph API, the sharepoint files are represented by another resource DriveItem. The driveItem resource represents a file, folder, or other item stored in a drive. All file system objects in OneDrive and SharePoint are returned as driveItem resources. You can retrieve driveItem resource by following ways.

Get by driveItem unique identifier :

#From SharePoint Online Site
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{itemId}
#From Office 365 Group associated SPO Site
https://graph.microsoft.com/v1.0/groups/{groupId}/drive/items/{itemId}
#From User's OneDrive
https://graph.microsoft.com/v1.0/me/drive/items/{item-id}

By file name :

The below request queries the given file name in root folder alone. If you want to query in sub folder, then you have to provide your folder name in the api url (Refer below example)
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/root:/TestFile.txt
 
https://graph.microsoft.com/v1.0/groups/{groupId}/drive/root:/TestFile.txt

By file system path :

The below request queries the given file name in TestFolder.
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/root:/TestFolder/TestFile.txt
You can refer this post for more details : Get a DriveItem resource

Get ListItem from DriveItem resource :

The driveItem resource includes the extended relationship with ListItem. The below graph api request retrieves the associated document library list item for the corresponding driveItem.
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{itemId}/listItem

Get DriveItem by File ID (Guid) using Graph API :

Using above methods, you can get driveItem resource by file name, file path and drive item id. There may be a requirement to get this driveItem by file's actual guid. This guid will be used with document URL in some cases. Actually I was in the same need with below file URL.
"https://MySPOTenant.sharepoint.com/sites/MySite/_layouts/15/Doc.aspx?sourcedoc={52173197-354A-532K-96BB-D4A5CDA8AF15}&file=TestFile.xlsx&action=default&mobileredirect=true"
Here my need was to find driveItem resource by using its document ID which is available in the document url. We can't able to use this guid with the above endpoint (ex: sites/{siteId}/drive/items/{itemId}), because it supports only drive item unique id. So here my alternative approach is Search endpoint, we can provide this guid in search query and get the associated driveItem resource.
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/root/search(q='52173197-354A-532K-96BB-D4A5CDA8AF15')
Actually searching by file guid will not provide perfect result in some cases, because it searches entire file system metadata, so you may get wrong result if the same guid text available as name of another file. As a work-around, once we get driveItem resource using above request, we can get the associated document library list item by providing driveItem id. The listItem resource includes the property eTag which includes the file guid, you can compare this eTag with file guid to ensure that you have found the correct resource. You can also cross-check the file name to get better result.
#Get the itemId from above search result items
https://graph.microsoft.com/v1.0/sites/{siteId}/drive/items/{itemId}/listItem

#ListItem result snippet 
"@odata.etag": "\"52173197-354A-532K-96BB-D4A5CDA8AF15,3\"",
"createdDateTime": "2019-07-02T12:11:44Z",
"eTag": "\"52173197-354A-532K-96BB-D4A5CDA8AF15,3\"",
Note : I am just sharing this method as a work-around, if you find any mistake or better method, then please post the same in below comments section.
Read More...

Tuesday, 2 July 2019

AADSTS54005: OAuth2 Authorization code was already redeemed

Problem :

I have been working with application that uses Device Code Flow to authenticate Office 365 user to consume Graph API resources. My code was working fine until today, but unfortunately today I got the below error.
error: "invalid_grant"
error_codes: [54005]
error_description: "AADSTS54005: OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token.

Solution :

As you see, the error message itself is self explanatory message, so I have tried to find the solution in that way. Finally found the real problem is with my code, my recent changes in the code leads to call get token api two times, in first call we are getting valid token, but when we use the same device code in second call, then we are getting the above error message. The problem solved after removing extra call for the get token endpoint with the same code.

Fix for OAuth2 Authorization code was already redeemed, please retry with a new valid code or use an existing refresh token

Read More...