PowerShell Script to Create Office 365 Security and Compliance Center eDiscovery Case and Holds

This week my customer and a peer both asked for a sample PowerShell script to automate the creation of an Office 365 Security and Compliance Center eDisovery case, hold, and content search.  This post will share out that script and a few things to be aware of (ex. deprecating basic authentication) that are important.


The below script accomplishes the following tasks:

  • Create a Security and Compliance Center eDiscovery case
  • Place an in-place hold on multiple users’ Exchange Online mailboxes
  • Create a content search within eDiscovery case for any folders named “Legal Hold” and the child folders under them

Important Note

As of the publish date (Mar 4th, 2020) the Security and Compliance Center remote PowerShell module relies on basic authentication.  The Exchange team has publicly shared that basic authentication for Exchange Online will be deprecated by Oct 2020.  As such that means the below script may not be usable in its current form in ~6 months.  When a replacement or update is available I will attempt to update this sample to reflect that.

Exchange Online deprecating Basic Authentication

In terms of the Exchange Online remote PowerShell module there is a v2 module being developed (active development, not ready for production) which you can find on the PowerShell Gallery.  This new module support OAuth authentication which resolves the issue of deprecated basic authentication.

ExchangeOnlineManagement module on PSGallery


Before running this script, ensure that the account you log in with has the appropriate permissions to both Exchange Online as well as Security and Compliance Center.  My sample uses a single admin account but you may adapt the script to use separate accounts if needed.  See the following articles for more details.

Connect to Exchange Online PowerShell

Connect to Office 365 Security & Compliance Center PowerShell

Note: If you do not see the below Gist please refer to code at this location: EXO_New-SCCeDiscoveryCaseAndHold.ps1

Set-StrictMode Version "Latest"
# eDiscovery case creation
$caseName = 'Smith v. Johnson';
$UPN = 'user1@contoso.onmicrosoft.com', 'user2@contoso.onmicrosoft.com'
$description = "$caseName"
$policyName = "$caseName – Hold Policy"
$ruleName = "$caseName – Hold Rule"
$searchName = "$caseName – Search Name"
$rootFolderNameQuery = "Legal Hold"
function GetFolderQueries {
param (
$folderQueries = @()
foreach($user in $UPN)
$rootFolderStats = Get-MailboxFolderStatistics Identity $user | Where-Object name -eq $rootFolderNameQuery
$childFolderStats = Get-MailboxFolderStatistics Identity $user | Where-Object FolderPath -like "$($rootFolderStats.FolderPath)*"
# sample script to convert folderId: https://docs.microsoft.com/en-us/microsoft-365/compliance/use-content-search-for-targeted-collections?view=o365-worldwide#step-1-run-the-script-to-get-a-list-of-folders-for-a-mailbox-or-site
foreach ($folderStatistic in $childFolderStats)
$folderId = $folderStatistic.FolderId;
$folderPath = $folderStatistic.FolderPath;
$encoding= [System.Text.Encoding]::GetEncoding("us-ascii")
$nibbler= $encoding.GetBytes("0123456789ABCDEF");
$folderIdBytes = [Convert]::FromBase64String($folderId);
$indexIdBytes = New-Object byte[] 48;
$folderIdBytes | Select-Object skip 23 First 24 | %{$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -shr 4];$indexIdBytes[$indexIdIdx++]=$nibbler[$_ -band 0xF]}
$folderQuery = "folderid:$($encoding.GetString($indexIdBytes))";
$folderStat = New-Object PSObject
Add-Member InputObject $folderStat MemberType NoteProperty Name UPN Value $user
Add-Member InputObject $folderStat MemberType NoteProperty Name FolderPath Value $folderPath
Add-Member InputObject $folderStat MemberType NoteProperty Name FolderQuery Value $folderQuery
$folderQueries += $folderStat
return $folderQueries
# Connection to EXO and SCC PowerShell Modules
$UserCredential = Get-Credential
$Session = New-PSSession ConfigurationName Microsoft.Exchange ConnectionUri https://outlook.office365.com/powershellliveid/ Credential $UserCredential Authentication Basic AllowRedirection
Import-PSSession $Session AllowClobber
$SccSession = New-PSSession ConfigurationName Microsoft.Exchange ConnectionUri https://ps.compliance.protection.outlook.com/powershellliveid Credential $UserCredential Authentication Basic AllowRedirection
Import-PSSession $SccSession AllowClobber DisableNameChecking
# Create eDiscovery case, hold, and compliance search
New-ComplianceCase Name $caseName Description $description
New-CaseHoldPolicy Name $policyName Case $caseName ExchangeLocation $UPN Enabled $true
New-CaseHoldRule Name $ruleName Policy $policyName Disabled $false
$folderQueries = GetFolderQueries rootFolderNameQuery $rootFolderNameQuery UPN $UPN
New-ComplianceSearch Name $searchName Case $caseName HoldNames "All" ContentMatchQuery $folderQueries.FolderQuery


In this post I shared a sample script for automating the creation of an Office 365 Security and Compliance Center eDiscovery case, hold, and folder scoped content search.  The folder scoping was an interesting detour as I had to track down the way to gather folder IDs from a product group engineer sample (linked in the above sample).  I hope you find this useful and good luck scripting.

-Frog Out

Migrating from PowerShell ISE to Visual Studio Code

In this blog post I’ll share a number of resources for how to move from the PowerShell ISE to Visual Studio Code for writing and executing PowerShell scripts.

See the source image


Much of the content from this post is sourced from two other excellent resources:

In my working with various customers many times we are working on PowerShell scripts together.  I’ve noticed that many are still using the PowerShell ISE as their primary host for developing and executing PowerShell snippets or scripts.  Back in May 2017 the PowerShell team announced the shift of focus from PowerShell ISE to Visual Studio Code with the Announcing PowerShell for Visual Studio Code 1.0! blog post.  Combining the move to PowerShell Core (v6) (based on .Net Core which is cross-platform) with the limitation that the PowerShell ISE is Windows-only meant a change in direction was needed.

In the following sections I’ll overview a number of steps for getting up and running with PowerShell in Visual Studio Code.

Visual Studio Code

The first step is to install Visual Studio Code.  If you’ve feared it because it has the words “Visual Studio” in the name don’t worry.  Visual Studio Code is really a text editor (think Notepad) with really powerful extensions that you can add.  I recommend the “user install” vs. “system install” as the former doesn’t require Administrator rights.

PowerShell extension

The next step is to install the PowerShell extension for Visual Studio Code.  This extension provides syntax highlighting, intellisense, running scripts with F8, and more.

One important change to the Visual Studio Code setting that I found especially helpful is keeping the focus on the editor window (not the console) when executing scripts.  Update the following setting to configure this.

"powershell.integratedConsole.focusConsoleOnExecute": false

Shell Launcher extension

If you have a need to regularly switch between multiple shells (PowerShell v5 / v6 / v7-preview, Git Bash, Windows Subsystem for Linux (WSL) Bash, CMD, etc.) like I do the Shell launcher extension is helpful.  Be sure to follow the instructions for setting a keybinding for switching shells (suggested “Ctrl+Shift+T” on Windows).  In my case this keybinding was already used by another extension but I was okay with overwriting that.

Keybinding for Shell Launcher to launch a shell

The following are the settings I use for the multiple shells:

"shellLauncher.shells.windows": [
            "shell""C:\\Program Files\\PowerShell\\6\\pwsh.exe",
            "label""Pwsh Core"
            "shell""C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
            "label""PSv7 (preview)"
        },        {
            "label""WSL zsh"
            "label""PowerShell (v5)"
            "shell""C:\\Program Files\\Git\\bin\\bash.exe",
            "label""Git bash"
            "label""WSL Bash"


Bonus – ZSH and auto-suggest

I’ll be the first to admit that I don’t use Windows Subsystem for Linux (WSL) often.  That said there are times I find it helpful to run the Azure CLI on a WSL shell though.  An example is when I’m doing demos or presentations and I want to showcase the auto-suggest feature in Zsh (short for “Z shell”).  Not only does the auto-suggest help me remember what commands to type but it lets the audience follow along more easily.  See the following links for installing and configuring.


Example Zsh auto-suggest with Azure CLI


In this article I walked through a number of useful extensions or configurations to help migrate from the Windows-only PowerShell ISE to the cross-platform Visual Studio Code.  If you have additional suggestions or tips please share them in the comments.

-Frog Out

How To Add PowerShell v7 Preview to the New Windows Terminal

In this quick post I’ll show you how to add PowerShell v7 stable and preview releases to the new Windows Terminal.

<Update 2020-07-20>Originally this post was written for PowerShell v7.0.0-preview.1 before it was generally available. Since that time PowerShell v7 now has stable releases but continues with preview releases as well. I’m updating this post to reflect how you can add stable and preview releases side-by-side in Windows Terminal.</Update>


The new Windows Terminal was announced at Build this year and has been available (building from source code yourself) through the Microsoft/Terminal GitHub repo.  A very early preview of the Windows Terminal is now available through the Microsoft Store (link).  I haven’t verified but I believe you’ll need a few prerequisites in order to install.  Out of the box the current preview of Windows Terminal supports a number of shells including:

  • PowerShell (v6, also known as “PowerShell Core”, and v7)
  • Windows PowerShell (v5)
  • CMD (command prompt)
  • WSL (Windows Sub-system for Linux)
  • Azure Cloud Shell
  • and more…

Dynamic Profiles

By default, Windows Terminal now uses a feature called “dynamic profiles” to automatically generate profiles for any of the following shells on your machine:

  • PowerShell
  • WSL
  • Azure Cloud Shell

At any time, you can remove (or rename if you want to keep a copy) the settings.json file used by Windows Terminal. This will force auto-generation of a new settings.json file so that you can see which shells it finds. Read more in the support article on this topic.

Dynamic profiles in Windows Terminal


After installing the Windows Terminal from the Microsoft Store, open the Settings from the dropdown menu (or click “Ctrl + ,”).

Find the “profiles” element in the settings file and add the following JSON snippet to the array.  Verify the “commandline” and “icon” locations match where PowerShell v7 preview is installed on your machine.

    "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
    "name": "PowerShell 7",
    "fontFace" : "Cascadia Code PL",
    "source": "Windows.Terminal.PowershellCore",
    "hidden": false
    "commandline" : "C:\\Program Files\\PowerShell\\7-preview\\pwsh.exe",
    "fontFace" : "Cascadia Code PL",
    "guid" : "{90cbdc15-f4fe-49d2-a245-ec066b70845f}",
    "icon" : "C:\\Program Files\\PowerShell\\7-preview\\assets\\Powershell_av_colors.ico",
    "name" : "PowerShell 7 preview",
    "startingDirectory" : "%USERPROFILE%",
    "hidden": false

Here is a link to my complete profiles.json for comparison.

After you’ve added the above element you’ll now see PowerShell v7 preview in your dropdown list of available shells with the proper icon.

Below screenshot shows PowerShell v7 Preview in use and verifying the PSVersion.

Bonus – Zsh and Oh My Zsh

For an added bonus I also added Zsh (with the Oh My Zsh framework for auto-suggestions and more) based on seeing demos from Jeff Hollan showcasing that shell.

Bonus – Cascadia Code PL

You may have noticed from the samples above that my shell profiles are using fontFace of “Cascadia Code PL”. Cascadia Code is a new font from Microsoft that was developed alongside Windows Terminal. The “PL” (Powerline) variant includes ligatures that take combinations of characters and convert them into glyphs. This is best demonstrated when you view the following documentation to see a sample. Note that there is some additional work (link) to configure this on your machine.

Cascadia Code


I’m very excited to begin using the Windows Terminal more in my daily tasks.  Being able to switch back and forth between multiple shells (specifically PowerShell v5 and v6/v7) is possible in Visual Studio Code but this will be a much easier solution for many of my scenarios.  Hopefully someone else will find this helpful as well.

-Frog Out

PowerShell Script to Find Connectors from PowerApps Apps

In this post I’ll share a script I developed for a customer to find which connectors are used by which PowerApps apps.  Currently this is not something available through the Power Platform Admin Center.  Feel free to use this script as you see fit.  This script is provided as-is without any warranties of any kind.  If you update or adapt it and decide to re-post please provide attribution.


Note: If you do not see the below Gist please refer to code at this location: PS-Get_PowerApps_App_Connections.ps1

$environments = Get-PowerAppEnvironment
foreach($environ in $environments.EnvironmentName)
$apps = Get-AdminPowerApp EnvironmentName $environ
$apps | Add-Member MemberType ScriptProperty Name Connections Value {$this.internal.properties.connectionReferences.PSObject.Properties.Value.DisplayName} Force
$apps | Select-Object AppName, DisplayName, Connections

Sample output


You may notice that the output contains a complex property for the Connections.  It has been some time since I worked with formatting output in PowerShell.  If you have an improvement to the formatting please share back suggestion and I’ll update the sample script.

-Frog Out

Wrap Up from Microsoft Ignite 2015

   This was the first year of the Microsoft Ignite conference which merged a number of previous conferences including TechEd, SharePoint Conference, Project Conference, and more.  With over 23,000 attendees, a new venue, and numerous Microsoft senior leadership and product group in attendance (including CEO Satya Nadella himself) this was definitely a huge event.  Rather than re-capping the event itself I wanted to take a minute to mention a few items that I heard / saw at the conference.  I am still downloading and viewing a number of sessions that I couldn’t attend (same time as another session or room was at capacity) but these are highlights that I wanted to share with others.



  • No “internal” FIM in SharePoint 2016 – SharePoint 2016 will not ship with a version of the Forefront Identity Manager product included.  This is a fairly big deal for any customers that are using the “SharePoint Synchronization” option (allows for import and export of content to / from SharePoint) for the User Profile Sync in 2010 or 2013.  Your options in 2016 will be the Active Directory Import (same as 2007 and re-introduced in 2013) or “external” FIM which is installed and managed outside of SharePoint Server.  See the following resources for more details and how to install FIM 2010 R2 + SP1 with SharePoint 2013 so that you can start planning today if you do need the full features of syncing data into and out of SharePoint.

What’s New for IT Professionals in SharePoint Server 2016 (session recording with announcement)

Configuring SharePoint 2013 for the Forefront Identity Manager 2010 R2 Service Pack 1 Portal (install overview)

  • Project Siena – Project Siena looks like a viable alternative (not replacement) for many (smaller) custom development scenarios.  Essentially it is an app that lets you build other apps.  I do not see this replacing InfoPath, Lightswitch, and half a dozen other technologies that have popped up over the past few years but I do see a promising future for this technology (HTML5 + JS based, similar to many other tech stacks that Microsoft is promoting).  Note that it is still in a beta release last time I checked but the fact that it caters to the Excel power user with similar syntax merged with an easy drag and drop interface feels like this could gain traction better than some other tools.  If you aren’t familiar with Project Siena you really need to see it to understand it.

Microsoft Project Siena: Build Apps and Create New Mobile Solutions (session recording with demos)

Microsoft Project Siena (Beta) (product site)

  • New SharePoint hybrid search option – Hybrid search is receiving a huge update / upgrade later this year.  In it’s current (May 2015) form SharePoint hybrid search involves separate search service applications / indices for on-prem farms and Office 365 / SharePoint Online.  If you query one source you can federate the query to the other and get results in a separate result block.  The problem though is that configuration can be fairly complex, search results aren’t integrated (in-line with each other), and you likely have a large number of servers on-prem for the search service.  Later this year (target timeframe, subject to change) Microsoft will release an update which will allow an on-prem “cloud search service application” to crawl and parse content but then push the metadata up to Office 365 for indexing, querying, etc.  The massive benefit of this is that your on-prem content will then be able to be used in other services like Delve, Office 365 data loss prevention (DLP), and others that currently have no expected on-prem release (or won’t be supported until future releases of SharePoint). Additionally you will need a much smaller on-prem server footprint to support search (the example given was going from 10+ search servers down to 2).  This is a big win in my opinion and I can’t want to test it out when it is released.

Implementing Next Generation SharePoint Hybrid Search with the Cloud Search Service Application (session recording)

  • Nano Server – Nano Server is a new installation option for Windows Server 10 (Server 2016 or whatever the final name ends up as) akin to Server Core in the past.  There were a number of sessions that talked about how small the footprint of Nano Server will be (400MB, yes MB compared to 8+ GB of server + GUI “full” edition).  The changes that this introduces not only affect performance but also re-architecting tools to work remotely (there is no local logon or UI for Nano Server, everything must be done remotely).  Things like Event Viewer, Task Manager, Local Services, etc. can be accessed remotely in a web UI similar to the “new” Azure Portal UI (super slick, take a look).  This may sound scary to some admins who are used to having RDP or locally logging on to a server but listen to Jeffrey Snover’s take on this.  We are IT Professionals and this is a technology that will reduce number of reboots, make servers more secure, reduce infrastructure footprint, and have numerous other benefits.  You owe it to yourself and your company to learn about this and see if it will work for the services you provide.

Nano Server (session recording)

Nano Server: The Future of Windows Server Starts Now (session recording)

Remotely Managing Nano Server (session recording)

  • PowerShell – Getting to see Jeffrey Snover (inventor or PowerShell) and Don Jones (first follower of PowerShell, see the link in slide deck) geek out about PowerShell was one of the best sessions I got to see at Ignite.  Hard to describe in words hence I recommend go watch the recording.  Jeffrey had some great advice about using PowerShell as a tool to explore and dive into problems or scenarios you are trying to solve.  That sense of adventure can be a motivating force for your personal and professional careers.  It was really inspiring and I love the fact that Jeffrey (and Don’s) mindset is spreading to so many others these days.

Windows PowerShell Unplugged with Jeffrey Snover (session recording)


   On a side note I also wanted to mention one of the obvious but not always talked about benefits of going to a conference like this in-person.  During the week I was able to introduce myself to a number of presenters that I had previously not met.  Some were MVPs, fellow Premier Field Engineers (PFEs), product group members, and more.  The connections you make can last for years and provide an invaluable network for sharing information and getting assistance when you are in need.  I even got a PowerShell sticker directly from Jeffrey Snover himself (another personal highlight).




   This is just a short list of some of the sessions that I attended along with highlights or key points that I wanted to share.  If I find anything else significant from the recordings I am going back to watch I’ll update this post.  For now though go check out the recordings above or the hundreds of other ones that are up on Channel 9.  I encourage you to attend next year when Ignite 2016 will be in Chicago again May 9-13.


      -Frog Out