Friday, 22 December 2017

How to find file and folder size using PowerShell

We can use the PowerShell cmdlet Get-Item to get file information including size of a file, we can also the the same command to get folder or directory information but it will return the size of folder as folder is determined by size of all the files that are inside the particular directory.

Find file size :

The below command returns the size of the given file as bytes.
$file = 'C:\Samples\Test.csv'
$size=(Get-Item $file).length
Write-Host "Size(Bytes): "$size -ForegroundColor Green

Get file size as KB, MB and GB :

We can use the function [System.Math]::Round to convert the byte value to desired unit like KB, MB and GB.
$file = 'C:\Samples\TestVideo.mp4'
$size = (Get-Item $file).length
$sizeKB = [System.Math]::Round((($size)/1KB),2)
$sizeMB = [System.Math]::Round((($size)/1MB),2) 
$sizeGB = [System.Math]::Round((($size)/1GB),2)
Write-Host "Size(KB): "$sizeKB -ForegroundColor Green
Write-Host "Size(MB): "$sizeMB -ForegroundColor Green
Write-Host "Size(GB): "$sizeGB -ForegroundColor Green

Find folder size :

You can’t directly find the size of a directory, but you can indirectly determine the size of a folder by getting files using the cmdlets Get-ChildItem and Measure-Object.
$folderInfo = Get-ChildItem C:\Scripts | Measure-Object -Property Length -Sum
$filesCount = $folderInfo.Count
$folderSize = $folderInfo.Sum
$folderSizeMB = [System.Math]::Round((($folderSize)/1MB),2) 
Write-Host "Folder Size(MB): "$folderSizeMB  "Total Files: "$filesCount -ForegroundColor Green
Note: The above command calculate folder size only using first level files , it will not include the nested folder files.

Find entire folder size with recursively :

To find actual directory size we need to query the Get-ChildItem with -Recurse parameter to et nested level of folder files.
$folderInfo = Get-ChildItem C:\Scripts -Recurse | Measure-Object -Property Length -Sum
$allfilesCount = $folderInfo.Count
$folderSize = $folderInfo.Sum
$folderSizeMB = [System.Math]::Round((($folderSize)/1MB),2) 
$folderSizeGB = [System.Math]::Round((($folderSize)/1GB),2) 
Write-Host "Folder Size(MB): "$folderSizeMB  "Total Files: "$filesCount -ForegroundColor Green
Write-Host "Folder Size(GB): "$folderSizeGB -ForegroundColor Green

Thursday, 21 December 2017

Connect Microsoft Graph API using PnP PowerShell

In this article I will show you how to connect Microsoft Graph and query user details using SharePoint PnP PowerShell Online module.

Prerequisites:

Before proceed we need to download and install the latest PnP PowerShell module. You can download latest released setup from GitHub. If your main OS is Windows 10, and if you have PowerShellGet installed, you can run the following command to install the PnP PowerShell module instead of download and install the setup:
Install-Module SharePointPnPPowerShellOnline -SkipPublisherCheck -AllowClobber -Force

Connect PnPOnline and Get Access Token:

To connect and query Microsoft Graph api end-point, first we need to get the access token with required permissions using Connect-PnPOnline and Get-PnPAccessToken cmdlets.
Connect-PnPOnline -Scopes "User.Read","User.ReadBasic.All"
$accessToken =Get-PnPAccessToken
You can refer Microsoft Grap Documentation to know more about required permissions for every end-point url.

Connect and Get data from Microsoft Graph Api :

Once you get the required access token you can easily query graph api using Invoke-RestMethod cmdlet by passing access token.

Example 1: The below command get the current user profile details.
$apiUrl = "https://graph.microsoft.com/v1.0/me"
$myPrfoile = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl -Method Get
Example 2: The below command get all the Azure AD user details.
$apiUrl = "https://graph.microsoft.com/v1.0/users"
$users = Invoke-RestMethod -Headers @{Authorization = "Bearer $accessToken"} -Uri $apiUrl -Method Get
Note: You can also refer this document Microsoft Graph Api Permissions to know more about Delegated permissions and Application permissions (Admin Consent).

PnP-PowerShell Module Install - Authenticode Issuer Mismatch

Problem:

I am receiving the publisher check error when I try to install SharePoint Online PnP PowerShell module from PowerShell window using below command.
Install-Module SharePointPnPPowerShellOnline
Error message:
Install-Module SharePointPnPPowerShellOnline -Force
PackageManagement\Install-Package : Authenticode issuer 'CN=Microsoft Root Certificate Authority 2011, O=Microsoft
Corporation, L=Redmond, S=Washington, C=US' of the new module 'SharePointPnPPowerShellOnline' with version
'2.21.1712.2' is not matching with the authenticode issuer 'CN=thawte Primary Root CA, OU="(c) 2006 thawte, Inc. - For
authorized use only", OU=Certification Services Division, O="thawte, Inc.", C=US' of the previously-installed module
'SharePointPnPPowerShellOnline' with version '2.20.1711.0'. If you still want to install or update, use
-SkipPublisherCheck parameter.
At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1809 char:21
+ ...          $null = PackageManagement\Install-Package @PSBoundParameters
+                      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (Microsoft.Power....InstallPackage:InstallPackage) [Install-Package],
   Exception
    + FullyQualifiedErrorId : AuthenticodeIssuerMismatch,Validate-ModuleAuthenticodeSignature,Microsoft.PowerShell.Pac
   kageManagement.Cmdlets.InstallPackage

Cause:

I have searched through Google and I see some people receiving the same error while update PnP PowerShell using the command "Update-Module SharePointPnPPowerShell*" and the cause of error is due to new signing certificates where used in latest setup.

Solution:

I have fixed this problem by setting the parameter SkipPublisherCheck as AllowClobber.
Install-Module SharePointPnPPowerShellOnline -SkipPublisherCheck -AllowClobber
-or-
Install-Module SharePointPnPPowerShellOnline -SkipPublisherCheck -AllowClobber -Force
Complete PowerShell Console log:
PnP-PowerShell  Module Install Error Authenticode Issuer Mismatch

Wednesday, 20 December 2017

Set Auto Reply on a Mailbox in Office 365 using PowerShell

In this post I am going to share how to configure automatic reply or out-of-office message on a user's mailbox. We can use the Set-MailboxAutoReplyConfiguration cmdlet to configure automatic reply settings for a specific user mailbox and this cmdlet is available for both Exchange On-Premises and Exchange Online environment.

Before proceed, first we need to connect Exchange Online powershel module by running below commands:
$o365Cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $o365Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session

Summary:


Set Automatic Reply for user's mailbox :

The below command configures Automatic Reply message to be sent on AlexW's mailbox.
Set-MailboxAutoReplyConfiguration -Identity "AlexW" -AutoReplyState Enabled -InternalMessage "Out of Office message for internal mails." -ExternalMessage "Out of Office message for external mails."
Once you run the above command the mailbox of AlexW will instantly start to send automatic reply for all incoming mails, instead if you want to send out of office message only for particular days, you need to set parameter -AutoReplyState as "Scheduled".

Set Automatic Reply in a specific time period :

If you want to configure to send auto reply message in a particular days like office holidays or when employees on leave, you need to set parameter -AutoReplyState as "Scheduled" along with "StartTime" and "EndTime".
Set-MailboxAutoReplyConfiguration -Identity "AlexW" -AutoReplyState Scheduled -StartTime "07/10/2018 01:00:00" -EndTime "7/15/2018 23:00:00" -InternalMessage "Internal out-of-office message"
Note: The parameters StartTime and EndTime are meaningful only when AutoReplyState is Scheduled. To set value for these two parameter, you need to use the short date format that's defined in the Regional Options settings on the computer where you're running the command.

Disable or Stop Auto Reply message :

Run the below command to disable or stop the out of office message.
Set-MailboxAutoReplyConfiguration -Identity "AlexW" -AutoReplyState Disabled
Run the below command if you want to clear or remove Internal/External message when you stop auto reply.
Set-MailboxAutoReplyConfiguration -Identity "AlexW" -AutoReplyState Disabled -InternalMessage "" -ExternalMessage ""

Disable Auto Reply message for External senders :

The Set-MailboxAutoReplyConfiguration cmdlet includes the parameter ExternalAudience, it specifies whether Automatic Replies are sent to external senders or not.

Valid values for ExternalAudience:
  • None - Don't sent message to any external senders.
  • Known - Send only to external senders that are specified in the Contact list of the mailbox.
  • All - Send to all external senders. This is the default value
 The below command enables auto reply for only internal senders by setting ExternalAudience value as "None".
Set-MailboxAutoReplyConfiguration -Identity "AlexW" -ExternalAudience "None" -AutoReplyState Scheduled -StartTime "07/10/2018 01:00:00" -EndTime "7/15/2018 23:00:00" -InternalMessage "Internal auto-reply message"

Get Automatic Reply Configuration :

Once you have configured the automatic reply settings you can get the latest setting by running below command.
Get-MailboxAutoReplyConfiguration -Identity "AlexW"
Run the below command if you want to get settings for all the mailbox users.
Get-Mailbox -ResultSize unlimited | Get-MailboxAutoReplyConfiguration

Monday, 18 December 2017

Create New Office 365 User Account using Powershell

Office 365 is not a single service and it is the brand name Microsoft uses for a group of cloud services along with new features in desktop Office suite. Azure Active Directory is the storage service for Office 365 user identities, we can easily create a new user account using the Azure AD powershell cmdlet New-MsolUser.

Note: Before proceed, Install and Configure Azure AD PowerShell and run the below command to connect Azure AD PowerShell module.
Import-Module MSOnline
$msolCred = Get-Credential
Connect-MsolService –Credential $msolCred

Summary:

Create new Office 365 user account :

Use the below command to create an individual user account without license subscription:
New-MsolUser -DisplayName "Steve Smith" -UserPrincipalName steves@contoso.com -Password "pass@word1" -FirstName Steve -LastName Smith
Note: The input -Password is the optional parameter if you don't specify a password, a random password will be assigned to the user account, and the password is visible in the results of the command.

Create new user account and assign license :

You don't need to assign a license when you create user account, but without license the new user can't access any of Office 365 services. Before proceed you need to get AccountSkuId of the license plan that you want to assign to new user, you can use the Get-MsolAccountSku cmdlet to get the available licensing plans ( AccountSkuId ) for your organization. For more info, see Get list of Office 365 licenses using Powershell.

The below command creates new Office 365 user account named "Steve Smith", set the Usage Location as United States and assign the license plan "contoso:ENTERPRISEPREMIUM".
New-MsolUser -DisplayName "Steve Smith" -UserPrincipalName steves@contoso.com -UsageLocation US -LicenseAssignment "contoso:ENTERPRISEPREMIUM" -FirstName Steve -LastName Smith
Note: The Usage Location is mandatory for the user when you assign license.

Create Bulk Office 365 user accounts from CSV file

Creating bulk user accounts is one of the important task for every Office 365 admin when multiple new employees join to organisation. In this case we can import user account details from CSV (Comma-Separated Value) file. Consider the CSV file NewOffice365Users.csv which contains user information with the column headers UserPrincipalName, FirstName, LastName, DisplayName, UsageLocation, AccountSkuId.

The below command creates user accounts by importing user details from the file "C:\NewOffice365Users.csv", and exports the results to "C:\NewAccountResults.csv".
Import-Csv "C:\NewOffice365Users.csv" | ForEach-Object {
New-MsolUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName -UsageLocation $_.UsageLocation -LicenseAssignment $_.AccountSkuId -FirstName $_.FirstName -LastName $_.LastName 
} | Export-CSV "C:\\NewAccountResults.csv" -NoTypeInformation -Encoding UTF8
Note: We didn't given the input parameter -Password in above command, so a random password will be assigned to every user accounts, and new password will be available in the exported csv file "C:\NewAccountResults.csv".

Friday, 15 December 2017

Export Blocked Office 365 Users to CSV using PowerShell

Getting list of sign-in blocked Azure AD users is one of the important task for every Office 365 admin to decide whether we are spending our license for valid user accounts or not. We can use the Azure AD powershell cmdlet Get-MsolUser to find and get a list of Office 365 users who are blocked to login into Office 365 service (Ex: Mailbox, Teams, Planner, SharePoint, etc).

Note: Before proceed, Install and Configure Azure AD PowerShell

Summary:


Find and List Blocked Users :

The Get-MsolUser cmdlet returns all the Azure AD users and we can apply the Where filter with parameter BlockCredential to get only sign-in blocked users.
Get-MsolUser -All | Where {$_.BlockCredential -eq $True} | Select DisplayName,UserPrincipalName

Export Blocked Users to CSV :

Run the below command to export all the users who have been blocked to login into Office 365 services.
Get-MsolUser -All | Where {$_.BlockCredential -eq $True} |
Select DisplayName,UserPrincipalName, IsLicensed |
Export-CSV "C:\\Blocked_Users.csv" -NoTypeInformation -Encoding UTF8

Export Licensed and Blocked Users to CSV :

Normally we will block access to the users who are leaving office or terminated employees, once we blocked access to Office 365 service the next step is to remove existing license subscriptions from blocked users, so we need to get a list of users who are licensed and blocked to login into Office portal. Run the following command to export all the licensed users who have been blocked.
Get-MsolUser -All | Where {$_.IsLicensed -eq $True -AND $_.BlockCredential -eq $True} |
Select DisplayName,UserPrincipalName, BlockCredential |
Export-CSV "C:\\Blocked_Licensed_Users.csv" -NoTypeInformation -Encoding UTF8
Note: Checkout this post : Manage Office 365 License using Powershell to add a new license, remove an existing license and update existing license subscription using PowerShell command.

Wednesday, 13 December 2017

Generate Random Numbers within a Range using PowerShell

We may required to generate a random number to set ID or in some cases to set temporary password. In PowerShell we can easily generate random numbers using Get-Random cmdlet and we can also use this cmdlet to select objects randomly from a collection of objects.

Summary:

Example 1: Get a random number

If you run Get-Random command without parameters or input it will returns a 32-bit unsigned random integer between 0 (zero) and Int32.MaxValue (2,147,483,647).
PS C:\> Get-Random
475917250

Example 2: Get a random number between given range

To set the range, we need to use the parameters -Minimum and -Maximum. The below command return a random number between 1 and 100.
PS C:\> Get-Random -Minimum 1 -Maximum 100
27

Example 3: Get a random number from array of numbers

Instead of generating random number from a given range we can also get a random number from our own list of numbers.
PS C:\> Get-Random -InputObject 1, 2, 4, 8, 12, 16,20,24
16
We can also get more than one random numbers by setting the parameter -Count.
PS C:\> Get-Random -InputObject 1, 2, 4, 8, 12, 16,20,24 -Count 3
2
8
20

Example 4: Get a character from random ASCII code

The ASCII code of upper-case alphabetical characters starts from 65 (A) and ends in 90 (Z), and lower-case chars starts from 97 (a) and ends in 122 (z), so we can get random number within this range and convert it to alphabetical character.
$code = Get-Random -Minimum 65 -Maximum 90
$random_char = [char]$code
Write-Host $random_char -ForegroundColor 'Yellow'

Example 5: Generate random password from ASCII code

In some situation we may required to generate random password from ASCII values with standard format, we can easily achieve this by getting random chars within the range of ASCII code.
$randomPassword = ""
1..5 | ForEach { 
$code = Get-Random -Minimum 65 -Maximum 122
$randomPassword = $randomPassword + [char]$code
}
Write-Host $randomPassword -ForegroundColor 'Yellow'

Tuesday, 12 December 2017

Reset Office 365 User Password using PowerShell

As you know Office 365 user identities are stored in Azure Active Directory, we can use the Azure AD powershell cmdlet Set-MsolUserPassword to set password of a user. You may already used the Set-MsolUser cmdlet to update user properties but we can't use the same command to change password.

Note: Before proceed, Install and Configure Azure AD PowerShell and run the following command to connect Azure AD powershell module.
Import-Module MSOnline
$msolCred = Get-Credential
Connect-MsolService –Credential $msolCred

Summary:

Set Password for Single User:

Run the below command to change the password for a single O365 user.
Set-MsolUserPassword –UserPrincipalName "user@domain.com" –NewPassword "pass@word1" -ForceChangePassword $False
Note: If you are Help Desk admin and you are resetting one time password for your end-user, you need to set the parameter -ForceChangePassword as $True, it will force the users to change their password from the portal the next time they sign-in.

You can find whether an user's password is set or not by getting user's password last set time by using Get-MsolUser cmdlet.
Get-MSOLUser -UserPrincipalName "user@domain.com" | Select DisplayName,LastPasswordChangeTimestamp

Change Password for Multiple Users:

In some scenarios, you might want to set temporary password for set of new users who are created in recent days. We can get the recently created users using Get-MsolUser cmdlet. The below command set temporary password for bulk users who are created in last 7 days, you can change the no of days or the Where filter as per your need.
Get-MsolUser -All | Where-Object { $_.WhenCreated –gt ([System.DateTime]::Now).AddDays(-7)} |
Set-MsolUserPassword –NewPassword "pass@word1" -ForceChangePassword $True

Reset Bulk Office 365 Users Password from CSV file

In some scenarios, we may required to set password for bulk azure ad users by importing user identities from csv file. Consider the CSV file office365users.csv which contains every user's userPrincipalName in each row with the column header UserPrincipalName.
Import-Csv 'C:\office365users.csv' | ForEach-Object {
$upn = $_."UserPrincipalName"
$tempPwd = "pass@word1"
Set-MsolUserPassword -UserPrincipalName $upn –NewPassword $tempPwd -ForceChangePassword $True
}

Friday, 8 December 2017

Find list of active mailboxes in Office 365 with PowerShell

We can use the Exchange powershell cmdlet Get-MailboxStatistics (On-premises and Online) to check the Last logon time of an user's mailbox. In this post I am going share powershell commands to find and get a list of active users who are actively using their mailbox in Office 365 environment.

Before proceed, first we need to connect Exchange Online powershel module by running below commands:
$o365Cred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $o365Cred -Authentication Basic -AllowRedirection
Import-PSSession $Session
You can find last logon time for a single user mailbox by running below command:
Get-MailboxStatistics -Identity "username@domain.com" | Select LastLogonTime
If you want to get last logon time for all the Office 365 mailbox users, first we need to get all mailbox details by using Get-Mailbox cmdlet and pipe the results to Get-MailboxStatistics.
Get-Mailbox -ResultSize Unlimited | Get-MailboxStatistics | Select-Object DisplayName,LastLogonTime

Find active mailboxes in last N days

To get active mailbox list we need to use Where filter in the output of Get-MailboxStatistics. The below powershell command find and retrieve all mailbox users who are logged-into their mailbox within last 7 days.
Get-Mailbox -RecipientType 'UserMailbox' -ResultSize Unlimited | Get-MailboxStatistics |
Where {$_.LastLogonTime –gt ([System.DateTime]::Now).AddDays(-7) } | Sort-Object LastLogonTime -Descending |
Format-Table DisplayName, LastLogonTime

Export list of active mailbox users to CSV file

The below command find and export list of active mailbox user names and their last logon time to CSV file.
Get-Mailbox -RecipientType 'UserMailbox' -ResultSize Unlimited | Get-MailboxStatistics |
Where {$_.LastLogonTime –gt ([System.DateTime]::Now).AddDays(-7) } | Sort-Object LastLogonTime -Descending |
Select DisplayName, LastLogonTime | Export-CSV "C:\\ActiveMailboxes.csv" -NoTypeInformation -Encoding UTF8
Note: Here I have used number of days as 7 to check logon activity, you can change this value (i.e 30 or 90 days) as per your need and you can also use the same commands for On-premise environment by properly connecting Exchange management powershell.

Wednesday, 6 December 2017

Add Bulk Users to Office 365 Group with PowerShell

As you know Microsoft is targeting Office 365 Group as base service for many of cloud services like Planner, Teams and etc. So now a days every admins getting frequent request to add new on-board users to Office 365 group, we can easily achieve this task with Exchange Online powershell cmdlet Add-UnifiedGroupLinks.

Before proceed run the below commands to connect Exchange Online Powershell session.
$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 add single user into Office 365 group.
Add-UnifiedGroupLinks –Identity "O365Group" –LinkType Members  –Links username@domain.com
We have to set the parameter LinkType as Members to add user as member and other valid values are Owners and Subscribers.

The parameter Links accept multiple values, we need to provide users as comma (",") separated values to add multiple members (ex: user1@domain.com,user2@domain.com....). If the user names contain spaces or otherwise require quotation marks (ex: "username 1","username 2",...).

Add multiple members to O365 group:
Add-UnifiedGroupLinks –Identity "O365Group" –LinkType Members  –Links "user1@domain.com","user2@domain.com"

Add Bulk Users to Office 365 Group from CSV

For bulk import process the CSV file is the best choice of all time, you can use the below powershell commands to add bulk members to an office 365 group by importing user identities from csv file. Consider the csv file members.csv that includes the column header member which holds the user identity values in each row of the csv file.
Import-CSV "C:\members.csv" | ForEach-Object {
Add-UnifiedGroupLinks –Identity "O365Group" –LinkType Members  –Links $_.member
}

Find and list members of unified group:

Once we added user as member in O365 group, we can use Get-UnifiedGroupLinks cmdlet to get members. The below command lists all members of given group.
Get-UnifiedGroupLinks –Identity "O365Group" –LinkType Members -ResultSize Unlimited

Monday, 4 December 2017

Change Office 365 Group Email Address using PowerShell

Modifying name or display name of Office 365 Group is simple. However, if you want to rename primary email address this is simply not possible from Admin center, but we can easily change it using the Exchange Online Powershell cmdlet Set-UnifiedGroup.

Note: Before proceed, Connect Exchange Online Remote PowerShell.

Rename Primary E-mail Address of Office 365 Group

We need to use the attribute PrimarySmtpAddress in Set-UnifiedGroup cmdlet to change the primary mail address of an O365 group. The below command change the primary address to salesgroupnew@domain.com for the group named "Sales Group".
Set-UnifiedGroup -Identity "Sales Group" -PrimarySmtpAddress "salesgroupnew@domain.com"

Rename Group Alias or Email Alias

To update email alias we need to update the attribute alias. The below command rename the mail alias to salesgroupnew.
Set-UnifiedGroup -Identity "Sales Group" -Alias "salesgroupnew"

Add or Remove Secondary Email Addresses (or Proxy Addresses)

We can use the parameter EmailAddresses in Set-UnifiedGroup cmdlet to update proxy addresses of office 365 group. The EmailAddresses parameter specifies all the email addresses (proxy addresses) for the recipient, including the primary SMTP address.

Syntax to update email addresses:
Set-UnifiedGroup -Identity "o365group" -EmailAddresses @{Add="[<Type>]:<emailaddress1>","
[<Type>]:<emailaddress2>"...;Remove="[<Type>]:<emailaddress3>","[<Type>]:<emailaddress4>"...}
The optional value <Type> specifies the type of email address. Some of valid values are:
  • SMTP - The primary SMTP address (You can use this value only once in a command).
  • smtp - Other SMTP email addresses.
If you don't include a <Type> value for an email address, the value smtp (proxy address) is assumed.

Add proxy address:
Set-UnifiedGroup -Identity "Sales Group" -EmailAddresses @{Add="salesgroup1@domain.com",
"salesgroup2@domain.com"}
Remove proxy address:
Set-UnifiedGroup -Identity "Sales Group" -EmailAddresses @{Remove="salesgroup3@domain.com",
"salesgroup4@domain.com"}
To add or remove proxy addresses without affecting other existing values, use the following syntax.
Set-UnifiedGroup -Identity "Sales Group" -EmailAddresses @{Add="salesgroup1@domain.com",
"salesgroup2@domain.com"; Remove="salesgroup3@domain.com","salesgroup4@domain.com"}

Change both Primary and Secondary Email Address in single command

We can easily update both primary and proxy address in a single command by specifying valid <Type> values (SMTP - for primary address. smtp - for proxy address).The following command removes the primary address salesgroupold@domain.com and the proxy address salesgroup3@domain.com, and adds the primary address salesgroupnew@domain.com and the proxy address salesgroup1@domain.com.
Set-UnifiedGroup -Identity "Sales Group" -EmailAddresses @{Remove="SMTP:salesgroupold@domain.com",
"smtp:salesgroup3@domain.com"; Add="SMTP:salesgroupnew@domain.com","smtp:salesgroup1@domain.com"; }

Export Email Address details of Office 365 group

We can use the following command to find and list the email address details for the given office 365 group.
Get-UnifiedGroup -Identity "Sales Group" | Select PrimarySMTPAddress,Alias,EmailAddresses|FL
The below command export email address details of all the office 365 groups to csv file.
Get-UnifiedGroup -ResultSize Unlimited | Select DisplayName,PrimarySMTPAddress,Alias,EmailAddresses |
Export-CSV "C:\\O365GroupMailAddresses.csv" -NoTypeInformation -Encoding UTF8

Wednesday, 22 November 2017

Remove user from Office 365 Group using PowerShell

We can use the Exchange Online powershell cmdlet Remove-UnifiedGroupLinks to remove members, owners and subscribers from Office 365 groups. The Remove-UnifiedGroupLinks cmdlet includes the following parameters:

Identity – This parameter specifies the Office 365 Group that you want to update. You can use alias, display name, or email address of the unified group that you want to modify.
Links  – This parameter specifies the recipients to remove from the Office 365 Group.You can use alias, display name, or email address of the user that you want to remove.
LinkType – Members, Owners, or Subscribers.

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

Remove user from Office 365 Group

The following command remove the member morgan@contoso.com from the Office 365 Group named TestO365Group. We need to set the parameter LinkType as Members to remove user from being a member.
Remove-UnifiedGroupLinks –Identity "TestO365Group" –Links morgan@contoso.com –LinkType Members -Confirm:$false

Remove multiple users from Office 365 Group

In above command the parameter Links accept multiple values, so we can easily remove multiple members by running single command by passing user identities as comma separated values (Ex: value1,value2....). If the user ids contain spaces we need to enclose every value by quotation marks (Ex: "value1","value2",..).

The below example removes the members alexw@contoso.com and alland@contoso.com from the Office 365 Group named TestO365Group.
Remove-UnifiedGroupLinks –Identity "TestO365Group" –Links alexw@contoso.com ,alland@contoso.com –LinkType Members -Confirm:$false

Remove bulk users from Office 365 Group (from CSV file)

You can use the below powershell commands to remove bulk members from an office 365 group by importing users from csv file. Consider the csv file members.csv that includes the column member which holds the member identity in each row of the csv file.
Import-CSV "C:\members.csv" | ForEach-Object {
Remove-UnifiedGroupLinks –Identity "TestO365Group" –Links $_.member –LinkType Members -Confirm:$false
Write-Host "The user" $_.member "removed"
}

Find and list members of Office 365 Group

Once we added or removed users in unified group, we can use the Get-UnifiedGroupLinks cmdlet to get members of a specific group. The below command lists all the members of the given group.
Get-UnifiedGroupLinks –Identity "TestO365Group" –LinkType Members -ResultSize "Unlimited"
You can also export all the member details to csv file using below command.
Get-UnifiedGroupLinks –Identity "TestO365Group" –LinkType Members -ResultSize "Unlimited" | 
Select DisplayName,Name,PrimarySMTPAddress |
Export-CSV "C:\\Office365GroupMembers.csv" -NoTypeInformation -Encoding UTF8

Wednesday, 4 October 2017

PowerShell : Check if user is member of local Adminstrators group

We can find whether the given user is member of local Administrators group or not by accessing ADSI WinNT Provider. In this post, I am going to write powershell script to check if an user is exists in local Administrators group in local machine and remote server.

Check if user is member of local Administrators group:

The following powershell commands checks whether the given user is member of Administrators group in local machine.
$user = "Morgan";
$group = "Administrators";
$groupObj =[ADSI]"WinNT://./$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 
$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})
If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}

Find if user is member of local Admins group in Remote server:

Use the below powershell command to check if user is member of Administrators group in remote computer.
$computer = "hp-pc" 
$user = "Morgan";
$group = "Administrators";
$groupObj =[ADSI]"WinNT://$computer/$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 
$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})
If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}

Check if multiple users are member of Administrators group:

Use the below powershell script to check if multiple users are member of local Admins group.
$users = "Morgan","TestUser1","TestUser2"
$group = "Administrators";
$groupObj =[ADSI]"WinNT://./$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 
$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})

ForEach ($user in $users) {
If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}}

Check if an user is member of a local group using PowerShell

We can easily find a local user is member of a local group by accessing ADSI WinNT Provider. In this post, I am going to share powershell script to check if local user is exists in a group, and check multiple users are member of a local group.

Check if local user is member of Administrators group:

The following powershell commands checks whether the given user is member of built-in Administrators group.
$user = "Morgan";
$group = "Administrators";
$groupObj =[ADSI]"WinNT://./$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 

$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})

If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}

Check if multiple users are member of a given local Group:

Run the below powershell command to check if multiple users are member of a given group.
$users = "Morgan","TestUser1","TestUser2"
$group = "Administrators";
$groupObj =[ADSI]"WinNT://./$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 

$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})

ForEach ($user in $users) {
If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}}

Check if users are member of a group in Remote Computer:

Use the below powershell command to check if users are member of a given group in remote machine/server.
$computer = "remote-pc" 
$users = "Morgan","TestUser1","TestUser2"
$group = "Administrators";
$groupObj =[ADSI]"WinNT://$computer/$group,group" 
$membersObj = @($groupObj.psbase.Invoke("Members")) 

$members = ($membersObj | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)})

ForEach ($user in $users) {
If ($members -contains $user) {
      Write-Host "$user exists in the group $group"
 } Else {
        Write-Host "$user not exists in the group $group"
}}

Tuesday, 26 September 2017

How to find Windows OS version using PowerShell

For troubleshooting purpose, or before deploy any software, it is good to know what is Windows operating system version that is currently running. We can easily find the OS details from My Computer properties, but if you want to get details from your customer machine to troubleshoot any issue, PowerShell is the best option to get all the required machine details.

In PowerShell, we can find operating system details in different ways, but to be safe we can use the WMI based cmdlet Get-WmiObject, this command is compatible from Windows PowerShell 2.0. Using this command we can query the WMI class Win32_OperatingSystem to get os version number:
(Get-WmiObject Win32_OperatingSystem).Version
The above command only returns the os version number. Run the following command to get the display name of your Windows version.
(Get-WmiObject Win32_OperatingSystem).Caption
Output :
Microsoft Windows 7 Ultimate
We can use select command to get the output of all the required OS related properties.
Get-WmiObject Win32_OperatingSystem |
Select PSComputerName, Caption, OSArchitecture, Version, BuildNumber | FL
We can use the Get-WmiObject cmdlet in short form gwmi.
(gwmi win32_operatingsystem).caption

Get OS version of a remote computer:

We can easily get the OS version details of a remote computer by adding the parameter -ComputerName to Get-WmiObject.
Get-WmiObject Win32_OperatingSystem -ComputerName "Remote_Machine_Name" |
Select PSComputerName, Caption, OSArchitecture, Version, BuildNumber | FL

Get OS details for a list of remote computers using PowerShell:

You can use the following powershell script to find OS version details for multiple remote computers. First create a text file named as computers.txt which includes one computer name in each line. You will get the output of machine name, OS name and version number in the csv file OS_Details.csv.
Get-Content C:\computers.txt  | ForEach-Object{
$os_name = (Get-WmiObject Win32_OperatingSystem -ComputerName $_ ).Caption
if(!$os_name){
$os_name = "The machine is unavailable"
$os_version = "The machine is unavailable"
}
else{
$os_version = (Get-WmiObject Win32_OperatingSystem -ComputerName $_ ).Version 
}
New-Object -TypeName PSObject -Property @{
ComputerName = $_
OSName = $os_name
OSVersion = $os_version 
}} | Select ComputerName,OSName,OSVersion |
Export-Csv C:\OS_Details.csv -NoTypeInformation -Encoding UTF8

Friday, 22 September 2017

Block and Unblock Sign-In status for Office 365 users using PowerShell

Blocking access to an Office 365 account prevents anyone from using the account to sign in and access all the services and data in your Office 365 tenant. We can use the Azure AD powershell cmdlet Set-MsolUser to block user from login into Office 365 service (Ex: Mailbox, Planner, SharePoint, etc).

Block and Unblock an Office user account:

We need to set the user associated property BlockCredential to block user access to Office 365 service.
Set-MsolUser -UserPrincipalName username@domain.com -BlockCredential $true
The following command unblock the blocked user.
Set-MsolUser -UserPrincipalName username@domain.com -BlockCredential $false

Block multiple Office 365 user accounts:

We can use the command Get-MsolUser to fetch set of required Azure AD users with proper filter and then pipe the results to Set-MsolUser cmdlet to block access to every user.
Get-MsolUser -All | Where {$_.Department -eq "Testing"} |
Set-MsolUser -BlockCredential $true

Block bulk user accounts by import CSV file:

We may required to block access to bulk of user accounts, in this case we can have user ids in csv. We need to import csv file, and then pass every user to Set-MsolUser cmdlet. Consider the csv file Block_Users.csv that has users with the column header UserPrincipalName.
Import-Csv 'C:\Block_Users.csv' | ForEach-Object {
$upn = $_."UserPrincipalName"
Set-MsolUser -UserPrincipalName $upn -BlockCredential $true
}

Export blocked user accounts to CSV file:

Run the following command to export all the users that have been blocked to access Office 365 services.
Get-MsolUser -All | Where {$_.BlockCredential -eq $True} |
Select DisplayName,UserPrincipalName, BlockCredential |
Export-CSV "C:\\Blocked_Users.csv" -NoTypeInformation -Encoding UTF8

Hide and Un-hide users from GAL using Powershell

We can use the Exchange Powershell cmdlet Set-Mailbox to hide and un-hide mailbox users from Global Address List (GAL). We need to change the mailbox associated property HiddenFromAddressListsEnabled to hide user from GAL.

Before proceed, run the following command to load Exchange Online Powershell 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

Hide and Un-hide a mailbox user from Global Address Book:

Run the following command to hide a single mailbox user.
Set-Mailbox -Identity username@domain.com -HiddenFromAddressListsEnabled $true
The following command un-hide the given mailbox user from GAL.
Set-Mailbox -Identity username@domain.com -HiddenFromAddressListsEnabled $false

Hide multiple mailbox users from GAL:

We can use the Get-Mailbox cmdlet to fetch set of required mailboxes by applying proper filter and then pipe the results to Set-Mailbox command to hide every mailbox from GAL.
Get-Mailbox -ResultSize Unlimited | Where {$_.Office -eq "Office1"} |
Set-Mailbox -HiddenFromAddressListsEnabled $true

Import mailbox users from CSV and hide from GAL:

We may required to hide bulk mailboxes from Global Address Book, in this case we can store the mailbox user ids in csv file and import csv in powershell using Import-Csv cmdlet and pass every mailbox to Set-Mailbox cmdlet. Consider the CSV file Hide_Mailboxes.csv which contains mailbox users with the column header UserPrincipalName.
Import-Csv 'C:\Hide_Mailboxes.csv' | ForEach-Object {
$upn = $_."UserPrincipalName"
Set-Mailbox -Identity $upn -HiddenFromAddressListsEnabled $true
}

Export hidden mailboxes to CSV file:

We can use the powershell cmdlet Export-csv to export all the hidden mailbox users to csv.
Get-Mailbox -ResultSize Unlimited | Where {$_.HiddenFromAddressListsEnabled -eq $True} |
Select DisplayName,UserPrincipalName, HiddenFromAddressListsEnabled |
Export-CSV "C:\\Hidden_MailBoxes_GAL.csv" -NoTypeInformation -Encoding UTF8

Thursday, 21 September 2017

Find mailboxes hidden from the GAL using Powershell

We can easily get the list of all mailboxes that are currently hidden from Global Address Book using the Exchange Powershell cmdlet Get-Mailbox. The Get-Mailbox cmdlet includes the property HiddenFromAddressListsEnabled and this property indicates whether the mailbox is hidden from GAL or not. So we can query the mailboxes with where filter by checking whether the property HiddenFromAddressListsEnabled is set to true or not.
Get-Mailbox -ResultSize Unlimited | Where {$_.HiddenFromAddressListsEnabled -eq $True}
You can run the following command if you want the output consist of only selected properties :
Get-Mailbox -ResultSize Unlimited | Where {$_.HiddenFromAddressListsEnabled -eq $True} |
Select DisplayName,UserPrincipalName, HiddenFromAddressListsEnabled
We can also export all the hidden mailbox users to csv by simply using Export-csv cmdlet:
Get-Mailbox -ResultSize Unlimited | Where {$_.HiddenFromAddressListsEnabled -eq $True} |
Select DisplayName,UserPrincipalName, HiddenFromAddressListsEnabled |
Export-CSV "C:\\Hidden_MailBoxes_GAL.csv" -NoTypeInformation -Encoding UTF8

Tuesday, 19 September 2017

Set office 365 user's password to never expire using powershell

When you set password expiration policy for your Office 365 organization, it will apply to all Azure AD users. If you have requirements to set some individual user's password to never expire, you need to use Windows Powershell and you can achieve this by using the Azure AD Powershell cmdlet Set-MSOLUser.

Before proceed, connect to your online service by running the following command.
Import-Module MSOnline
$msolCred = Get-Credential
Connect-MsolService –Credential $msolCred

Set an individual user's password to never expire

Run the following command to set an user's password to never expire:
Set-MsolUser -UserPrincipalName <upn of user> -PasswordNeverExpires $true
For example, if the UserPrincipalName of the account is alexd@contoso.com, you can use below command:
Set-MsolUser -UserPrincipalName "alexd@contoso.com" -PasswordNeverExpires $true
You can find whether an user's password is set to never expire or not by running following command:
Get-MSOLUser -UserPrincipalName <upn of user> | Select DisplayName,PasswordNeverExpires

Set multiple users password to never expire

In some situations, we may required to update specific set of user's password to never expire. we can put the required user's upn in csv file and import the csv file and set passwordneverexpires setting. Consider the CSV file office365users.csv which contains users with the column header UserPrincipalName.
Import-Csv 'C:\office365users.csv' | ForEach-Object {
$upn = $_."UserPrincipalName"
Set-MsolUser -UserPrincipalName $upn -PasswordNeverExpires $true;
}

Thursday, 14 September 2017

Find SharePoint List items with unique permissions using powershell

We can easily find and retrieve SharePoint list items which has unique permissions using CSOM in Powershell. In this script, we are going to use GitHub open source library Load-CSOMProperties.ps1 to fetch extra properties (ex: HasUniqueRoleAssignments) in SharePoint CSOM API. You can refer this post : How to load additional CSOM properties in PowerShell for more details.

The following Powershell script get all files (or list items) which has unique (or explicit) permission entries from a given SharePoint Online document library. To use CSOM in Powershell, we need to load the required Microsoft SharePoint Online SDK assembly files.
#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")

C:\Scripts\Load-CSOMProperties.ps1
  
$siteUrl="https://spotenant.sharepoint.com/sites/mysite1"
$UserName = "admin@spotenant.onmicrosoft.com"
$SecPwd = $(ConvertTo-SecureString 'myAdminPwd' -asplaintext -force) 
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials
$ctx.Load($ctx.Web)
$ctx.ExecuteQuery()
$list=$ctx.Web.Lists.GetByTitle("Documents")
$ctx.Load($list)
$ctx.ExecuteQuery()
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml ="<View Scope='RecursiveAll' />";
$allItems=$list.GetItems($camlQuery)
$ctx.Load($allItems)
$ctx.ExecuteQuery()
 
foreach($item in $allItems)
{
Load-CSOMProperties -object $item -propertyNames @("HasUniqueRoleAssignments");
$ctx.ExecuteQuery();
if($item.HasUniqueRoleAssignments -eq $true)
{
Write-Host $item["FileRef"]
Write-Host "##############"
}
}

Friday, 8 September 2017

How to load additional CSOM properties in a PowerShell script

We can use CSOM (client-side object model) in different programming and scripting languages to manage SharePoint On-Premises and SharePoint Online objects, as you know loading additional properties in client side object is very challenging task. In .NET C#, we can easily load specific values using Lambda Expressions with SharePoint CSOM API, but we should have proper knowledge to write lambda expression in Windows PowerShell.

In this post, I am going to share how to use GitHub open source library Load-CSOMProperties.ps1 to fetch extra properties in SharePoint CSOM API. It includes the following steps.
  1. Copy the required Powershell script code from this GitHub location Load-CSOMProperties.ps1 or you also copy the same script code at the end of this post.
  2. Once you copied the script, open Notepad (or Text Document) and paste the copied script, then save the file with .ps1 extension (Ex: Load-CSOMProperties.ps1).
  3. Now you can load the saved Powershell script file in Powershell console by just entering the file path. Ex: C:\Scripts\Load-CSOMProperties.ps1
  4. Once you loaded the Load-CSOMProperties.ps1 file in Powershell console, you can call the function Load-CSOMProperties anywhere in your script to load extra properties for any client side object.

Example 1:

The below powershell script load the additional properties 'Url' and 'Title' of the object $web along with default properties.
C:\Scripts\Load-CSOMProperties.ps1

$web = $ctx.Web
Load-CSOMProperties -object $web -propertyNames @("AllProperties", "Url", "Title")
$ctx.ExecuteQuery()

Example 2:

The below powershell script load the property HasUniqueRoleAssignments in every list item and check if the item has any unique permission entry or not.
C:\Scripts\Load-CSOMProperties.ps1

foreach($listItem in $allItems)
{
Load-CSOMProperties -object $listItem -propertyNames @("HasUniqueRoleAssignments");
$ctx.ExecuteQuery();
if($listItem.HasUniqueRoleAssignments -eq $true)
{
Write-Host $listItem["FileRef"]
}
}

Source of Load-CSOMProperties.ps1:

<#
.Synopsis
    Facilitates the loading of specific properties of a Microsoft.SharePoint.Client.ClientObject object or Microsoft.SharePoint.Client.ClientObjectCollection object.
.DESCRIPTION
    Replicates what you would do with a lambda expression in C#. 
    For example, "ctx.Load(list, l => list.Title, l => list.Id)" becomes
    "Load-CSOMProperties -object $list -propertyNames @('Title', 'Id')".
.EXAMPLE
    Load-CSOMProperties -parentObject $web -collectionObject $web.Fields -propertyNames @("InternalName", "Id") -parentPropertyName "Fields" -executeQuery
    $web.Fields | select InternalName, Id
.EXAMPLE
   Load-CSOMProperties -object $web -propertyNames @("Title", "Url", "AllProperties") -executeQuery
   $web | select Title, Url, AllProperties
#>
function global:Load-CSOMProperties {
    [CmdletBinding(DefaultParameterSetName='ClientObject')]
    param (
        # The Microsoft.SharePoint.Client.ClientObject to populate.
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObject")]
        [Microsoft.SharePoint.Client.ClientObject]
        $object,

        # The Microsoft.SharePoint.Client.ClientObject that contains the collection object.
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0, ParameterSetName = "ClientObjectCollection")]
        [Microsoft.SharePoint.Client.ClientObject]
        $parentObject,

        # The Microsoft.SharePoint.Client.ClientObjectCollection to populate.
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 1, ParameterSetName = "ClientObjectCollection")]
        [Microsoft.SharePoint.Client.ClientObjectCollection]
        $collectionObject,

        # The object properties to populate
        [Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ClientObject")]
        [Parameter(Mandatory = $true, Position = 2, ParameterSetName = "ClientObjectCollection")]
        [string[]]
        $propertyNames,

        # The parent object's property name corresponding to the collection object to retrieve (this is required to build the correct lamda expression).
        [Parameter(Mandatory = $true, Position = 3, ParameterSetName = "ClientObjectCollection")]
        [string]
        $parentPropertyName,

        # If specified, execute the ClientContext.ExecuteQuery() method.
        [Parameter(Mandatory = $false, Position = 4)]
        [switch]
        $executeQuery
    )

    begin { }
    process {
        if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
            $type = $object.GetType()
        } else {
            $type = $collectionObject.GetType() 
            if ($collectionObject -is [Microsoft.SharePoint.Client.ClientObjectCollection]) {
                $type = $collectionObject.GetType().BaseType.GenericTypeArguments[0]
            }
        }

        $exprType = [System.Linq.Expressions.Expression]
        $parameterExprType = [System.Linq.Expressions.ParameterExpression].MakeArrayType()
        $lambdaMethod = $exprType.GetMethods() | ? { $_.Name -eq "Lambda" -and $_.IsGenericMethod -and $_.GetParameters().Length -eq 2 -and $_.GetParameters()[1].ParameterType -eq $parameterExprType }
        $lambdaMethodGeneric = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($type.FullName),System.Object]])"
        $expressions = @()

        foreach ($propertyName in $propertyNames) {
            $param1 = [System.Linq.Expressions.Expression]::Parameter($type, "p")
            try {
                $name1 = [System.Linq.Expressions.Expression]::Property($param1, $propertyName)
            } catch {
                Write-Error "Instance property '$propertyName' is not defined for type $type"
                return
            }
            $body1 = [System.Linq.Expressions.Expression]::Convert($name1, [System.Object])
            $expression1 = $lambdaMethodGeneric.Invoke($null, [System.Object[]] @($body1, [System.Linq.Expressions.ParameterExpression[]] @($param1)))
 
            if ($collectionObject -ne $null) {
                $expression1 = [System.Linq.Expressions.Expression]::Quote($expression1)
            }
            $expressions += @($expression1)
        }


        if ($PsCmdlet.ParameterSetName -eq "ClientObject") {
            $object.Context.Load($object, $expressions)
            if ($executeQuery) { $object.Context.ExecuteQuery() }
        } else {
            $newArrayInitParam1 = Invoke-Expression "[System.Linq.Expressions.Expression``1[System.Func````2[$($type.FullName),System.Object]]]"
            $newArrayInit = [System.Linq.Expressions.Expression]::NewArrayInit($newArrayInitParam1, $expressions)

            $collectionParam = [System.Linq.Expressions.Expression]::Parameter($parentObject.GetType(), "cp")
            $collectionProperty = [System.Linq.Expressions.Expression]::Property($collectionParam, $parentPropertyName)

            $expressionArray = @($collectionProperty, $newArrayInit)
            $includeMethod = [Microsoft.SharePoint.Client.ClientObjectQueryableExtension].GetMethod("Include")
            $includeMethodGeneric = Invoke-Expression "`$includeMethod.MakeGenericMethod([$($type.FullName)])"

            $lambdaMethodGeneric2 = Invoke-Expression "`$lambdaMethod.MakeGenericMethod([System.Func``2[$($parentObject.GetType().FullName),System.Object]])"
            $callMethod = [System.Linq.Expressions.Expression]::Call($null, $includeMethodGeneric, $expressionArray)
            
            $expression2 = $lambdaMethodGeneric2.Invoke($null, @($callMethod, [System.Linq.Expressions.ParameterExpression[]] @($collectionParam)))

            $parentObject.Context.Load($parentObject, $expression2)
            if ($executeQuery) { $parentObject.Context.ExecuteQuery() }
        }
    }
    end { }
}

Thursday, 7 September 2017

Get Item Level Permissions in SharePoint using CSOM

In this post, I am going to write C# code sample to get item level permissions for all list items using CSOM in SharePoint On-Premises/SharePoint Online library. Every list items should have permission entries only if they have unique (or explicit) permissions assigned. If an item or document doesn't have any unique permission entry, then the item's permissions will be derived from its parent library permission.

Retrieve Item Level Permissions For List Items with CSOM

The below CSOM based C# code find all list items for a given SharePoint Online list (or library) and gets the permissions for every items if an item has unique permission.
public static void Get_Item_Level_Permissions_For_All_List_Items()
{
    string sitrUrl = "https://spotenant.sharepoint.com/sites/mysite";
    using (var ctx = new ClientContext(sitrUrl))
    {
        //ctx.Credentials = Your Credentials
        ctx.Load(ctx.Web, a => a.Lists);
        ctx.ExecuteQuery();
        List list = ctx.Web.Lists.GetByTitle("Documents");
        var listItems = list.GetItems(CamlQuery.CreateAllItemsQuery());
        //load all list items with default properties and HasUniqueRoleAssignments property
        ctx.Load(listItems, a => a.IncludeWithDefaultProperties(b => b.HasUniqueRoleAssignments));
        ctx.ExecuteQuery();
        foreach (var item in listItems)
        {
            Console.WriteLine("List item: " + item["FileRef"].ToString());
            if (item.HasUniqueRoleAssignments)
            {
                //load permissions if item has unique permission
                ctx.Load(item, a => a.RoleAssignments.Include(roleAsg => roleAsg.Member.LoginName,
                    roleAsg => roleAsg.RoleDefinitionBindings.Include(roleDef => roleDef.Name,
                    roleDef => roleDef.Description)));
                ctx.ExecuteQuery();
                foreach (var roleAsg in item.RoleAssignments)
                {
                    Console.WriteLine("User/Group: " + roleAsg.Member.LoginName);
                    List<string> roles = new List<string>();
                    foreach (var role in roleAsg.RoleDefinitionBindings)
                    {
                        roles.Add(role.Description);
                    }
                    Console.WriteLine("Permissions: " + string.Join(",", roles.ToArray()));
                    Console.WriteLine("----------------");
                }
            }
            else
            {
                Console.WriteLine("No unique permission found");
            }
            Console.WriteLine("###############");
        }
    }
}
The above code first fetch the list items and then load the role assignments for every items, so it includes multiple server requests, alternatively we can also load the list items and its permissions in single server request call.
List list = ctx.Web.Lists.GetByTitle("Documents");
var listItems = list.GetItems(CamlQuery.CreateAllItemsQuery());

//load all list items with default properties and HasUniqueRoleAssignments property and also
//load permissions of every items 
ctx.Load(listItems, a => a.IncludeWithDefaultProperties(b => b.HasUniqueRoleAssignments),
    permsn => permsn.Include(a => a.RoleAssignments.Include(roleAsg => roleAsg.Member.LoginName,
            roleAsg => roleAsg.RoleDefinitionBindings.Include(roleDef => roleDef.Name,
            roleDef => roleDef.Description))));
ctx.ExecuteQuery();
foreach (var item in listItems)
{
    Console.WriteLine("List item: " + item["FileRef"].ToString());
    if (item.HasUniqueRoleAssignments)
    {
        foreach (var roleAsg in item.RoleAssignments)
        {
            Console.WriteLine("User/Group: " + roleAsg.Member.LoginName);
            List<string> roles = new List<string>();
            foreach (var role in roleAsg.RoleDefinitionBindings)
            {
                roles.Add(role.Description);
            }
            Console.WriteLine("Permissions: " + string.Join(",", roles.ToArray()));
            Console.WriteLine("----------------");
        }
    }
    else
    {
        Console.WriteLine("No unique permission found");
    }
    Console.WriteLine("###############");
}

Wednesday, 30 August 2017

Get all files from a SharePoint Document Library using CSOM

In this post, I am going to write CSOM based C# code to retrieve all files in a SharePoint library using Client-side Object Model. Using this CSOM code you can easily fetch all the files from document library even it has files more than List View Threshold limit.

The List View Threshold defines the maximum limit to retrieve a number of documents in a single request. By default this limit is set to 5000 rows, and in OneDrive for Business (ODFB) this limit is 20000. So any library with more than 5000 files will return an error (Ex: The number of items in this list exceeds the list view threshold, which is 5000 items).

By using CSOM, you can retrieve documents page by page by setting row limit, this will avoid the list view threshold error as we are querying only certain amount of rows in a single request.

Retrieve all documents from a SharePoint library which has more than 5000 items

The following C# code fetch all files from a SharePoint online library. It will get 100 rows in every page. You can change the row limit as per your wish. In CAML query, we are setting the view as <View Scope='RecursiveAll'>, thus gets the documents from root folder and its sub folders by recursively.
public static List<ListItem> GetAllDocumentsInaLibrary()
{
    List<ListItem> items = new List<ListItem>();
    string sitrUrl = "https://spotenant.sharepoint.com/sites/yoursite";
    using (var ctx = new ClientContext(sitrUrl))
    {
        //ctx.Credentials = Your Credentials
        ctx.Load(ctx.Web, a => a.Lists);
        ctx.ExecuteQuery();

        List list = ctx.Web.Lists.GetByTitle("Documents");
        ListItemCollectionPosition position = null;
        // Page Size: 100
        int rowLimit = 100;
        var camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View Scope='RecursiveAll'>
            <Query>
                <OrderBy Override='TRUE'><FieldRef Name='ID'/></OrderBy>
            </Query>
            <ViewFields>
                <FieldRef Name='Title'/><FieldRef Name='Modified' /><FieldRef Name='Editor' />
            </ViewFields>
            <RowLimit Paged='TRUE'>" + rowLimit + "</RowLimit></View>";
        do
        {
            ListItemCollection listItems = null;
            camlQuery.ListItemCollectionPosition = position;
            listItems = list.GetItems(camlQuery);
            ctx.Load(listItems);
            ctx.ExecuteQuery();
            position = listItems.ListItemCollectionPosition;
            items.AddRange(listItems.ToList());
        }
        while (position != null);
    }           
    return items;
}

Tuesday, 29 August 2017

Get all Items from a SharePoint List using CSOM

In this article, I am going to write C# code to find all items in a SharePoint list with Client-side Object Model (CSOM). This code also solves the List View Threshold problem. The List View Threshold defines the maximum limit to retrieve a number of items in a single query call. By default this limit is set to 5000, and in OneDrive for Business this limit is 20000. So any list with more than 5000 items will return an error (Ex: The number of items in this list exceeds the list view threshold, which is 5000 items).

By using CSOM, you can retrieve list items page by page by setting row limit, this will avoid the list view threshold error as we are querying only certain amount of rows in a single query.

Retrieve all items from a SharePoint library which has more than 5000 items

The following C# based CSOM code fetch all items from a SharePoint online library. It will get 50 rows in every page. You can change the row limit as per your wish.
public static List<ListItem> GetAllListItemsInaList1()
{
    List<ListItem> items = new List<ListItem>();
    string sitrUrl = "https://spotenant.sharepoint.com/sites/yoursite";
    using (var ctx = new ClientContext(sitrUrl))
    {
        //ctx.Credentials = Your Credentials
        ctx.Load(ctx.Web, a => a.Lists);
        ctx.ExecuteQuery();

        List list = ctx.Web.Lists.GetByTitle("Documents");
        ListItemCollectionPosition position = null;
        // Page Size: 50
        int rowLimit = 50;
        var camlQuery = new CamlQuery();
        camlQuery.ViewXml = @"<View Scope='RecursiveAll'>
            <Query>
                <OrderBy Override='TRUE'><FieldRef Name='ID'/></OrderBy>
            </Query>
            <ViewFields>
                <FieldRef Name='Title'/><FieldRef Name='Modified' /><FieldRef Name='Editor' />
            </ViewFields>
            <RowLimit Paged='TRUE'>" + rowLimit + "</RowLimit></View>";
        do
        {
            ListItemCollection listItems = null;
            camlQuery.ListItemCollectionPosition = position;
            listItems = list.GetItems(camlQuery);
            ctx.Load(listItems);
            ctx.ExecuteQuery();
            position = listItems.ListItemCollectionPosition;
            items.AddRange(listItems.ToList());
        }
        while (position != null);
    }           
    return items;
}

Monday, 28 August 2017

Get Office 365 Groups in which a User is a Member using Graph Api

Office 365 Groups is developed with collaboration in mind. It becomes a base service for most of existing Office 365 tools that we use already ( like writing documents, planner plans, calendar meetings, teams chat and exchange mail), so finding which unified groups a user is a member of is inevitable in many scenarios.

The Microsoft Graph Api endpoint https://graph.microsoft.com/v1.0/me/memberOf gets and return all the groups where a current user is memberOf. But it returns not only the unified groups, it also lists security groups and dynamic groups. So, you need to apply proper filter to get only Office 365 groups using memberOf endpoint.

Retrieve Office 365 Groups in which current user is a member of

The following API returns all the unified groups in which I am member of.
https://graph.microsoft.com/v1.0/me/memberOf/$/microsoft.graph.group?$filter=groupTypes/any(a:a eq 'unified')

Find Office 365 Groups where an user is a member of

You can also use the same memberOf endpoint to retrieve all the unified groups where a specific user is a member. You need to just replace the /me/ part in the above url with the user's id /users/<user-id>/
https://graph.microsoft.com/v1.0/users/<user-id>/memberOf/$/microsoft.graph.group?$filter=groupTypes/any(a:a eq 'unified')
Note: You can call the memberOf endpoint without Admin Consent after applying unified filter and adding extra parameter (/$/microsoft.graph.group?$filter=groupTypes/any(a:a eq 'unified')) in memberOf endpoint url. If you call without this filter, you will receive the following Access Denied error when you don't have proper admin consent.
"error": {
    "code": "Authorization_RequestDenied",
    "message": "Insufficient privileges to complete the operation.",
}

Tuesday, 22 August 2017

EWS - Create a meeting or appointment in other user calendar.

Using Exchange Web Services (EWS) managed api, we can access and add event to specific user's calendar either by delegate access or impersonation. Both methods are used in different scenario and they require different set of permissions.

A user or account that has been granted impersonation rights will have the same rights as the user whom they are impersonating. Typically, service accounts are given the ability to impersonate the mailbox owner. In that case, the impersonating account has full mailbox rights, just as the mailbox owner does.

With delegate access, the delegate can be granted more granular rights, up to and including full mailbox access. Delegate access can also be configured per folder, or per mailbox. For example, a user can grant the delegate read-only access to the Inbox, read-write access to a calendar folder, and so on. For more info : Impersonation vs Delegate Access

Before proceed, you have to download Microsoft Exchange Web Services Managed API 2.0 dll and add as reference in your C# project.

Create a meeting or appointment as a delegate in C# using EWS

You can use following C# code to create new event in any specific user's calendar. This sample assumes that the delegate user has been granted the appropriate permissions for the mailbox owner's Calendar folder.
ExchangeService exchService = new ExchangeService(ExchangeVersion.Exchange2013_SP1); 
exchService.UseDefaultCredentials = false; 
exchService.Credentials = new NetworkCredential("admin@o365domain.com", "pwd"); 
exchService.Url = new Uri("https://outlook.office365.com/Ews/Exchange.asmx"); 
exchService.PreAuthenticate = false; 
  
Appointment appointment = new Appointment(exchService); 
// Set the properties on the appointment object to create the appointment. 
appointment.Subject = "Sales Meeting"; 
appointment.Body = "Focus on pre-sale and marketing."; 
appointment.Start = DateTime.Now.AddDays(2); 
appointment.End = DateTime.Now.AddDays(2).AddHours(3); 
appointment.Location = "Room 111"; 
appointment.ReminderMinutesBeforeStart = 240; 

appointment.RequiredAttendees.Add("user3@o365domain.onmicrosoft.com"); 

// Save the meeting to the Calendar folder of the mailbox owner and send the meeting request.
// This method call results in a CreateItem call to EWS.
Folder calendar_Folder = Folder.Bind(exchService, new FolderId(WellKnownFolderName.Calendar, "user1@o365domain.com")); 
appointment.Save(calendar_Folder.Id, SendInvitationsMode.SendToNone); 

if (appointment.RequiredAttendees.Count > 0)
{
   // The appointment has attendees so send them the meeting request.
   appointment.Save(calendar_Folder.Id, SendInvitationsMode.SendToAllAndSaveCopy);
}
else
{
    // The appointment does not have attendees, so just save to calendar.
    appointment.Save(calendar_Folder.Id, SendInvitationsMode.SendToNone);
}
Please check this MSDN post : Add appointments by using Exchange impersonation to create appointment by using impersonation.

Impersonation vs Delegate Access in Exchange/Exchange Online

we can access and modify other (or specific) user's mail items, calendar events, and other exchange related objects using either by delegate access or impersonation with Exchange Web Services (EWS) Managed API. Both methods are used in different scenario and they require different set of permissions.

Impersonation is used in scenarios in which a single account needs to access many accounts (ex: service account). An application can be written to display mailbox data such as number of unread items, calendar, and so on. The application can use a dedicated service account to access multiple users’ mailboxes to display their respective data.

Delegate access is used in scenarios in which there needs to be a one-to-one relationship between users. One common application of delegate access is the sharing of calendars between users, such as when an admin manages an executive’s calendar, or a when handful of individuals working on a project need to coordinate calendars.

A user or account that has been granted impersonation rights will have the same rights as the user whom they are impersonating. Typically, service accounts are given the ability to impersonate the mailbox owner. In that case, the impersonating account has full mailbox rights, just as the mailbox owner does.

With delegate access, the delegate can be granted more granular rights, up to and including full mailbox access. Delegate access can also be configured per folder, or per mailbox. For example, a user can grant the delegate read-only access to the Inbox, read-write access to a calendar folder, and so on.

For more details, refer this article : Exchange Impersonation vs. Delegate Access

Monday, 21 August 2017

Create calendar event in Office 365 group using EWS

As Office 365 Group (Unified Group) is robustly becoming the base service for many of Office 365 features (i.e. Planner, MS Teams, etc..) managing planner team meetings and users appointment in group calendar is inevitable now. In this post, I am going to share .net based C# code to create meeting in Office 365 Group using Managed Exchange Web Services Api (EWS).

Before proceed, you have to download Microsoft Exchange Web Services Managed API 2.0 dll and add as reference in your C# project.
ExchangeService exchService = new ExchangeService(ExchangeVersion.Exchange2013_SP1); 
exchService.UseDefaultCredentials = false; 
exchService.Credentials = new NetworkCredential("admin@o365domain.com", "pwd"); 
exchService.Url = new Uri("https://outlook.office365.com/Ews/Exchange.asmx"); 
exchService.PreAuthenticate = false; 
  
Appointment appointment = new Appointment(exchService); 
// Set the properties on the appointment object to create the appointment. 
appointment.Subject = "Sales Meeting"; 
appointment.Body = "Focus on pre-sale and marketing."; 
appointment.Start = DateTime.Now.AddDays(2); 
appointment.End = DateTime.Now.AddDays(2).AddHours(3); 
appointment.Location = "Room 111"; 
appointment.ReminderMinutesBeforeStart = 240; 
appointment.RequiredAttendees.Add("user1@o365domain.onmicrosoft.co"); 
appointment.RequiredAttendees.Add("user2@o365domain.onmicrosoft.co"); 
// Bind the specified group calendar folder. 
Folder calendar_Folder = Folder.Bind(exchService, new FolderId(WellKnownFolderName.Calendar, "salesboard@o365domain.com")); 
appointment.Save(calendar_Folder.Id, SendInvitationsMode.SendToNone);
The parameter SendInvitationsMode specifies how meeting invitations to be send to its attendees. It includes following options:

  • SendToNone : Do not send meeting invitations.
  • SendOnlyToAll : Send meeting invitations to all attendees but do not save a copy of the meeting invitation in the organizer's Sent Items folder.
  • SendToAllAndSaveCopy " Send meeting invitations to all attendees and save a copy of the meeting invitation in the organizer's Sent Items folder.

Note: AFAIK, using Exchange Web Service (EWS) we can create only meeting not appointment in office 365 group.

Wednesday, 12 July 2017

Add or Remove member from a Group using Microsoft Graph .NET Client Library

Managing Group membership of Azure AD Group and Office 365 Group is one of a routine task for every Office 365 Admin. We can easily add or remove user from group using Microsoft Graph api. In this post, I am going to share C# .NET Client Library code to add and remove membership of an user.

Add member to a Group using Microsoft Graph .NET SDK:

private static void AddGroupMember(string groupId, User user) 
{ 
  var client = new GraphServiceClient(new DelegateAuthenticationProvider( 
      (requestMessage) => 
      { 
          requestMessage.Headers.Add("Authorization", "Bearer " + "<Access Token>"); 
          return Task.FromResult(0); 
  
      })); 

  client.Groups[groupId].Members.References.Request().AddAsync(user); 
}
For more info : Add member - Documentation - Microsoft Graph

Remove member from a Group using Microsoft Graph C# Client Library:

private static void RemoveGroupMember(string groupId, string memberId) 
{ 
  var client = new GraphServiceClient(new DelegateAuthenticationProvider( 
      (requestMessage) => 
      { 
          requestMessage.Headers.Add("Authorization", "Bearer " + "<Access Token>"); 
          return Task.FromResult(0); 
  
      })); 

 client.Groups[groupId].Members[memberId].Reference.Request().DeleteAsync(); 
}
For more info: Remove member - Documentation - Microsoft Graph

Thursday, 22 June 2017

List All Checked Out Files Using PowerShell

As a SharePoint administrator, if you are in need to find and get the list of all checked out files from a SharePoint document library, we can easily retrieve it by using client object model (CSOM) in Powershell.

The following Powershell script find and lists all the documents in a SharePoint Online document library which are checkout by current user and all other users.
#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")  
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
  
$siteUrl="https://spotenant.sharepoint.com/sites/testsite"
$UserName = "admin@spotenant.onmicrosoft.com"
$SecPwd = $(ConvertTo-SecureString 'adminPassword' -asplaintext -force) 
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials
$ctx.Load($ctx.Web)
$ctx.ExecuteQuery()
$list=$ctx.Web.Lists.GetByTitle("Documents")
$ctx.Load($list)
$ctx.ExecuteQuery()
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml ="<View Scope='RecursiveAll' />";
$allItems=$list.GetItems($camlQuery)
$ctx.Load($allItems)
$ctx.ExecuteQuery()
 
foreach($item in $allItems)
{

$file = $ctx.Web.GetFileByServerRelativeUrl($item["FileRef"]);
$ctx.Load($file)
$ctx.Load($file.ListItemAllFields)
$CheckedOutByUser=$file.CheckedOutByUser     
$ModifiedBy=$file.ModifiedBy
$ctx.Load($CheckedOutByUser)
$ctx.Load($ModifiedBy)
try
{
$ctx.ExecuteQuery()
if($CheckedOutByUser.LoginName -ne $null){
Write-Host "##############"
Write-Host "File:" $file.Name
Write-Host "Url:" $item["FileRef"]
Write-Host "CheckedOutBy:" $CheckedOutByUser.LoginName
Write-Host "Last Modified By:" $ModifiedBy.LoginName
Write-Host "##############"
Write-Host ""
}}
catch{}
}
In the above powershell script first we are retrieving all the files from library and check every file if it is checked out by user or not, this process may time consuming in large document library, so alternatively we can use filter in caml query itself and retrieve checked out files alone.
#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")  
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
  
$siteUrl="https://spotenant.sharepoint.com/sites/testsite"
$UserName = "admin@spotenant.onmicrosoft.com"
$SecPwd = $(ConvertTo-SecureString 'adminPassword' -asplaintext -force)
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials
$ctx.Load($ctx.Web)
$ctx.ExecuteQuery()
$list=$ctx.Web.Lists.GetByTitle("Documents")
$ctx.Load($list)
$ctx.ExecuteQuery()
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml ="<View Scope='RecursiveAll'><Query><Where><IsNotNull><FieldRef Name='CheckoutUser' /></IsNotNull></Where></Query></View>";
$allItems=$list.GetItems($camlQuery)
$ctx.Load($allItems)
$ctx.ExecuteQuery()
 
foreach($item in $allItems)
{
$file = $ctx.Web.GetFileByServerRelativeUrl($item["FileRef"]);
$ctx.Load($file)
$CheckedOutByUser=$file.CheckedOutByUser     
$ModifiedBy=$file.ModifiedBy
$ctx.Load($CheckedOutByUser)
$ctx.Load($ModifiedBy)
$ctx.ExecuteQuery()

Write-Host "##############"
Write-Host "File:" $file.Name
Write-Host "Url:" $item["FileRef"]
Write-Host "CheckedOutBy:" $CheckedOutByUser.LoginName
Write-Host "Last Modified By:" $ModifiedBy.LoginName
Write-Host "##############"
Write-Host ""
}

Wednesday, 21 June 2017

Get All List Items in Library using PowerShell with CSOM

In this article, I am going write a simple Powershell script using client object model (CSOM) to find and retrieve all files from a document library in SharePoint Online. To use csom in Powershell, we need to load the required Microsoft SharePoint Online SDK assembly files.

The below Powershell script simply load and list all the files from given document library in a SharePoint Online site. You need to replace sharepoint site url, list name and required admin credentials with your own details.
#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")  
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")
 
$siteUrl="https://spotenant.sharepoint.com/sites/testsite"
$UserName = "admin@spotenant.onmicrosoft.com"
$SecPwd = $(ConvertTo-SecureString 'adminPassword' -asplaintext -force) 
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl) 
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName,$SecPwd) 
$ctx.credentials = $credentials
$ctx.Load($ctx.Web)
$ctx.ExecuteQuery()
$list=$ctx.Web.Lists.GetByTitle("Documents")
$ctx.Load($list)
$ctx.ExecuteQuery()
$camlQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$camlQuery.ViewXml ="<View Scope='RecursiveAll' />";
$allItems=$list.GetItems($camlQuery)
$ctx.Load($allItems)
$ctx.ExecuteQuery()

foreach($item in $allItems)
{
Write-Host "##############"
Write-Host $item["FileRef"]
$file = $ctx.Web.GetFileByServerRelativeUrl($item["FileRef"]);
$ctx.Load($file)
$ctx.Load($file.ListItemAllFields)
$Author=$file.Author
$CheckedOutByUser=$file.CheckedOutByUser     
$ModifiedBy=$file.ModifiedBy
$ctx.Load($Author)
$ctx.Load($CheckedOutByUser)
$ctx.Load($ModifiedBy)
try
{
$ctx.ExecuteQuery()
Write-Host "File:" $file.Name 
Write-Host "Author:" $Author.LoginName
Write-Host "ModifiedBy:" $ModifiedBy.LoginName
if($CheckedOutByUser.LoginName -ne $null){
Write-Host "CheckedOutBy:" $CheckedOutByUser.LoginName
}}
catch{}
Write-Host "##############"
Write-Host ""
}
Along with file name and file relative url, the above script also retrieves useful information such as Author name of the file, modified username and if the file was checked out, it also returns the name of the user who currently checked-out the file and yet to checked-in.

Thursday, 15 June 2017

Hide Office 365 Group from GAL using Powershell

Hiding Office 365 Group from Global Address List (GAL) is one of the tedious job as there is no Admin UI to hide group mail from GAL. When you create an office 365 group it will not be hidden from GAL by default either it is public or private group. Currently Office 365 team accepted the user voice request to hide private groups from GAL by default. But for now, Powershell is the only option for Administrators to hide and show the groups from the GAL.

We can use the Exchange Online Powershell cmdlet Set-UnifiedGroup to hide group mail address from GAL. Before proceed, Connect Exchange Online Powershell module and use the following command.
Set-UnifiedGroup <group> -HiddenFromAddressListsEnabled $true
Actually we are setting the attribute HiddenFromAddressListsEnabled as true to hide group mail id from global address list. You can use the following command if you want set this property for all of your Office 365 Groups.
Get-UnifiedGroup | Set-UnifiedGroup -HiddenFromAddressListsEnabled $true
Normally you might want to hide only private groups. You can use below command to hide all the private groups from GAL:
Get-UnifiedGroup | Where-Object {$_.AccessType -eq 'Private'} | Set-UnifiedGroup -HiddenFromAddressListsEnabled $true
We can also list the groups that are disabled from address book using below powershell command:
Get-UnifiedGroup | Where-Object {$_.HiddenFromAddressListsEnabled -eq $true} | Select Alias,HiddenFromAddressListsEnabled
Hiding a Group from the GAL will only removes the availability of group in address list from external users, but it does not prevent other users to send email to the Group if they already know the address. If you want to restrict other users (other than group members) from sending message to the group, you need to set one more property - AcceptMessagesOnlyFromSendersOrMembers.
Set-UnifiedGroup <group> -AcceptMessagesOnlyFromSendersOrMembers <group>
If you want a group to accept messages from more than one group (multiple groups), you can give the group names as comma separated values in the above command.
Set-UnifiedGroup <group> -AcceptMessagesOnlyFromSendersOrMembers <group>,<group2>

Wednesday, 24 May 2017

How to allow external sender for Office 365 Groups using Powershell

Office 365 Group is a back end service for Microsoft Teams, Planner, and etc. By default Office 365 Groups are not configured to receive external messages either it is public or private group. But most of organizations using Teams, Planner and even standalone Office 365 Groups for external collaboration and conversation, so receiving mails from external domain users is inevitable.

We can use the Exchange Online Powershell cmdlet Set-UnifiedGroup to set the people outside the organization to send mail to a specific group. Before proceed, Connect Exchange Online Powershell module and use the following command to allow external sender.
Set-UnifiedGroup <group> -RequireSenderAuthenticationEnabled $false
Actually we need to set the attribute RequireSenderAuthenticationEnabled as false to remove the authentication check of external senders. You can use the below command if you want set this property for all the Office 365 Groups.
Get-UnifiedGroup | Set-UnifiedGroup -RequireSenderAuthenticationEnabled $false
You can use below command if you want allow guest users only for all public groups:
Get-UnifiedGroup | Where-Object {$_.AccessType -eq 'Public'} | Set-UnifiedGroup -RequireSenderAuthenticationEnabled $false
We can list all the groups with external sender access property using below powershell:
Get-UnifiedGroup | Select Alias,AccessType,RequireSenderAuthenticationEnabled
The below command lists only office 365 groups with guest sender access enabled.
Get-UnifiedGroup | Where-Object {$_.RequireSenderAuthenticationEnabled -eq $false} | Select Alias,RequireSenderAuthenticationEnabled
You can also enable via UI using Office 365 Admin center: Office 365 Portal -> Peoples -> Edit Group and set the option "Let people outside the organization email the group"

allow guest sender access for Office 365 Groups

Thursday, 23 March 2017

Enable or Disable In-Place Archive in Exchange Online using Powershell

In Exchange Online, users can have additional mailbox storage space by enabling In-Place Archive. Archive mailboxes also provide an alternate storage location in which to store historical messaging data. You can easily enable or disable In-Place Archive through Exchange Admin Center (EAC), but you should go with Powershell if you want to quickly enable the archive mailbox for all mailboxes in your organization.

Enable In-Place Archive for a mailbox

You can use Enable-Mailbox cmdlet to enable archiving for existing mailbox. You may already used Enable-Mailbox cmdlet to create mailbox for existing users who don't already have mailbox, you can use the same cmdlet to enable In-Place Archive by passing an extra parameter -Archive.

Before proceed, Connect Exchange Online Powershell module and use the following command to enable mailbox archiving.
Enable-Mailbox -Identity <mailbox user id> -Archive
The following command creates an In-Place archive for the existing user AlexD who already has a mailbox.
Enable-Mailbox -Identity AlexD -Archive

Disable In-Place Archive in a mailbox

As like enable archiving, you can use Disable-Mailbox cmdlet to disable archive feature in a mailbox. Use the below command to disable mailbox archive:
Disable-Mailbox -Identity <mailbox user id> -Archive
The below command removes archiving feature from the user AlexD's mailbox.
Disable-Mailbox -Identity AlexD -Archive

Wednesday, 15 February 2017

Read Multiple Users Profile Properties From SharePoint Online Using CSOM

This post is follow-up of the article http://www.morgantechspace.com/2016/09/read-sharepoint-user-profile-properties-csom.html, in previous post I have clearly explained about how to read current user profile properties, specific user (other user) properties and how to read only required profile properties using client object model (CSOM). One of our user asked the question "How to get a specific profile property (path to profile picture for example) for all of my Sharepoint's website users in one request", so I am writing this post to help every users.

Summary

Get All Profile Properties for Multiple SharePoint Online Users

In the below C# code, I have passed only list of SharePoint Online users, you can fetch all SharePoint Online users using your own best method and use it in below code. You can read users using Azure AD powershell cmdlet Get-MsolUser or you can fetch from your own csv file.
public static void GetMultipleUsersProfileProperties()
{
    string siteUrl = "https://spotenant-admin.sharepoint.com";

    var passWord = new SecureString();
    foreach (char c in "pass@word1".ToCharArray()) passWord.AppendChar(c);
    var credentials = new SharePointOnlineCredentials("admin@spotenant.onmicrosoft.com", passWord);
           
    // Connect to the sharepoint site client context.
    ClientContext clientContext = new ClientContext(siteUrl);
    clientContext.Credentials = credentials;

    // Get the PeopleManager object.
    PeopleManager peopleManager = new PeopleManager(clientContext);

    // Get multiple users
    List<string> Users = new List<string> { "admin@spotenant.onmicrosoft.com",
"alexw@spotenant.onmicrosoft.com", "benw@spotenant.onmicrosoft.com" };

    var results = new Dictionary<string, PersonProperties>();
    foreach (var user in Users)
    {
        string loginName = "i:0#.f|membership|" + user;  //claim format login name
        var personProperties = peopleManager.GetPropertiesFor(loginName);
        clientContext.Load(personProperties, p => p.AccountName, p => p.DisplayName,
                           p => p.UserProfileProperties);
        results.Add(loginName, personProperties);
    }
    clientContext.ExecuteQuery();

    foreach (var kvp in results)
    {
        if (kvp.Value.ServerObjectIsNull.HasValue && !kvp.Value.ServerObjectIsNull.Value)
        {
            Console.WriteLine(kvp.Value.DisplayName);
            Console.WriteLine("---------------------------------");
            foreach (var property in kvp.Value.UserProfileProperties)
            {
                Console.WriteLine(string.Format("{0}: {1}",
                    property.Key.ToString(), property.Value.ToString()));
            }                    
        }
        else
        {
            Console.WriteLine("User not found:"+kvp.Key);
        }
        Console.WriteLine("------------------------------");
        Console.WriteLine("          ");
    }
}

Get Specific Profile Properties for Multiple SharePoint Online Users

The below csom based C# code read only specific set of properties for set of SharePoint Online users.
public static void GetSpecificProfilePropertiesForAllUsers()
{
    string siteUrl = "https://spotenant-admin.sharepoint.com";

    var passWord = new SecureString();
    foreach (char c in "pass@word1".ToCharArray()) passWord.AppendChar(c);
    var credentials = new SharePointOnlineCredentials("admin@spotenant.onmicrosoft.com", passWord);

    // Connect to the sharepoint site client context.
    ClientContext clientContext = new ClientContext(siteUrl);
    clientContext.Credentials = credentials;

    // Get the PeopleManager object.
    PeopleManager peopleManager = new PeopleManager(clientContext);

    // Get multiple users - you can provide all users by fetching with different service
    // Ex: from Get-MsolUser powershell cmdlet
    List<string> Users = new List<string> { "admin@spotenant.onmicrosoft.com",
"alex2w@spotenant.onmicrosoft.com", "benw@spotenant.onmicrosoft.com" };

    var results = new Dictionary<string, IEnumerable<string>>();
    foreach (var user in Users)
    {
        string loginName = "i:0#.f|membership|" + user;  //claim format login name
        // Retrieve specific properties by using the GetUserProfilePropertiesFor method.  
        string[] profilePropertyNames = new string[] { "PersonalSpace", "PictureURL", "SPS-JobTitle" };
        UserProfilePropertiesForUser profilePropertiesForUser = new UserProfilePropertiesForUser(
            clientContext, loginName, profilePropertyNames);

        IEnumerable<string> profilePropertyValues = peopleManager.GetUserProfilePropertiesFor(profilePropertiesForUser);

        // Load the request for the set of properties. 
        clientContext.Load(profilePropertiesForUser);
        results.Add(loginName, profilePropertyValues);
    }
    clientContext.ExecuteQuery();

    foreach (var kvp in results)
    {
        if (kvp.Value != null && kvp.Value.Count() > 0)
        {
            Console.WriteLine("User :" + kvp.Key);
            // Returned collection contains only property values 
            foreach (var value in kvp.Value)
            {
                Console.WriteLine(value);
            }
        }
        else
        {
            Console.WriteLine("User not found:" + kvp.Key);
        }
    }
}