Monday, 14 October 2019

Set Primary Email Address for Office 365 Users using Powershell

In this post, I am going to share Powershell commands to add email alias and set the new alias as the primary email address for Office 365 users. We can use the Set-Mailbox command to add new email alias, remove an old email address and set primary email address.

Note: Before proceed, Connect Exchange Online Remote Powershell.

Add new Email Alias and set as a Primary Email Address:

The below command adds the new alias in the EmailAddresses list and sets it as PrimarySmtpAddress. Replace the parameter "username" with Name or UserPrincipalName of the mailbox user.
Set-Mailbox 'username' -WindowsEmailAddress 'newalias@newdomain.com'

Remove old Email Alias:

The above command just adds the new email address and it will not remove the existing email, the old email address still works as a proxy address for the primary email address, you may want to remove existing email once you confirmed the new email works fine. You can remove the existing email alias by running the below command.
Set-Mailbox 'username' -EmailAddresses @{Remove='oldalias@olddomain.com'}

Add new Email Alias as Proxy Address:

In some cases, you may want to add a new email address for an Office 365 user without affecting the user's existing primary email address, in this case, you can add the new alias in the EmailAddresses list by running the below command.
Set-Mailbox 'username' -EmailAddresses @{Add='newalias@newdomain.com'}

Set Primary Email Address for Bulk O365 Users from CSV:

In some scenarios, we may be required to add email alias for multiple mailbox users in bulk by importing user details from CSV file. Consider the CSV file Office365Users.csv which contains every users` UPN (or Name) and new email address in each row with the column headers UserPrincipalName and NewEmailAddress.
Import-Csv 'C:\Office365Users.csv' | ForEach-Object {
Set-Mailbox $_."UserPrincipalName" -WindowsEmailAddress $_."NewEmailAddress"
}

Change UserPrincipalName to match with Primary Email Address:

In Office 365 cloud, users need to use their UPN (UserPrincipalName) as login name to sign-in to any Office 365 apps, changing user's WindowsEmailAddress (or PrimarySmtpAddress) will not change UPN of the users, so we may be required to update UPN while setting new email address to avoid confusions for end-users when they login-in to Office 365 apps. The Set-Mailbox command includes the parameter MicrosoftOnlineServicesID, this is the equivalent property of UPN which exists in the mailbox's associated Azure AD user object, we can set the new email address in this attribute to update UPN of the user.
Set-Mailbox 'username' -WindowsEmailAddress 'newalias@newdomain.com' -MicrosoftOnlineServicesID 'newalias@newdomain.com'
The above command throws the warning message: WARNING: UserPrincipalName "username@domain.com" should be same as WindowsLiveID "newalias@domain.com", UserPrincipalName should remain as "newalias@domain.com". Ignore this warning message and the UPN will be updated in a few minutes.

Set UPN and Primary Email Address for bulk mailbox users:

The below commands import mailbox users` names and email alias from CSV file and set the new mail address as PrimarySmtpAddress and UserPrincipalName.
Import-Csv 'C:\Office365Users.csv' | ForEach-Object {
Set-Mailbox $_."UserPrincipalName" -WindowsEmailAddress $_."NewEmailAddress" -MicrosoftOnlineServicesID $_."NewEmailAddress"
}

Saturday, 28 September 2019

Report Group and Teams Enabled SharePoint Online Sites using Powershell

In this post, I am going to share Powershell commands to find and list all SharePoint Online sites, the returned result object includes the property GroupEnabled (Groupify) which indicates the site is connected with group or not, and the result object also includes the property TeamEnabled (Teamify) which indicates whether the Teams feature enabled in the site's associated group or not.

Before proceeding, install SPO Powershell Module and run the below commands to connect Exchange Online (EXO) and SharePoint Online modules:
$Cred = Get-Credential
#Import Exchange Online Powershell module
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session
#Connect SharePoint Service
Connect-SPOService -Url "https://YourTenantName-admin.sharepoint.com " -Credential $Cred

Export all SPO Sites with Group and Team Details

Here, we are fetching site details using the Get-SPOSite command and retrieve Office 365 Group object with Get-UnifiedGroup command, the group object includes the property resourceProvisioningOptions which includes the array of values, we need to check whether this array includes the value "Team" to identify the teams feature is configured or not in the group.
$Result = @()
$AllSites = Get-SPOSite -IncludePersonalSite:$False -Limit All | Select Title,URL
$TotalSites = $AllSites.Count
$i = 1 
ForEach ($Site in $AllSites) {
Write-Progress -Activity "Processing $($Site.Title)" -Status "$i out of $TotalSites completed"
$O365Group = $null;
$TeamEnabled = $false;
#Once again call Get-SPOSite for each site with -Detailed switch to get GroupId property
#The bulk enumeration command (Get-SPOSite -Limit All) do not support -Detailed switch
$GroupId = (Get-SPOSite $Site.URL -Detailed).GroupId.Guid
if($GroupId -ne $null) {
$O365Group = (Get-UnifiedGroup -Identity $GroupId -ErrorAction SilentlyContinue)
if($O365Group -ne $null -and $O365Group.resourceProvisioningOptions -contains "Team") {
$TeamEnabled = $true;
}
}
$Result += New-Object PSObject -property @{ 
SiteName = $Site.Title
SiteURL  = $Site.URL
GroupEnabled = if ($O365Group -ne $null) { $true } else { $false }
GroupName  = if ($O365Group -ne $null) { $O365Group.DisplayName } else { $null }
GroupID  = if ($O365Group -ne $null) { $GroupId } else { $null }
GroupMail  = if ($O365Group -ne $null) { $O365Group.PrimarySmtpAddress } else { $null } 
TeamEnabled  = $TeamEnabled   
}
$i++
}
$Result | Select SiteName,SiteURL,GroupEnabled,GroupName,GroupID,GroupMail,TeamEnabled |
Export-CSV "C:\\AllSPOSites.CSV" -NoTypeInformation -Encoding UTF8
The above commands store the details in the array object $Result, we can generate the required report from this result array.

List all sites without group feature

You can just filter the $Result array with Where-Object to list sites without a group.
$Result | Where-Object { $_.GroupEnabled -eq $false } | Select SiteName,SiteURL
Alternatively, you can list sites only that are connected with a group.
$Result | Where-Object { $_.GroupEnabled -eq $true } | Select SiteName,SiteURL,GroupName

Get all sites without group and teams feature

$Result | Where-Object { $_.GroupEnabled -eq $false -and $_.TeamEnabled -eq $false} | Select SiteName,SiteURL
Alternatively, you can list sites with group, but without teams feature.
$Result | Where-Object { $_.GroupEnabled -eq $true -and $_.TeamEnabled -eq $false} | Select SiteName,SiteURL,GroupName

Get all sites with group and teams feature

$Result | Where-Object { $_.GroupEnabled -eq $true -and $_.TeamEnabled -eq $true} | Select SiteName,SiteURL,GroupName

Sample CSV Report:

Find Group and Teams Enabled SharePoint Online Sites using Powershell

Friday, 27 September 2019

Groupify and Teamifiy a SharePoint Online Site using Powershell

In this post, I am going to explain how to programmatically connect an existing SharePoint Online site with Office 365 Group and add Teams feature using Powershell (and Office 365 CLI). We can use the SPO Powershell command Set-SPOSiteOffice365Group to integrate a site with a new Office 365 group and use New-Team command to create a team from the site connected Office 365 group.

Before proceeding, install SPO and Teams Powershell Modules and run the below commands :
$Cred = Get-Credential
#Connect Teams module
Connect-MicrosoftTeams -Credential $Cred
#Connect SharePoint Service
Connect-SPOService -Url "https://YourTenantName-admin.sharepoint.com " -Credential $Cred

Groupify a SharePoint Online Site

Run the following command to connect the root site collection with a new group.
#Groupify - Connect the site to new O365 Group
$SiteURL = "https://YourTenantName.sharepoint.com/sites/TestSite1"
$GroupName = "TestGroup"
$MailNickName = "TestGroup"
Set-SPOSiteOffice365Group -Site $SiteURL -DisplayName $GroupName -Alias $MailNickName

Teamify the group which is connected with the site

Once the site is integrated with a modern group, we can get the id (GroupId) of the connected group from spo_site object and create a new team from this new group.
#Teamify - Add Teams feature in the site associated O365 Group
$SiteObj = Get-SPOSite -Identity $SiteURL
New-Team -GroupId $SiteObj.GroupId.Guid
Office 365 CLI : If you are a big fan of Office 365 CLI, you can refer this post to achieve this functionality using Office 365 CLI.

Friday, 20 September 2019

Find and Export Manager of All Office 365 Users using Powershell

In this post, I am going to share powershell script to find manager info of all Office 365 users and export the details to CSV file. We can use the Get-AzureADUser command to get user details, but this command does not include manager based details, so we have to use the Get-AzureADUserManager cmdlet to get a user's manager info.

Before proceeding, install Azure AD PowerShell V2 module and run the below command to connect Azure AD Powershell:
Connect-AzureAD
Run the following command to retrieve manager information for a single user account.
Get-AzureADUserManager -ObjectId "username@domain.com"

Get Manager of All Azure AD Users

To get the manager detail of all users, first, we have to get all Office 365 users using the Get-AzureADUser command and pipe the result to Get-AzureADUserManager command. The below commands fetch all users and their manager details (ex: name and email address) and store the detail in the $Result array object.
$Result = @()
$AllUsers= Get-AzureADUser -All $true | Select-Object -Property Displayname,UserPrincipalName
$TotalUsers = $AllUsers.Count
$i = 1 
$AllUsers | ForEach-Object {
$User = $_
Write-Progress -Activity "Processing $($_.Displayname)" -Status "$i out of $TotalUsers completed"
$managerObj = Get-AzureADUserManager -ObjectId $User.UserPrincipalName
$Result += New-Object PSObject -property @{ 
UserName = $User.DisplayName
UserPrincipalName = $User.UserPrincipalName
ManagerName = if ($managerObj -ne $null) { $managerObj.DisplayName } else { $null }
ManagerMail = if ($managerObj -ne $null) { $managerObj.Mail } else { $null }
}
$i++
}
After the successful run of the above commands, you can run the below command to list all office 365 users with their manager info.
$Result | Select UserName, ManagerName, ManagerMail

List All Office 365 Users without Manager :

You can just filter the $Result array with Where-Object filter to list users with no manager.
$Result | Where-Object { $_.ManagerName -eq $null } | Select UserName, UserPrincipalName
Alternatively, you can list users only who have a manager.
$Result | Where-Object { $_.ManagerName -ne $null } | Select UserName, ManagerName

Export the Result to CSV file

You can easily export the result to CSV file using the Export-CSV cmdlet.
$Result | Select UserName, UserPrincipalName, ManagerName,ManagerMail  |
Export-CSV "C:\\O365UsersManagerInfo.CSV" -NoTypeInformation -Encoding UTF8

Find manager info for multiple users from CSV file

In some scenarios, we may need to find manager name for particular set of users. In this case, we can store the user ids in CSV file and import csv in powershell using Import-Csv cmdlet. Consider the CSV file "O365Users.csv" which contains username (or upn) of users with the column header UserPrincipalName.
$Result = @()
Import-Csv 'C:\O365Users.csv' | ForEach-Object {
$managerObj = Get-AzureADUserManager -ObjectId $_."UserPrincipalName"
$Result += New-Object PSObject -property @{ 
UserName = $_."UserPrincipalName"
ManagerName = if ($managerObj -ne $null) { $managerObj.DisplayName } else { $null }
ManagerMail = if ($managerObj -ne $null) { $managerObj.Mail } else { $null }
}
}
$Result | Select UserName, ManagerName,ManagerMail  |
Export-CSV "C:\\O365UsersManagerInfo.CSV" -NoTypeInformation -Encoding UTF8

Monday, 16 September 2019

Create a new Team in Microsoft Teams using PowerShell

In this post, I am going share Powershell commands to create a new team, add team (or enable team) in an existing Office 365 group and create a team from an existing SharePoint Online site. This post also includes commands to add members, owners, and channels in the newly created team. We can use the New-Team cmdlet from the Microsoft Teams Powershell module to add a team.

Before proceed run the below command to install Microsoft Teams Powershell module if you have not already installed.
Install-Module MicrosoftTeams -Force
Note: You have to run the powershell with "Run as administrator" privilege to install this module.

Once you have installed the Teams module, then run the following command to connect Microsoft Teams and it will ask your credentials to proceed.
Connect-MicrosoftTeams

Create a new team

Run the following command to create a new team.
New-Team -DisplayName "Test Team" -Visibility Public -Description "This is test Team"
While creating a team, this command also creates a new Office 365 Group with the same name and associate the new team with this group. On successfull run, this command returns the newly created Group object with GroupId property (which is TeamId), we can note this GroupId for future uses (ex: to add members, owners, and channels).

You can also provide more inputs with New-Team cmdlet, like MailNickName (Mail Alias for Office 365 Group), Owner (Additional owner). You can refer New-Team cmdlet documentation for more details.
New-Team -DisplayName "Test Team" -MailNickName "TestTeam" -Visibility Public -Description "This is test Team"

Create a team from existing Office 365 Group

If you already created and worked with an Office 365 Group, then you can easily convert this group to a team using the New-Team command by just passing the id of the group.
New-Team -GroupId "Existing Group ID - GUID"
Here, you cannot provide the group related values that are already specified in the existing group, ex: Visibility, Alias, Description, or DisplayName.

Create a team from existing SharePoint Online Site

You may have a scenario to enable Office 365 Group (Groupify) and add Team (Teamify) with existing SharePoint Online Site. If you have already connected the site with Office 365 group, then you can easily Teamify the group using the above command. If you yet to integrate the site with Office 365 group, then first you need to run the Set-SPOSiteOffice365Group cmdlet to add a new Office 365 group with the site and you can enable teams feature in the newly created group.

Before proceed run the below command to install the SharePoint Online Powershell module if you have not already installed.
Install-Module -Name Microsoft.Online.SharePoint.PowerShell
The below commands connect the site "TestSite1" with the new group "TestGroup" and teamify the new group.
#Connect SharePoint Service
$Cred = Get-Credential
Connect-SPOService -Url "https://YourTenantName-admin.sharepoint.com " -Credential $Cred
#Groupify - Connect the Site to new Office 365 Group
$SiteURL = "https://YourTenantName.sharepoint.com/sites/TestSite1"
$GroupName = "TestGroup"
$MailNickName = "TestGroup"
Set-SPOSiteOffice365Group -Site $SiteURL -DisplayName $GroupName -Alias $MailNickName
#Teamify - Create Team from the Site Associated O365 Group
$SiteObj = Get-SPOSite -Identity $SiteURL
New-Team -GroupId $SiteObj.GroupId.Guid

Add Members and Owners

We can use the Add-TeamUser cmdlet to add a user as member or owner in an existing team. The below command adds the user "user@domain.com" to a team with the id "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".
Add-TeamUser -GroupId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -User "user@domain.com"
We have to set the parameter Role as Owner to add a user as owner.
Add-TeamUser -GroupId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -User "admin@domain.com" -Role Owner

Add New Channels

We can use the New-TeamChannel cmdlet to create a new channel in a team. The below command creates a standard channel with display name as "Support"
New-TeamChannel -GroupId "<GroupId>" -DisplayName "Support"
You can create a private channel by passing the parameter MembershipType as Private.
New-TeamChannel -GroupId "<GroupId>" -DisplayName "Support" -MembershipType Private

Modify Team, Channel and Member Settings

We can use the Set-Team cmdlet to update the properties of a team, including its displayname, description, and team-specific settings.
Set-Team -GroupId "<GroupId>" -DisplayName "Edit Test Team" -Visibility Public
You can refer Set-TeamChannel and Set-TeamMemberSettings commands to update channel and member settings.

Friday, 13 September 2019

Get Calendar Permissions for All Users in Office 365

In Exchange, the calendar data located in a special folder (Calendar) under the user's mailbox. We can use the Exchange powershell cmdlet Get-MailboxFolderPermission to list all permissions that are configured in a Calendar folder.

Before proceed run the below commands to connect Exchange Online (EXO) powershell.
$365Logon = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $365Logon -Authentication Basic -AllowRedirection
Import-PSSession $Session

Get permissions for a single user in a user's calendar :

The below command returns permission entries for an individual user in a specific user's mailbox calendar. Run the below command after replacing the user "Kevin@contoso.com" and mailbox identity "Alex@contoso.com" with your user id and mailbox id.
Get-MailboxFolderPermission -Identity "Alex@contoso.com:\Calendar" -User "Kevin@contoso.com"

Get all permissions in a user's calendar :

You have to exclude the parameter "-User" to list all user permissions in a specific user's mailbox. The below command list all users who have permission in Alex's mailbox calendar.
Get-MailboxFolderPermission -Identity "Alex@contoso.com:\Calendar"

Get all permissions in all users calendar :

To extract permissions from all users' calendars, first, we have to fetch all mailboxes with the Get-Mailbox cmdlet and pipe the result to Get-MailboxFolderPermission cmdlet to get the mailbox folder permissions.
Get-Mailbox -ResultSize Unlimited | ForEach {Get-MailboxFolderPermission -Identity "$($_.PrimarySMTPAddress):\Calendar" } | Select Identity,User,AccessRights
The above command returns all permission entries in all users' calendar, the result also includes the default permissions "Default" & "Anonymous". You can exclude the "Default" & "Anonymous" entries with Where-Object filter.
Get-Mailbox -ResultSize Unlimited | ForEach {Get-MailboxFolderPermission -Identity "$($_.PrimarySMTPAddress):\Calendar" } | Where-Object {$_.User.DisplayName -ne "Default" -and $_.User.DisplayName -ne "Anonymous"} | Select Identity,User,AccessRights

Export all users calendar permissions in all mailboxes to CSV file :

$Result=@()
$allMailboxes = Get-Mailbox -ResultSize Unlimited | Select-Object -Property Displayname,PrimarySMTPAddress
$totalMailboxes = $allMailboxes.Count
$i = 1 
$allMailboxes | ForEach-Object {
$mailbox = $_
Write-Progress -activity "Processing $($_.Displayname)" -status "$i out of $totalMailboxes completed"
$folderPerms = Get-MailboxFolderPermission -Identity "$($_.PrimarySMTPAddress):\Calendar"
$folderPerms | ForEach-Object {
$Result += New-Object PSObject -property @{ 
MailboxName = $mailbox.DisplayName
User = $_.User
Permissions = $_.AccessRights
}}
$i++
}
$Result | Select MailboxName, User, Permissions |
Export-CSV "C:\\CalendarPermissions.CSV" -NoTypeInformation -Encoding UTF8

Set-MailboxFolderPermission : There is no existing permission entry found for user

Problem:

You might have received the below error message when you try to add permission for a user in another user's mailbox folder using the Set-MailboxFolderPermission cmdlet.
Set-MailboxFolderPermission -Identity Alland@contoso.com:\Calendar -User KevinM@contoso.com -AccessRights Editor -SharingPermissionFlags Delegate
There is no existing permission entry found for the user: Kevin Morgan.
    + CategoryInfo          : NotSpecified: (:) [Set-MailboxFolderPermission], UserNotFoundInPermissionEntryException
    + FullyQualifiedErrorId : [Server=XXXXXXXXXXXXX,RequestId=XXXXXXXX-181a-44f7-a613-53fdedb3d83c,TimeStamp=13-09-201
   9 06:31:53] [FailureCategory=Cmdlet-UserNotFoundInPermissionEntryException] F5424EF5,Microsoft.Exchange.Management
  .StoreTasks.SetMailboxFolderPermission
    + PSComputerName        : outlook.office365.com

Fix/Solution:

You are receiving this error since you are trying to edit permission object that does not already exist. The mailbox folder permissions are treated as individual permission objects for every user, the Set-MailboxFolderPermission cmdlet will be used to update the existing permission object and you have to use Add-MailboxFolderPermission cmdlet to add new permission entry for a user.
Add-MailboxFolderPermission -Identity Alland@contoso.com:\Calendar -User KevinM@contoso.com -AccessRights Editor

Thursday, 12 September 2019

Send Email using PowerShell command and Graph API

We can use the Send-MailMessage cmdlet to send e-mail from Powershell script and you can use Send mail API endpoint to send a message using Microsoft Graph API.

Send Email using PowerShell command with Office 365 SMTP Server settings

You can replace the required parameter values ($username, $password, $from and $to) in below script and run the commands to send mail. Here, we are using Office 365 SMTP server, you can also use different mail server settings with this script by providing required inputs.
$username = "user@domain.com"
$password = "user_password"
$sstr = ConvertTo-SecureString -string $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -argumentlist $username, $sstr
$from = "user@domain.com"
$to = "user2@domain.com"
$body = "This is a test email"
$subject = "Test message"
Send-MailMessage -To $to -from $from -Subject $subject -Body $body -BodyAsHtml -SmtpServer "smtp.office365.com" -UseSSL -Credential $cred -Port 587
Troubleshooting : Fix: Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated

Send Email using Microsoft Graph API

The Send mail API requires the permission Mail.Send. If you have valid Graph AccessToken with this permission, you can easily send mail without providing user credentials from Powershell. Before proceed, replace the parameters $AccessToken and "to_address@domain.com".
$AccessToken = "eyJ0***Access Token with Mail.Send permission*****"
$ApiUrl = "https://graph.microsoft.com/v1.0/me/sendMail"
# Create JSON Body object
$Body = 
@"
{
"message" : {
"subject": "Test message",
"body" : {
"contentType": "Text",
"content": "This is test mail"
},
"toRecipients": [{"emailAddress" : { "address" : "to_address@domain.com" }}]
}
}
"@ 
Invoke-RestMethod -Headers @{Authorization = "Bearer $AccessToken"} -Uri $ApiUrl -Method Post -Body $Body -ContentType "application/json"

Fix: Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated

Problem :

You might have received the below error message when you try to send mail using the Send-MailMessage cmdlet.
Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated. The server
response was: 5.7.57 SMTP; Client was not authenticated to send anonymous mail during MAIL FROM
[PSXP216CAXXXX.XXXXXX.PROD.OUTLOOK.COM]
At line:1 char:1
+ Send-MailMessage -To "user@domain.com" -from "admin ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.Mail.SmtpClient:SmtpClient) [Send-MailMessage], SmtpExcept
   ion
    + FullyQualifiedErrorId : SmtpException,Microsoft.PowerShell.Commands.SendMailMessage
I have used the below script to send mail using Office 365 user account. In my case, I have got the above error while providing wrong password in my script and the problem solved after providing correct user credentials.
$username = "user@domain.com"
$password = "user_password"
$sstr = ConvertTo-SecureString -string $password -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential -argumentlist $username, $sstr
$body = "This is a test email"
Send-MailMessage -To "admin@domain.com" -From "user@domain.com" -Subject 'Test message' -Body $body -BodyAsHtml -SmtpServer smtp.office365.com -UseSSL -Credential $cred -Port 587

Cause/Solution :

  • Possible Reason 1: Ensure that you have provided correct user credentials (User name and password).
  • Possible Reason 2: Ensure that you have provided the same user account for both Credential and From field.
  • Possible Reason 3: If you want to send mail on behalf of another user, then ensure that the user account has required permissions (ex: SendAs or SendOnBehalf) for the account that you provided in From field.
  • Possible Reason 4: If you face the issue with MFA enabled account, then you can generate an app password and then use an app password for that account, instead of the regular user password. Related thread: https://techcommunity.microsoft.com/t5/Identity-Authentication/Send-Mail-SMTP-through-Office-365-with-MFA/m-p/163867
  • Possible Reason 5: Make sure that you have used the option -UseSSL if your SMTP server requires a secure connection.

Thursday, 5 September 2019

Hide Office 365 Group from GAL via Graph API

We can easily hide Office 365 Groups from Global Address List (GAL) by using the Set-UnifiedGroup cmdlet by setting the property HiddenFromAddressListsEnabled as True. You can refer this post for more details.
Set-UnifiedGroup "group_name" -HiddenFromAddressListsEnabled:$true
In some scenarios, we need to set this property using Graph API. Microsoft Graph introduced the property resourceBehaviorOptions which includes a set of options that are related to Exchange properties of the group. The property resourceBehaviorOptions includes the option HideGroupInOutlook which is equivalent to HiddenFromAddressListsEnabled.

Hide from GAL while creating new Office 365 Group using Graph API

The following Graph API call creates a new group and sets the option HideGroupInOutlook in resourceBehaviorOptions which in-turn hides the group from global address book in Outlook.
POST : https://graph.microsoft.com/v1.0/groups
Body :
{
  "description": "This is test group",
  "displayName": "TestO365Group",
  "groupTypes": [
    "Unified"
  ],
  "mailEnabled": true,
  "mailNickname": "TestO365Group",
  "securityEnabled": false,
  "resourceBehaviorOptions":["HideGroupInOutlook"]
}

Update GAL visibility in existing Office 365 Group using Graph API

As of now, the Graph REST API supports this option only when creating a new group and you can't update the resourceBehaviorOptions property in existing groups. Currently you will get below error when you try to edit this option in existing group.
PATCH : https://graph.microsoft.com/v1.0/groups/{id}
Body  :
{
  "resourceBehaviorOptions":["HideGroupInOutlook"]
}

Error :
"code": "Request_BadRequest",
"message": "Property cannot be updated because it is immutable."

Supported values in resourceBehaviorOptions:

  • AllowOnlyMembersToPost
  • CalendarMemberReadOnly
  • ConnectorsEnabled
  • HideGroupInOutlook
  • NotebookForLearningCommunitiesEnabled
  • ReportToOriginator
  • SharePointReadonlyForMembers
  • SubscriptionEnabled
  • SubscribeMembersToCalendarEvents
  • SubscribeMembersToCalendarEventsDisabled
  • SubscribeNewGroupMembers
  • WelcomeEmailDisabled
  • WelcomeEmailEnabled

Saturday, 10 August 2019

Move SharePoint Online Sites using Powershell

You can now swap your SharePoint Online site with another site using the Invoke-SPOSiteSwap Powershell cmdlet. When the swap is initiated, the target site is moved to the archive location and the source site is moved to the target location.

To use this new cmdlet, you need to use SharePoint Online Powershell version 16.0.8812.1200 or later (Download Link: https://www.microsoft.com/en-us/download/details.aspx?id=35588). You can install the latest SPO Powershell by running below command.
Install-Module -Name Microsoft.Online.SharePoint.PowerShell -MinimumVersion "16.0.8812.1200"

Current Limitations :

  • The target site can only be the root site (https://tenant-name.sharepoint.com) or the search center (https://tenant-name.sharepoint.com/search.).
  • The source or target sites can't be associated with an Office 365 Group (team) or a hub site. If the site is a associated to a hub site, you can remove the association, perform the swap and then re enable the association.
  • If the target is the root site at https://tenant-name.sharepoint.com then the source site must be either a Team Site (STS#0), a Modern Team Site (STS#3), or a Communication Site (SITEPAGEPUBLISHING#0).
  • If the target is the search center site at https://tenant-name.sharepoint.com/search then the source site must be either a Search Center Site (SRCHCEN#0) or a Basic Search Center Site (SRCHCENTERLITE#0).
The below commands archives the existing root site and moves the CommunicationSite to root site.
Connect-SPOService -Url "https://<tenant-name>-admin.sharepoint.com"
$SourceSite = "https://<tenant-name>.sharepoint.com/sites/CommunicationSite"
$TargetSite = "https://<tenant-name>.sharepoint.com"
$AcrhiveSite = "https://<tenant-name>.sharepoint.com/sites/Archive"
Invoke-SPOSiteSwap -SourceUrl $SourceSite -TargetUrl $TargetSite -ArchiveUrl $AcrhiveSite
Note: Before proceed replace the parameter "<tenant-name>" with your tenant name in commands.

You can refer this post for more details.

Monday, 5 August 2019

Get Microsoft Graph API Access Token using ClientID and ClientSecret

In some cases, apps or users might want to acquire Microsoft Graph access token by using the ClientID (Azure AD Application ID) and ClientSecret instead of providing their own credentials. In many cases, these are background services or automation jobs which require to authenticate a script without user interaction (Unattended Authentication). I would also recommend you to read the post Get access without a user.

Note: Assume that you have already registered an App in Azure AD through App Registration and you have the Client ID, Client Secret, and your Tenant Domain Name (or Tenant ID).

Required Parameters :

  • ClientID - AppId of your Azure AD Application.
  • ClientSecret - A secret code that you get from the registered app.
  • Tenant - Provide your tenant id or tenant domain name (ex: xxxxx.onmicrosoft.com). Refer this post to find your tenant id.

Get Access Token :

In the OAuth 2.0 client credentials grant flow, you can acquire an access token by sending a POST request to the /token identity platform endpoint with required parameters :
Http Method : POST
Endpoint URL : https://login.microsoftonline.com/Tenant/oauth2/v2.0/token
Content-Type : application/x-www-form-urlencoded
Body : scope=https://graph.microsoft.com/.default&grant_type=client_credentials&client_id=535fb089-9ff3-47b6-9bfb-4f1264799865&client_secret=qWgdYAmab0YSkuL1qKv5bPX

Get Graph Access Token Using Powershell :

In Powershell, you can use the Invoke-RestMethod cmdlet to send the post request to the /token identity endpoint. Use the below commands after replacing your own values for ClientID, ClientSecret and TenantId.
#This is the ClientID (Application ID) of registered AzureAD App
$ClientID = "535fb089-9ff3-47b6-9bfb-4f1264799865"
#This is the key of the registered AzureAD app
$ClientSecret = "qWgdYAmab0YSkuL1qKv5bPX"
#This is your Office 365 Tenant Domain Name or Tenant Id
$TenantId = "m365xxxxx.onmicrosoft.com"
#$TenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$Body = @{client_id=$ClientID;client_secret=$ClientSecret;grant_type="client_credentials";scope="https://graph.microsoft.com/.default";}
$OAuthReq = Invoke-RestMethod -Method Post -Uri https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token -Body $Body
$TokenType = $OAuthReq.token_type
$AccessToken = $OAuthReq.access_token

Call Graph API Using Powershell :

The below command retrieves all the Azure AD user details by passing the acquired access token.
$apiUrl = "https://graph.microsoft.com/v1.0/users"
$users = Invoke-RestMethod -Headers @{Authorization = "Bearer $AccessToken"} -Uri $apiUrl -Method Get
Note : Before using the above end-point (https://graph.microsoft.com/v1.0/users), you should have added the Application permission User.Read.All (or User.ReadWrite.All) in your Azure AD app and you should have already provided Admin consent for your app.

Fix - AADSTS90002: Tenant 'xxxxxx' not found. This may happen if there are no active subscriptions for the tenant.

Problem :

Recieived the below error when I try to get Microsoft Graph access token with my Azure AD Application using OAuth 2.0 client credentials grant flow.
Token identity endpoint URL : https://login.microsoftonline.com/xxxxxx/oauth2/v2.0/token

"error":"invalid_request","error_description":"AADSTS90002: Tenant 'xxxxxx' not found. This
may happen if there are no active subscriptions for the tenant. Check with your subscription administrator

Fix/Solution :

We need to provide either TenantID (Guid) or Tenant DomainName (ex: xxxxx.onmicrosoft.com) in token identity endpoint URL to get access token. But in my case, I have just provided tenant name alone instead of tenant domain name/tenantId, the problem solved after providing complete tenant domain name.
#Token endpoint URL with tenant domain name
https://login.microsoftonline.com/xxxxxx.onmicrosoft.com/oauth2/v2.0/token

#Token endpoint URL with tenant id (guid)
Token identity endpoint URL : https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/oauth2/v2.0/token
To find your tenant id, you can refer this post : How to find your Office 365 Tenant ID

How to find your Office 365 Tenant ID

Office 365 Tenant ID is a globally unique identifier (GUID) value for your Azure AD Tenant. You can find your Tenant ID in the following methods.

1. From Azure AD Portal

You can find your tenant ID in the Azure AD portal if you have Azure AD administrator privilege.
  • Log in to Azure AD Portal (https://portal.azure.com).
  • In the Azure portal, click Azure Active Directory in the left-side navigation box.
  • Under Manage, click Properties. You can retrieve the tenant id from the Directory ID box.
How to retrieve your Office 365 Tenant ID


2. Use Azure AD PowerShell

If you have already installed Azure AD Powershell V2 module, then you can get TenantId while running the Connect-AzureAD cmdlet.
PS C:\> Connect-AzureAD | FL

Account      : UserName@DomainName.onmicrosoft.com
Environment  : AzureCloud
Tenant       : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
TenantId     : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
TenantDomain : DomainName.onmicrosoft.com

3. Use Azure CLI

If you have Azure CLI setup, you can run the command "azure account show" to get your tenant id.
$ azure account show

data:    Name                        : Subscription Name
data:    ID                          : yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
data:    State                       : Enabled
data:    Tenant ID                   : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
data:    Is Default                  : true
data:    Environment                 : AzureCloud
data:    Has Certificate             : No
data:    Has Access Token            : Yes
data:    User name                   : kevin@xxxxxxx.onmicrosoft.com
You can fine tune the result by running below commands.
azure account show --json | jq -r '.[0].tenantId'

or 

az account show --subscription a... | jq -r '.tenantId'
az account list | jq -r '.[].tenantId'

Related Posts :

Wednesday, 31 July 2019

Get all Public Folders and Permissions using Powershell

You can get a list of all public folders with the Exchange powershell cmdlet Get-PublicFolder and you can easily extract the permissions applied to the public folder by using the Get-PublicFolderClientPermission cmdlet.

Before proceed run the below commands to connect Exchange Online (EXO) powershell.
$365Logon = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $365Logon -Authentication Basic -AllowRedirection
Import-PSSession $Session
Run the below command to list all the public folders in your organization.
Get-PublicFolder -Recurse -ResultSize Unlimited
The below command returns the specific folder "Support" and all the sub-folders located under this folder.
Get-PublicFolder -Identity "\Support" -Recurse -ResultSize Unlimited
The below command list only first level sub-folders alone.
Get-PublicFolder -Identity "\Support" -GetChildren -ResultSize Unlimited
Run the below command to list the applied permissions in a public folder.
Get-PublicFolderClientPermission -Identity "\Support" | Select User, AccessRights
The below command returns all the folders and the permissions applied to each folder.
Get-PublicFolder -Recurse | Get-PublicFolderClientPermission | Select Identity, User, AccessRights

Export all Public Folder Permissions

The below commands fetch all folders and permissions applied to the folders and export the result to CSV file.
$Result=@()
$allFolders = Get-PublicFolder -Recurse -ResultSize Unlimited
$totalfolders = $allFolders.Count
$i = 1 
$allFolders | ForEach-Object {
$folder = $_
Write-Progress -activity "Processing $folder" -status "$i out of $totalfolders completed"
$folderPerms = Get-PublicFolderClientPermission -Identity $folder.Identity
$folderPerms | ForEach-Object {
$Result += New-Object PSObject -property @{ 
Folder = $folder.Identity
User = $_.User
Permissions = $_.AccessRights
}}
$i++
}
$Result | Select Folder, User, Permissions |
Export-CSV "C:\\PublicFolderPermissions.CSV" -NoTypeInformation -Encoding UTF8

Thursday, 25 July 2019

Create a new public folder in Office 365 using Powershell

Public folders are used to share information and collaborate with other people in your organization. Once the admin enables public folders they are automatically shown in all users' Outlook client. The content is organized in a hierarchy which makes browsing very easier for users.

Things To Know Before Proceed :

  • Public folder content can include email messages, posts, documents, and eForms.
  • Public folder architecture uses specially designed mailboxes (Public folder mailbox) to store both the public folder hierarchy (folder structure) and the content.
  • Before creating a public folder, you must first create a public folder mailbox.
  • There are two types of public folder mailboxes: the primary hierarchy mailbox and secondary hierarchy mailboxes. Both types of mailboxes can contain content.
  • Primary hierarchy mailbox: The first public folder mailbox you create will be the primary hierarchy mailbox in your organization. This is the only mailbox containing a writable (read/write) copy of the public folder structure. The public folder hierarchy is copied to all other public folder mailboxes, but these will be read-only copies.
  • Secondary hierarchy mailboxes: Any additional public folder mailboxes you create will be secondary mailboxes. The secondary hierarchy mailboxes contain public folder content and a read-only copy of the public folder hierarchy.

Create a new public folder mailbox using Powershell

We can use the New-Mailbox cmdlet to create a public folder mailbox. Before proceed run the below commands to connect Exchange Online (EXO) powershell.
$365Logon = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $365Logon -Authentication Basic -AllowRedirection
Import-PSSession $Session
The first mailbox you create will be the primary hierarchy mailbox in your organization.
New-Mailbox -PublicFolder -Name "Master-PFM"
Once you created a public folder mailbox (primary), any additional public folder mailboxes you create will be secondary mailboxes.
New-Mailbox -PublicFolder -Name "PFM2"
You can get your primary public folder mailbox by running the following command.
Get-OrganizationConfig | Format-List RootPublicFolderMailbox

Create a new public folder using Powershell

Once you created required public folder mailbox, you can use the New-PublicFolder cmdlet to create a public folder. The below command creates the public folder Support in the root of the public folder.
New-PublicFolder -Name "Support"
The below command creates the public folder Product1 under the existing folder \Support.
New-PublicFolder -Name "Product1" -Path "\Support"
The below command creates the public folder FAQs under the existing folder \Support\Product1.
New-PublicFolder -Name "FAQs" -Path "\Support\Product1"
The below command creates the public folder Marketing in the specific mailbox PFM2.
New-PublicFolder -Name "Marketing" -Mailbox "PFM2"

Tuesday, 23 July 2019

Fix : The query cannot be completed because the number of lookup columns exceeds the threshold limit

Problem

You might have received the below error when you use SharePoint Rest API to retrieve list items.
Code: Microsoft.SharePoint.SPQueryThrottledException
Message: "The query cannot be completed because the number of lookup columns it contains exceeds the lookup column threshold enforced by the administrator."
In my case I have received the same error for the following queries :
https://Tenant.sharepoint.com/sites/MySite/_api/web/lists/getByTitle('MyList')/items?$select=*

https://Tenant.sharepoint.com/sites/MySite/_api/web/lists/getByTitle('MyList')/items

Cause

As error message clearly indicates, this problem occurs since the problematic list contains lookup columns more than the List View Lookup Threshold limit. In SharePoint On-premises, the List View Lookup Threshold default limit is 8 and it can be changed on the Resource Throttling page in Central Administration. In SharePoint Online, this limit is 12 lookup columns and increasing the List View Threshold is not supported in SharePoint Online.

What columns are classified as Lookup columns

The following column types are classified as lookup columns: (Source: List view Lookup threshold uncovered)
  • Standard lookup columns
  • Managed metadata (single and multi-value)
  • People and group (single and multi-value)
  • Workflow status Created by (people and group)
  • Modified by (people and group)
Additionally following columns shows on list view also work as lookup columns : Name ( linked to Document) , Link (Edit to edit item) , Name ( linked to Document with edit menu), type ( icon linked to document).

Workaround

In Rest API, you can fix this problem by limiting the number columns to be returned using Select option. If you do not use the select option (or if you use select=*), then the rest query try to retrieve all fields, so you can limit the columns by providing only required columns in select option.
https://Tenant.sharepoint.com/sites/MySite/_api/web/lists/getByTitle('MyList')/items?$select=Title,Id
Note : Your select query should not include more than 12 (List View Lookup Threshold) lookup columns.

You can apply the same workaround if you face the issue in Microsoft Flow and PowerApps. For SharePoint views, you can just modify your view to list only required columns so that there will be no more than 12 lookup columns inside your view.

Monday, 22 July 2019

Fix : Connect-SPOService : Could not authenticate to SharePoint Online using OAuth 2.0

Problem :

You might have received the below error when you try to connect SPO site using Connect-SPOService cmdlet.
Connect-SPOService : Could not authenticate to SharePoint Online https://TenantName.sharepoint.com/sites/SiteName
using OAuth 2.0
At line:1 char:1
+ Connect-SPOService -Url https://TenantName.sharepoint.com/sites/SiteName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Connect-SPOService], AuthenticationException
    + FullyQualifiedErrorId : Microsoft.Online.SharePoint.PowerShell.AuthenticationException,Microsoft.Online.SharePoi
   nt.PowerShell.ConnectSPOService

Fix/Soution:

In my case, I have provided my site URL as opposed to my SharePoint Admin Site URL. The URL parameter of Connect-SPOService cmdlet accepts only the URL of SharePoint Online Administration Center site. My problem solved after providing Admin site URL.
Connect-SPOService -url https://TenantName-admin.sharepoint.com
If you face the problem even with Admin site URL, then pass the -Credential parameter to bypass ADAL/Modern authentication and use legacy authentication.
$PSCred = Get-Credential
Connect-SPOService -url https://TenantName-admin.sharepoint.com -Credential $PSCred
Note: The legacy authentication will not work MFA enabled user account.

If you still face the issue, try to re-install the SPO Powershell module, reboot machine and check the case again.

Sunday, 21 July 2019

Fix : Connect-SPOService : Current site is not a tenant administration site

Problem :

You might have received the below error when you try to connect your SharePoint Online site using Connect-SPOService cmdlet.
Connect-SPOService : Current site is not a tenant administration site.
At line:1 char:1
+ Connect-SPOService -Url https://TenantName.sharepoint.com/sites/SiteName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Connect-SPOService], ServerException
    + FullyQualifiedErrorId : Microsoft.SharePoint.Client.ServerException,Microsoft.Online.SharePoint.PowerShell.Conne
   ctSPOService

Fix/Soution:

The URL parameter of the Connect-SPOService cmdlet accepts only the url of SharePoint Online Administration Center site. To work with any of your site using SharePoint Online PowerShell module, you have to first establish a connection with SPO tenant site which is the admin site of your SharePoint tenant in Office 365, once this connection is established you can access any SPOSite using Get-SPOSite cmdlet.
Connect-SPOService -Url https://TenantName-admin.sharepoint.com
Once you connected your Admin site, you can get your required SPO site details using the below command.
Get-SPOSite -Identity https://TenantName.sharepoint.com/sites/SiteName
You can set required details in SPO site using the below command
Set-SPOSite -Identity https://TenantName.sharepoint.com/sites/SiteName -Owner username@domain.com -NoWait
For more details read How to Connect SharePoint Online PowerShell Module.

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

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

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"}

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 :

We can extract the tenant name in different ways (i.e. Graph API, Powershell , from SharePoint and OneDrive URL).

Using Microsoft Graph API:

We can use the following Microsoft Graph API endpoints to find the tenant name from verifiedDomains list.

Get organization endpoint:
https://graph.microsoft.com/v1.0/organization
#Select only verifiedDomains property
https://graph.microsoft.com/v1.0/organization?$select=verifiedDomains
List domains endpoint:
https://graph.microsoft.com/v1.0/domains
Both endpoints return all the verified domains in your organization. You have to look for the initial domain ("isInitial": true) from the domains list, the initial domain name is your full tenant domain name and removing the subdomain part ".onmicrosoft.com" will provide the tenant name of your organization.
{
 "capabilities": "Email, OfficeCommunicationsOnline",
 "isDefault": false,
 "isInitial": true,
 "name": "contoso.onmicrosoft.com",
 "type": "Managed"
 }
From the above result :

Tenant domain name is: "contoso.onmicrosoft.com"
Tenant name is: "contoso"

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

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

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'}

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.

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

Monday, 1 July 2019

Upload files to OneDrive for Business site using PowerShell

OneDrive for Business site (ODFB) is built on top of SharePoint Online (SPO). Users' personal OneDrive site is nothing but a special site collection in SharePoint, so if you have enough permission on the required user's OneDrive site, then you can do whatever operations (as like you can do with SPO site) in Powershel with the help of CSOM and SharePoint Online module. In this post, I am going to share CSOM based powershell script to fetch documents from local folder and upload into an user's OneDrive location.

Permissions Required :

By default every users have full access permission on their personal OneDrive site. If you are going to upload in your own drive, then you don't need extra permission. If you are going to upload files into another user's OneDrive, then you should add yourself as a site collection admin of the particular user's OneDrive site. Refer this post for more details : Add Site Collection Admin to OneDrive Users

Required details to upload files into OneDrive document library :

Before proceed you need to replace the following input parameters in below script.

$LocalFolder - Specify your local folder path.
$AdminAccount - Provide admin user account.
$AdminPass - Provide admin password.
$OneDriveURL - User's OneDrive site URL.
$TargetFolderName - OneDrive folder where you want to upload. Leave empty if you want to upload in root folder. If you specify target folder, then the folder should already exist in OneDrive root folder.

Available Scripts:

Upload local files into OneDrive folder

#Add required references to SharePoint client assembly to use CSOM 
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")  

# Specify local folder path
$LocalFolder = "C:\Users\Username\Documents\MyFiles"

# Specify the User account for an Office 365 global admin in your organization
$AdminAccount = "admin@<your tenant name>.onmicrosoft.com"
$AdminPass = "admin_password"

# Specify User's OneDrive Site URL and Folder name
$OneDriveURL = "https://<your tenant name>-my.sharepoint.com/personal/username_domainame_com"
$DocumentLibrary ="Documents"
$TargetFolderName ="" #Leave empty to target root folder

# Connect and Load OneDrive Library and Root Folder
$SecPwd = $(ConvertTo-SecureString $AdminPass -asplaintext -force) 
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($OneDriveURL) 
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($AdminAccount,$SecPwd) 
$Ctx.credentials = $Credentials
$List = $Ctx.Web.Lists.GetByTitle("$DocumentLibrary")
$Ctx.Load($List)
$Ctx.Load($List.RootFolder)
$Ctx.ExecuteQuery()

#Setting Target Folder
$TargetFolder = $null;
If($TargetFolderName) {
$TargetFolderRelativeUrl = $List.RootFolder.ServerRelativeUrl+"/"+$TargetFolderName
$TargetFolder = $Ctx.Web.GetFolderByServerRelativeUrl($TargetFolderRelativeUrl)
$Ctx.Load($TargetFolder)
$Ctx.ExecuteQuery()
if(!$TargetFolder.Exists){
Throw  "$TargetFolderName - the target folder not exist in OneDrive root folder."
}
} Else {
$TargetFolder = $List.RootFolder
}

#Get files from local folder and Upload into OneDrive folder
$i = 1
$Files = (Dir $LocalFolder -File) # Read files only from root folder
#$Files = (Dir $LocalFolder -File -Recurse) # Read files both from root folder and all sub folders
$TotoalFiles = $Files.Length
ForEach ($File in $Files)
{
Try {
Write-Progress -activity "Uplading $File" -status "$i out of $TotoalFiles completed"
$FileStream = New-Object IO.FileStream($File.FullName,[System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$FileCreationInfo.URL = $File
$Upload = $TargetFolder.Files.Add($FileCreationInfo)
$Ctx.Load($Upload)
$Ctx.ExecuteQuery()
}
catch
{
Write-Host $_.Exception.Message -Forground "Red"
}
$i++
}

Upload files recursively from local folder and subfolders with folder structure

You can use the following script to read files recursively from local folder with nested folder structure and upload files and sub folders into the given OneDrive folder with complete folder hierarchy.
#Add required references to SharePoint client assembly to use CSOM 
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")  

# Specify local folder path
$LocalFolder = "C:\Users\Username\Documents\MyFiles"

# Specify the User account for an Office 365 global admin in your organization
$AdminAccount = "admin@<your tenant name>.onmicrosoft.com"
$AdminPass = "admin_password"

# Specify User's OneDrive Site URL and Folder name
$OneDriveURL = "https://<your tenant name>-my.sharepoint.com/personal/username_domainame_com"
$DocumentLibrary ="Documents"
$TargetFolderName ="" #Leave empty to target root folder

# Connect and Load OneDrive Library and Root Folder
$SecPwd = $(ConvertTo-SecureString $AdminPass -asplaintext -force) 
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($OneDriveURL) 
$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($AdminAccount,$SecPwd) 
$Ctx.credentials = $Credentials
$List = $Ctx.Web.Lists.GetByTitle("$DocumentLibrary")
$Ctx.Load($List)
$Ctx.Load($List.RootFolder)
$Ctx.ExecuteQuery()

# Setting Target Folder
$TargetFolder = $null;
$TargetFolderRelativeUrl;
If($TargetFolderName) {
$TargetFolderRelativeUrl = $List.RootFolder.ServerRelativeUrl+"/"+$TargetFolderName
$TargetFolder = $Ctx.Web.GetFolderByServerRelativeUrl($TargetFolderRelativeUrl)
$Ctx.Load($TargetFolder)
$Ctx.ExecuteQuery()
if(!$TargetFolder.Exists){
Throw  "$TargetFolderName - the target folder not exist in OneDrive root folder."
}
} Else {
$TargetFolder = $List.RootFolder
$TargetFolderRelativeUrl = $TargetFolder.ServerRelativeUrl
}

# Get folders and sub folders from source location
$folders= @()
foreach ($file in (Get-ChildItem  -Recurse -Path $LocalFolder -Attributes Directory))
{
$folders +=($file.FullName).replace($LocalFolder+'\','')
}

# Create folders and sub-folders in destination location
Write-Progress -activity "Creating folder structure in OneDrive" -status "Creating Folder"
foreach ($folder in $folders)
{
$subfolder_names = $folder.split('\')
$subfolder = $TargetFolder.Folders.Add($subfolder_names[0])
$Ctx.Load($subfolder)
$Ctx.ExecuteQuery()
for ($i = 1; $i -le ($subfolder_names.Count-1) ; $i++)
{
$subfolder = $subfolder.folders.Add($subfolder_names[$i])
$Ctx.Load($subfolder)
$Ctx.ExecuteQuery()
}
}

# Read all files recursively from the local folder and upload into corresponding OneDrive folder.
$i = 1
$Files = (Dir $LocalFolder -File -Recurse)
$TotoalFiles = $Files.Length
ForEach($File in $Files)
{
Try {
$URL_Dest = $TargetFolderRelativeUrl +(($file.FullName).Replace($LocalFolder,'')).Replace('\','/')
Write-Progress -activity "Uplading $File" -status "$i out of $TotoalFiles completed"
$FileStream = New-Object IO.FileStream($File.FullName,[System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$FileCreationInfo.URL = $URL_Dest
$Upload = $TargetFolder.Files.Add($FileCreationInfo)
$Ctx.Load($Upload)
$Ctx.ExecuteQuery()
}
catch {
Write-Host $_.Exception.Message -Forground "Red"
}
$i++
}

Thursday, 20 June 2019

Add user as owner of all Office 365 groups using Powershell

As you know modern Office 365 groups are being used as base service for other Office 365 workloads (Ex: Teams, Planner).So you have to first member of the group to manage and work with groups and its associated services, if you want to do some high privilege actions (ex: delete group, delete team, or delete planner plan) then you have to be added as a owner of the group.

With Global Admin privilege, sometimes you may wanted to delete a group or update some group settings, but without being a owner of the group you can't delete group even though you have global admin privilege. We can use Exchnage Online Powershell cmdlet Add-UnifiedGroupLinks to add user as owner and member of unified group.

Before proceed, first connect EXO Powershell module by using below commands :
$365Logon = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $365Logon -Authentication Basic -AllowRedirection
Import-PSSession $Session
The below commands add an user as owner and member of the specific group :
Add-UnifiedGroupLinks –Identity "TestO365Group" –LinkType Members –Links username@yourdomain.com
Add-UnifiedGroupLinks –Identity "TestO365Group" –LinkType Owners –Links username@yourdomain.com
Note : To get complete privilege, you have to add the user as owner and also as a member of the group.

Add user as owner of all Office 365 Groups :

To add an user as owner of all the available o365 groups in your organization, first you have to get all the unified groups using the Get-UnifiedGroup cmdlet and pipe the results to Add-UnifiedGroupLinks cmdlet to add user one by one.
$userToAddOwner = "username@yourdomain.com"
Get-UnifiedGroup -ResultSize Unlimited | ForEach-Object {
$o365group = $_
Add-UnifiedGroupLinks –Identity $o365group.Name –LinkType Members –Links $userToAddOwner
Add-UnifiedGroupLinks –Identity $o365group.Name –LinkType Owners –Links $userToAddOwner
}

Add user as owner of multiple groups from CSV :

Consider the csv file o365Groups.csv that includes the column GroupName which holds the name of groups in each row of the csv file.
$userToAddOwner = "username@yourdomain.com"
Import-CSV "C:\o365Groups.csv" | ForEach-Object {
$groupName = $_."GroupName"
Add-UnifiedGroupLinks –Identity $groupName –LinkType Members –Links $userToAddOwner
Add-UnifiedGroupLinks –Identity $groupName –LinkType Owners –Links $userToAddOwner
}
You can use the Remove-UnifiedGroupLinks cmdlet to remove membership and ownership of an user from Office 365 group.

Friday, 14 June 2019

Get info of all the configured Outlook profiles using Powershell

In Outlook client you can configure multiple email accounts as per your need. You can add your primary work email as default profile and configure personal email as secondary profile. Sometimes you may need to fetch details of all the configured profiles, you can use outlook client itself for this need, but an easiest way is Powershell to retrieve all the properties from multiple accounts.

Open the powershell console and run the below command to list DisplayName and SmtpAddress of all accounts.
$outlookApplication = New-Object -ComObject 'Outlook.Application'
$accounts = $outlookApplication.Session.Accounts
$accounts | Select DisplayName, SmtpAddress
Use below command to list all the available properties.
$accounts | FL
The below command returns the default profile name
$outlookApplication.Application.DefaultProfileName
Note: The above commands will work only in Outlook client installed machine as it uses the interface library "Microsoft.Office.Interop.Outlook.Application" which works under the logged-in user's outlook context.

Since the above commands are working under the logged-in user's outlook context, you have to open the powershell without Run as administrator privilege. If you have opened the powershell console with Run as administrator privilege, then you will get the below error :
PS C:\WINDOWS\system32> $outlookApplication = New-Object -ComObject 'Outlook.Application'
New-Object : Retrieving the COM class factory for component with CLSID {0006F03A-0000-0000-C000-000000000046} failed
due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005
(CO_E_SERVER_EXEC_FAILURE)).
At line:1 char:23
+ $outlookApplication = New-Object -ComObject 'Outlook.Application'
+                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [New-Object], COMException
    + FullyQualifiedErrorId : NoCOMClassIdentified,Microsoft.PowerShell.Commands.NewObjectCommand

Wednesday, 5 June 2019

Get Azure AD Users with their Registered Devices using Powershell

In this post I am going to share Powershell script to find and list devices that are registered by Azure AD users. We can use the Get-AzureADUserRegisteredDevice cmdlet to get the registered devices.

Before proceed run the below command to connect Azure AD Powershell module.
Connect-AzureAD
The below command gets the devices that are registered to the specified user.
$user = Get-AzureADUser -SearchString "UserName"
Get-AzureADUserRegisteredDevice -ObjectId  $user.ObjectId -All $true

List registered devices of all Azure AD users :

To get a report of device list for all Azure AD users, first we need to get users by Get-AzureADUser cmdlet and pipe the users list to Get-AzureADUserRegisteredDevice cmdlet.
$Result=@()
$Users = Get-AzureADUser -All $true | Select UserPrincipalName,ObjectId
$Users | ForEach-Object {
$user = $_
Get-AzureADUserRegisteredDevice -ObjectId $user.ObjectId | ForEach-Object {
$Result += New-Object PSObject -property @{ 
DeviceOwner = $user.UserPrincipalName
DeviceName = $_.DisplayName
DeviceOSType = $_.DeviceOSType
ApproximateLastLogonTimeStamp = $_.ApproximateLastLogonTimeStamp
}
}
}
$Result | Select DeviceOwner,DeviceName,DeviceOSType,ApproximateLastLogonTimeStamp

Export Report to CSV file :

You can export the result to CSV file using the command Export-CSV.
$Result | Export-CSV "C:\\AzureADJoinedDevices.csv" -NoTypeInformation -Encoding UTF8