Recipient “xxxxx” couldn’t be read from domain controller “xxxxx”. This may be due to replication delays. Switching out of Forest mode should allow this operation to complete successfully.

I got the following error when trying to execute actions against Exchange online using the MSOnline module. Specifically when trying to call Set-UserPhoto. The production site we ran this against uses ADSync so I assumed it was something to do with this, as my testing worked perfectly in my demo environments. We searched google for ages and couldn't figure it out.

After about 2 hours, this had magically fixed itself and the script has run fine ever since. Unsure what happened, but try waiting for a while to see if it resolves. ADSync wasn't new to the organisation so it wasn't as if data hadn't been synced. 

Recipient "xxxxx" couldn't be read from domain controller "xxxxx". This 
may be due to replication delays. Switching out of Forest mode should allow this operation to complete successfully.

Enable content types in a library or in Site Pages with PowerShell

PowerShell to enable content types in a list. I had an issue where "Site page" content type had dissapeared from some older sites and the content types couldn't be changed as this setting was turned off. If you didn't know, you can't turn on content types in the advanced menu of a site pages library, you have to run a script. 

 

Clear-Host
Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
Import-Module 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll'

$url = "SITE"
$username =  "EMAIL"
$password = ConvertTo-SecureString "PASSWORD" -AsPlainText -Force
$SPOCredentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)

$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$context.Credentials = $SPOcredentials
$web = $context.Web
$context.Load($web)
$context.load($web.lists)

$context.executeQuery()

$lists = $web.lists
$list = $lists.GetByTitle("Site Pages") #change list if required

$list.ContentTypesEnabled=$true
$list.Update()
$context.ExecuteQuery()

Write-Host all good 

 

New Site Page option missing from new drop down in Site Pages library

I had an issue where I could no longer create site pages in my site pages library, the option wasn't there although the content type was in the library.

The cause was an admin setting "Allow users to create site pages" which we had turned off when reviewing what users should be allowed to do in Office 365 and SharePoint. 

To fix:

  • Go to Admin
  • SharePoint admin
  • Settings
  • Ensure "Allow users to create site pages" is checked
  • Restart your browser and re-login
  • Option should now be available

Can’t see all options under Web Design Galleries in SharePoint Online, themes, solutions, composed looks missing

This has caught me out twice now, trying to upload my custom theme file. I activate publishing features thinking this will fix it, it doesn't. I then go to the URL for themes "/_catalogs/theme/15/" and upload my SP colour file, i get the error “The documents cannot be uploaded because different permissions are needed. Request the necessary permissions.” 

After googling around, it turns out it's nothing to do with user permissions and you actually need to enable scripting on your site collection, see here. If you change the setting manually it will take 24 hours, but you can use my C# code or PowerShell from this blog post.

Web Designer Galleries before changing setting:

Web Designer Galleries after changing setting:

Missing links:

  • Web parts
  • List templates
  • Themes
  • Solutions
  • Composed looks

Set SharePoint Online user profile mobile number from Azure Active Directory, PowerShell

PowerShell to get values from Azure AD and set them in SharePoint user profiles. Here I am getting all users that are licensed and setting their mobile numbers. This has been run in a production environment and works well. 

Import-Module MSOnline
Clear-Host

# add SharePoint CSOM libraries
Import-Module 'C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll'
Import-Module 'C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll'
Import-Module 'C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.UserProfiles.dll'

# Defaults
$spoAdminUrl = "https://TENANT-admin.sharepoint.com"
$overwriteExistingSPOUPAValue = "True"

# Get credentials of account that is AzureAD Admin and SharePoint Online Admin
$userName = "ADMINEMAIL"
$password = ConvertTo-SecureString "PASSWORD" -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $userName, $password

Try {
    # Connect to AzureAD
    Connect-MsolService -Credential $credential

    # Get credentials for SharePointOnline
    $spoCredentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($credential.GetNetworkCredential().Username, (ConvertTo-SecureString $credential.GetNetworkCredential().Password -AsPlainText -Force))
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($spoAdminUrl)
    $ctx.Credentials = $spoCredentials
    $spoPeopleManager = New-Object Microsoft.SharePoint.Client.UserProfiles.PeopleManager($ctx)

    # Get all AzureAD Users
    $AzureADUsers = Get-MsolUser | Where-Object { $_.isLicensed -eq "TRUE" }
    ForEach($AzureADUser in $AzureADUsers){ 
        $mobilePhone = $AzureADUser.MobilePhone
        $targetUPN = $AzureADUser.UserPrincipalName.ToString()
        $targetSPOUserAccount = ("i:0#.f|membership|" + $targetUPN)

        # Check to see if the AzureAD User has a MobilePhone specified
        if (!([string]::IsNullOrEmpty($mobilePhone))) {
            $targetspoUserAccount = ("i:0#.f|membership|" + $AzureADUser.UserPrincipalName.ToString())
            $spoPeopleManager.SetSingleValueProfileProperty($targetspoUserAccount, "CellPhone", $mobilePhone)
            
            Try {
                $ctx.ExecuteQuery()
                Write-Output "User: $targetUPN has had their mobile number set to: $mobilePhone"
            }
            Catch {
                [Exception]
                Write-Output "User: $targetUPN with mobile: $mobilePhone, failed to be set, see exception below"
                Write-Output $_.Exception.Message
            }
        }
        else {
            # AzureAD User MobilePhone is empty, nothing to do here
            Write-Output "AzureAD MobilePhone Property is Null or Empty for $targetUPN)"
        }
    }
}
Catch {
    [Exception]
    Write-Output $_.Exception.Message
}

 

SharePoint Online, PowerShell, Get all sites in a site collection and document libraries/lists

Ever wanted a PowerShell script to loop through all of your site collection's webs and then grab all of the document libraries and lists? Try this script out. Big thanks to this sharepointfire.com article, I used this code but altered it to use one site collection, write to the file at the very end (was running too fast and getting errors due to the file being locked), have an exclusion list and other bits n pieces. Code has been tested against a real environment.

Make sure you install the SharePoint Online Management Shell and the SharePoint Online Client Components SDK.

Here is a screenshot of the output. Has item counts for each library. Outputs file in same folder as your .ps1 script.

function connectToO365{ 
    $adminUrl = "https://TENANT-admin.sharepoint.com"
    $url = "SITECOLLECTION"
    $userName = "ADMIN"
    $password = ConvertTo-SecureString "PASSWORD" -AsPlainText -Force
 
    Import-Module 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll'
    $credentials = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $userName, $password
    $SPOCredentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($userName, $password)
 
    try{
        Connect-SPOService -Url $adminUrl -Credential $credentials
        write-host "Info: Connected succesfully to Office 365" -foregroundcolor green
    }
    catch{
        write-host "Error: Could not connect to Office 365" -foregroundcolor red
        Break connectToO365
    }
 
    #create HTML file
    $filePath = create-outputfile

    #add start HTML information
    $global:textToWrite += "
    <html>
    <body>
    <h1>Site structure</h1>
    <table border='0' style='font-family: Calibri, sans-serif; padding: 5px;'>
        <tr>
            <th style='background-color:#454545; color:white'>URL</th>
            <th style='background-color:#454545; color:white'>Type</th>
            <th style='background-color:#454545; color:white'>Template</th>
            <th style='background-color:#454545; color:white'>Item Count</th>
        </tr>"
    
    $global:textToWrite += "<tr style='background-color:#95B9C7'><td>$($url)</td><td>Site Collection</td><td></td><td></td></tr>"
    write-host "Info: Found $($url)" -foregroundcolor green
    
    $AllWebs = Get-SPOWebs($url)

    $global:textToWrite += "
    </body>
    </html>"

    #finally, write everything to file
    add-content -value $textToWrite -path $filePath
}
 
function create-outputfile(){ 
    $date = get-date -format dMMyyyyhhmm
    $filePath = "$($PSScriptRoot)\Output$($date).html"
 
    if (!(Test-Path -path $filePath)){
        #create file
        New-Item $filePath -type file | out-null 
        write-host “File created: $($filePath)” -foregroundcolor green
    }
    else{
        write-host "Output file already exists, wait 1 minute" -foregroundcolor yellow
        Break create-outputfile
    }
 
    return $filePath
}
 
function Get-SPOWebs($url){ 
    $context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
    $context.Credentials = $SPOcredentials
    $web = $context.Web
    $context.Load($web)
    $context.Load($web.Webs)
    $context.load($web.lists)
 
    try{
        $context.ExecuteQuery()
        foreach($list in $web.lists){
            if ($excludeLists -notcontains $list.Title)
            {
                write-host "List included " $list.Title -foregroundcolor green
                $global:textToWrite += "<tr><td><span style='margin-left:$($pixelslist)px'>$($list.title)</td><td></td><td></td><td>$($list.itemcount)</td></tr>"
            }
            else
            {
                write-host "List excluded " $list.Title -foregroundcolor blue
            }
        }
 
        $pixelsweb = $pixelsweb + 15
        $pixelslist = $pixelslist + 15
 
        foreach($web in $web.Webs) {
            $global:textToWrite += "<tr style='background-color:#999999'><td><span style='margin-left:$($pixelsweb)px'>$($web.url)</td><td>Web</td><td>$($web.webtemplate)</td><td></td></tr>"
            write-host "Info: Found $($web.url)" -foregroundcolor green
            Get-SPOWebs($web.url)
        }
    }
    catch{
        write-host "Could not find web" -foregroundcolor red
    }
}

Clear-Host
write-host "Kicking Off" -foregroundcolor green

$excludeLists = @("Master Page Gallery",
                "User Information List",
                "TaxonomyHiddenList",
                "Theme Gallery",
                "Style Library",
                "Solution Gallery",
                "Reusable Content",
                "Reporting Templates",
                "Reporting Metadata",
                "Long Running Operation Status",
                "List Template Gallery",
                "Content and Structure Reports",
                "Cache Profiles",
                "wfpub",
                "Workflows",
                "Workflow History",
                "Web Part Gallery",
                "Form Templates",
                "Workflow Tasks",
                "Suggested Content Browser Locations",
                "Site Template Gallery",
                "fpdatasources",
                "Variation Labels",
                "Content type publishing error log",
                "Converted Forms"
                )

# create variable to write to, later add to file
$textToWrite = ''

#go do all the things
connectToO365

Write-Host "Completed!" -foregroundcolor Yellow

 

Find out what Script Links (custom actions) have been added to a site collection in Office 365 / SharePoint Online

A script link is added via a Custom Action (see this post) to a site collection, site, library etc.

I wanted to migrate a site collection in Office 365 with Sharegate, but couldn't find out whether this would include custom actions (our script links).

After the migration I ran the following PowerShell script to find out what was deployed (code from here). 

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")

$siteUrl = "site"
$username = "admin user"
$password = "password"
$SecurePassword = $password | ConvertTo-SecureString -AsPlainText -Force
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

# SharePoint Online
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $SecurePassword)
$ctx.Credentials = $credentials

$rootWeb = $ctx.Site
$ctx.Load($rootWeb)
$ctx.ExecuteQuery()
$caColl = $rootWeb.get_userCustomActions()
$ctx.Load($caColl)
$ctx.ExecuteQuery()

Write-Host
Write-Host 'Total number of custom actions: '$caColl.Count
$count = 1
$caColl | ForEach-Object {
Write-Host $count')'  $_.Name
$count++
}

As it turns out, Sharegate didn't migrate the custom actions. I emailed the support team and they have confirmed this isn't going to be developed any time soon. For now we can keep deploying it with our old console app. 

What Office 365 applications use the custom Theme you set?

This article from Microsoft explains how to change your Office 365 theme, but doesn't discuss what applications the theme is actually used in.

I customised my theme in a demo site and had a look through the apps.

The following displayed my new custom theme: SharePoint Online, Outlook Online, Admin Centre, OneDrive for Business, Yammer, Planner, Video, Security & Compliance.

Note that this won't be relevant for long as Microsoft are likely planning to make the navigation more global. The app launcher isn't available in all of the applications, but there is a number where it is but doesn't pick up the theme. Hopefully this will change and make for a more consistent user experience. The theme doesn't appear to update in Outlook either which is a real shame. 

Copy new Email and Details From Outlook 365 to SharePoint Online Document Library, Logic Apps: Mail Enable Office 365 SharePoint Online

Parent Post – Requirements and InformationCopy new Email and Details From Outlook 365 to SharePoint Online Document Library, Microsoft Flow and Logic Apps: Mail Enable Office 365 SharePoint Online

Related Post – Microsoft FlowCopy new Email and Details From Outlook 365 to SharePoint Online Document Library, Microsoft Flow: Mail Enable Office 365 SharePoint Online

How to create a Logic App to pass an email from Outlook 365 to a SharePoint Online Document Library

  1. Go to your SharePoint Online site
  2. Create a document library to receive the emails
  3. Go to portal.azure.com and login as the account you use to manage Azure
  4. Click New + at the top left
  5. Type in "Logic App", click "Logic App" in the Results
  6. Click "Create", enter the details for your Logic App
  7. Click Create
  8. Wait till the deployment is completed
  9. Navigate to your Logic App – you can do this by finding it under "All Resources" or clicking the notification message once deployment is completed
  10. Click on "Blank Logic App" under Templates
  11. Click "Office 365 Outlook" under connectors
  12. Click "Office 365 Outlook (When a new email arrives)"
  13. Click Sign in, log in with the email account you will be receiving emails to
  14. I've left most stuff as default, feel free to add any extra required filters. For the frequency, every time the "check for items" is run, you will be charged money. Doing it once an hour works out pretty cheap. Check out https://azure.microsoft.com/en-us/pricing/details/logic-apps/ for details on calculating costs. I have left 3 minutes for development. MAKE SURE you set "Include Attachments" to yes, or the attachments part will error
  15. Click "+ New step" and "Add an action"
  16. Type in "Content Conversion" and click on "Content Converstion – Html to text" – NOTE: we are adding this step as generally emails are in HTML and this shows as HTML markup when you add the Body of the email into a .txt file in SharePoint. Converting to plain text means we can add just the text and not HTML markup from the Body
  17. In the "Content" field, enter "Body" by double clicking the value "Body" under "When a new email arrives -> Body" as seen below
  18. Click "+ New step" and "Add an action"
  19. Search for SharePoint and click "SharePoint – Create file"
  20. Sign into SharePoint with the account that you will use to upload the emails. Don't forget about accounts with expiring passwords, you will need to come update the app when the password expires
  21. Enter the Site Address for your site collection or subsite, then select the Folder Path as your document library
  22. For the File Name we will enter the From field "_" and the utcNow() function. This will make the file name easy to identify by who it is from but also unique based on the utc date and time. You will find all of the email fields under "Dynamic content" and functions such as utcNow() under "Expression". Scroll down to "Date and time" section, double click on utcNow() which will enter it into the expression field, then click OK
  23. Ensure you type in ".txt" at the end of the file name. The type of file is not decided for you and .docx does not work (throws an error that it can't open the document)
  24. For the "File Content" field you can enter a mixture of dynamic and static content. I have entered a number of the email fields due to the fact that I can't assign these to metadata. Note that "Dynamic content" pop up box groups available information by the action it comes from, e.g. below we have "Html to text" and "When a new email arrives". You'll also notice there is a "See more" button which will show more hidden fields. You can also just search dynamic content and it will filter the list so you can quickly find values
  25. Below you can see what I have chosen for the File Content. Notice that I have used "The plan text content." from the "Html to text" group instead of the Body field
  26. Now we need to add any attachments from the email. Click "New Step" -> "Add an Action"
  27. Search for SharePoint and click "SharePoint – Create file". This will automatically log you into the same account used earlier for the first SharePoint step
  28. Enter the site URL and the document library. Under file name, click "Name (Attachment name)"
  29. This will convert your create file action into an "For each" action as it will need to loop through each attachment
  30. Let's make the "File Name" more useful, add the From field, "_ATT_" (to signify it's an attachment), utcNow() and Name field. When the item is added to the document library it should now look similar to the main email and group better, especially if you set your default view to sort by created date descending and then by File Name, you will be able to tell quickly that the email has attachments associated with it
  31. For the "File Content" field, enter "Content (Attachment content)"
  32. Your final attachment loop should look like the above
  33. Save your Logic App, you're now ready to test it!
  34. Click Run to start watching for emails (it will check on your trigger time set at the start, mine is 3 minutes)
  35. Create an email and add a couple of attachments. Send this to the email address you setup for the email trigger to watch
  36. It will take a few moments for the email to get picked up.
  37. Once it has successfully run, I can see my email plus two attachments in my document library
  38. If we open and view the txt file

Copy new Email and Details From Outlook 365 to SharePoint Online Document Library, Microsoft Flow: Mail Enable Office 365 SharePoint Online

Parent Post – Requirements and InformationCopy new Email and Details From Outlook 365 to SharePoint Online Document Library, Microsoft Flow and Logic Apps: Mail Enable Office 365 SharePoint Online

Related Post – Logic AppsCopy new Email and Details From Outlook 365 to SharePoint Online Document Library, Logic Apps: Mail Enable Office 365 SharePoint Online

How to create a Microsoft Flow to pass an email from Outlook 365 to a SharePoint Online Document Library

  1. Go to your SharePoint Online site
  2. Create a document library to receive the emails
  3. In your document library, create a flow (in the modern UI click Flow -> Create a Flow, click "See more templates", click "create a flow from blank"). If you are using classic view, you can simply go to https://us.flow.microsoft.com/manage, login and under My Flows click "Create from blank"
  4. In your new flow, the below should be displayed. Click "Office 365 Outlook"
  5. Click "Office 365 Outlook – When a new email arrives" trigger
  6. You will need to choose which email address you want the trigger to watch, mine has automatically used the email address I am signed in as. To change this click "Add new connection" and then make sure is it ticked under "My connections". MAKE SURE you set "Include Attachments" to yes, or the attachments part will error
  7. I have left all of the other options default, but you can add other filters for the trigger to look for such as a subject keyword
  8. Click "New Step" -> "Add an Action"
  9. Type in "Content Conversion" and click on "Content Converstion – Html to text" – NOTE: we are adding this step as generally emails are in HTML and this shows as HTML markup when you add the Body of the email into a .txt file in SharePoint. Converting to plain text means we can add just the text and not HTML markup from the Body
  10. In the "Content" field, enter "Body" by double clicking the value "Body" under "When a new email arrives -> Body" as seen below
  11. Click "New Step" -> "Add an Action"
  12. Click on SharePoint
  13. Click "SharePoint – Create file"
  14. Enter the Site Address for your site collection or subsite, then select the Folder Path as your document library
  15. NOTE: as with Outlook, SharePoint also has "My connections" on the "…" menu, make sure you use the user you want to add the items to SharePoint. I have used the user I am logged in as
  16. For the File Name we will enter the From field "_" and the utcNow() function. This will make the file name easy to identify by who it is from but also unique based on the utc date and time. You will find all of the email fields under "Dynamic content" and functions such as utcNow() under "Expression". Scroll down to "Date and time" section, double click on utcNow() which will enter it into the expression field, then click OK
  17. Ensure you type in ".txt" at the end of the file name. The type of file is not decided for you and .docx does not work (throws an error that it can't open the document)
  18. For the "File Content" field you can enter a mixture of dynamic and static content. I have entered a number of the email fields due to the fact that I can't assign these to metadata. Note that "Dynamic content" pop up box groups available information by the action it comes from, e.g. below we have "Html to text" and "When a new email arrives". You'll also notice there is a "See more" button which will show more hidden fields. You can also just search dynamic content and it will filter the list so you can quickly find values
  19. Below you can see what I have chosen for the File Content. Notice that I have used "The plan text content." from the "Html to text" group instead of the Body field
  20. Now we need to add any attachments from the email. Click "New Step" -> "Add an Action"
  21. Click "SharePoint"
  22. Click "SharePoint – Create File" action
  23. Enter the site URL and the document library. Under file name, click "Name (Attachment name)"
  24. This will convert your create file action into an "Apply to each" action as it will need to loop through each attachment
  25. Let's make the "File Name" more useful, add the From field, "_ATT_" (to signify it's an attachment), utcNow() and Name field. When the item is added to the document library it should now look similar to the main email and group better, especially if you set your default view to sort by created date descending and then by File Name, you will be able to tell quickly that the email has attachments associated with it
  26. For the "File Content" field, enter "Content (Attachment content)"
  27. Your final attachment loop should look like the above
  28. Update your flow, you're now ready to test it!
  29. Create an email and add a couple of attachments. Send this to the email address you setup for the email trigger to watch
  30. It will take a few moments for the email to get picked up. You can view the run history and other details on your flows main screen, you should be taken here once you update your Flow and click "Done"
  31. Once it has successfully run, I can see my email plus two attachments in my document library
  32. If we open and view the txt file