Category Archives: PowerShell

Invoke-WebRequest: The response content cannot be parsed when adapting a local powershell script for Azure Automation.

I have been recently adapting a PowerShell script that collects the Microsoft 365 Service Health using the Office 365 Service Communication API publishing the information into ServiceNow to log a support ticket to make the helpdesk aware of a potential issue in the service. The script was working is no issues when run manually out of PowerShell ISE but failed when ran by Azure Automation.

The focus of this post is to show two specific steps for adapting a locally executed PowerShell script for an Azure Automation runbook. In addition to standard work in Automation to add credentials/connections and create parameters where needed, the following changes were required to adapt a local script for my runbook.

1) Add -UseBasicParsing to Web Requests

The Management API script is filled with various requests using Invoke-WebRequest. Initially, I received numerous errors stating that the Internet Explorer engine is not available.

Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or 
Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. 
At line:46 char:9
+ $subs = Invoke-WebRequest -Headers $headerParams -Uri "https://manage ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotImplemented: (:) [Invoke-WebRequest], NotSupportedException
    + FullyQualifiedErrorId : WebCmdletIEDomNotSupportedException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

To overcome this, I needed to add the parameter UseBasicParsing.

Original: $oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body
Adapted: $oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/token?api-version=1.0 -Body $body –UseBasicParsing

2) Alter $ProgressPreference

PowerShell shows progress using $ProgressPreference, which defaults to “continue”. For Automation, I needed to change the option to “silently continue” and suppress progress bars. Locally, the progress can be convenient to see, but it was causing issues with

Automation.Adapted: $ProgressPreference = “silentlyContinue”

Regards
The Author – Blogabout.Cloud

Configuring Password Expiry within Microsoft 365 admin center and with PowerShell

In this brief blog post I am going to demonstrate how simple it is to configure or modify password expiry policy within the Microsoft 365 Admin Center or using PowerShell to make the necessary change.

Configure using Admin Center

Launch https://admin.microsoft.com and go to Settings –> Org Settings –> Security & Privacy –> Password expiration policy

Here you can untick Set user passwords to expire which will disable this setting or configure/modify “Days before passwords expire”

Configure using PowerShell

Running the following commands has the same effect as changing the configuration in the Microsoft 365 Admin Center.

# Connect to M365
$cred = Get-Credential
Connect-MsolService -Credential $cred

# Configure Password Policy
Set-MsolPasswordPolicy -ValidityPeriod 60 -NotificationDays 14 -DomainName "contoso.com"

Regards
The Author – Blogabout.Cloud

Pushing Microsoft Health Status to Microsoft Teams using Azure Automation.

In this post I am going to demostrate how to push Microsoft Health Status to Microsoft Teams using Azure Automation

In this post it is expected you have configured Office 365 Service Communications API. If not, visit this blog post https://www.blogabout.cloud/2020/10/1884/

Configuring Microsoft Teams for Incoming Webhook

Launch Microsoft Teams right click a channel within a Team and select Connectors. Search for Incoming Webhook and click Add

Click Add

Specify the name of the Webhook and click Create

Make a note of the URL below as you will need this for the PowerShell script later and click Done

Create Automation Account

Launch your Azure Portal and search for Automation Account to create a new account.

Specify;
– Name
– Subscription
– Resource Group
– Location

No should be selected for “Create Azure Run As Account” as this is will enable the Account to run against Office 365.
Click Create

Once the resources has successful created, go into the resource

First of all we need to create a number of variables, these variables will use within the PowerShell script we are going to add to the Runbook.

You will need to add the following variables

– AzO365ServiceHealthApplicationID = Client ID
– AzO365ServiceHealthApplicationKey = Client Secrey
– AzO365TeamsURI = This is the url we created earlier in the post
– AzO365TenantDomain = Tenant ID

If you unsure how to obtain these values, please visit this blog post for more details

We now need to launch Runbooks

Select Create a runbook
– Provide a name
– Runbook Type should equal PowerShell
– Description (is optional)

Press Create

I am now going to use the following PowerShell script available from GitHub within my Runbook. https://github.com/einast/PS_M365_scripts. This script has been created for the sole purpose of extracting the Microsoft 365 Service Health using the Office Service Communication API, which I demonstrated how to configure in another post.

Under the “User Defined Variables” you will need to edit;

Mintues to reflect the same time as your schedule.

Pushover notifications and Services to monitor as shown below

# ------------------------------------- USER DEFINED VARIABLES -------------------------------------

$ApplicationID = Get-AutomationVariable -Name 'AzO365ServiceHealthApplicationID'
$ApplicationKey = Get-AutomationVariable -Name 'AzO365ServiceHealthApplicationKey'
$TenantDomain = Get-AutomationVariable -Name 'AzO365TenantDomain'
$URI = Get-AutomationVariable -Name 'AzO365TeamsURI'
$Minutes = '1440'

# Pushover notifications in case Teams is down.
# Due to limitations and readability, the script will only send the title of the incident/advisory to Pushover. 
# COMMENT OUT THIS SECTION IF YOU DON'T WANT TO USE PUSHOVER!

#$Pushover = 'yes' # Comment out if you don't want to use Pushover. I use 'yes' for readability.
#$PushoverToken = Get-AutomationVariable -Name 'AzO365PushoverToken' # Your API token. Comment out if you don't want to use Pushover
#$PushoverUser = Get-AutomationVariable -Name 'AzO365PushoverUser' # User/Group token. Comment out if you don't want to use Pushover
#$PushoverURI = 'https://api.pushover.net/1/messages.json' # DO NOT CHANGE! Default Pushover URI. Comment out if you don't want to use Pushover

# Service(s) to monitor
# Leave the one(s) you DON'T want to check empty (with '' ), add a value in the ones you WANT to check (I added 'yes' for readability)

$ExchangeOnline = 'yes'
$MicrosoftForms = 'yes'
$MicrosoftIntune = 'yes'
$MicrosoftKaizala = 'yes'
$SkypeforBusiness = 'yes'
$MicrosoftDefenderATP = 'yes'
$MicrosoftFlow = 'yes'
$FlowinMicrosoft365 = 'yes'
$MicrosoftTeams = 'yes'
$MobileDeviceManagementforOffice365 = 'yes'
$OfficeClientApplications = 'yes'
$Officefortheweb = 'yes'
$OneDriveforBusiness = 'yes'
$IdentityService = 'yes'
$Office365Portal = 'yes'
$OfficeSubscription = 'yes'
$Planner = 'yes'
$PowerApps = 'yes'
$PowerAppsinMicrosoft365 = 'yes'
$PowerBI = 'yes'
$AzureInformationProtection = 'yes'
$SharePointOnline = 'yes'
$MicrosoftStaffHub = 'yes'
$YammerEnterprise = 'yes'

# Classification(s) to monitor
# Leave the one(s) you DON'T want to check empty (with '' ), add a value in the ones you WANT to check (I added 'yes' for readability)

$Incident = 'yes'
$Advisory = 'yes'

# ------------------------------------- END OF USER DEFINED VARIABLES -------------------------------------

You will now need to define a schedule within the Runbook for it to execute.

Once you have done all of the above, you will receive a nice notification in Microsoft Teams about the Service Health Status

In my next article I will show how simple it is too trigger this Runbook using Power Automate.

Regards
The Author – Blogabout.Cloud

Improvements for PowerShel scripts in Microsoft Endpoint Manager – Good or Bad?

As a big adovcate of PowerShell Scripts in Microsoft Endpoint Manager, I definitely welcome the recent changes which Microsoft have implemented. This will have some positive effects on most organisations but maybe not as welcomed by others and heres why?

In my experience some organisations like to leverage PowerShell to modify applications that have been installed using Win32 apps. An example I have experience within this space is Java ( Oh the horror ). This organisation still required a fat install of java to run a legacy application and Java was inserted using GPO with reg hive modified to prevent the regular and annoying pop up for updates.

So to address this we installed Java via W32 apps and used a PowerShell script from Microsoft Endpoint Manager to modify the key.

What you will probably need to do is allow your script to fail. Once the script has failed, the Win32 apps will then be installed, and If the script fails, the Intune management extension agent retries the script three times for the next three consecutive Intune management extension agent check-in. The check in period is every 60 minutes so in that time you should have succesfully installed all Win32 apps.

Here is the new channges for PowerShell scripts.

PowerShell scripts execute before apps, and time out reduced

There are some updates to PowerShell scripts:

  • Microsoft Intune management extension execution flow is reverted back to processing PowerShell scripts first, and then running Win32 apps.
  • To resolve an Enrollment Status Page (ESP) time out issue, PowerShell scripts time out after 30 minutes. Previously, they timed out after 60 minutes.

For more information, see Use PowerShell scripts on Windows 10 devices in Intune.

Regards
The Author – Blogabout.Cloud

PowerShell – Prompt for file using Windows Explorer

Sometimes when working with PowerShell scripts you may need to import a particular source file. You could specify a variable pointing directly to the file but using the below function provide a more flexible solution.

Function Get-FileName
{  
  [CmdletBinding()]
  param
  (
    [Object]$initialdirectory
  )
  Add-Type -AssemblyName System.windows.forms | Out-Null
    
    $OpenFileDialog = New-Object -TypeName System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.initialDirectory = $initialdirectory
    $OpenFileDialog.filter = "All files (*.*)| *.*"
    $OpenFileDialog.ShowDialog() | Out-Null
    $OpenFileDialog.filename
}
#endregion
Get-FileName -initialdirectory "$env:USERProfile\Desktop" 

Regards
The Author – Blogabout.Cloud

PowerShell – Checking whether a TCP port is open using PowerShell

Sometimes when working with customers you need to validate if a particular port has been opened on their Firewall. I am unable to count how many times I’ve had a discussion where a port hasnt been opened. So for simplicity the following PowerShell function allows you to check instantly;

Function Get-CheckPort{
[cmdletbinding()]
Param (
[parameter(Mandatory=$true)]
[string]$ipaddress,
[string]$port
)
# End of Parameters
Process{
  $Date = Get-Date
  $MyDate =  " " + $Date.Day + "." + $Date.Month + "." + $Date.Year + " " + $date.Hour + ":" + $date.Minute
  
  Try{
     $connection = $null
     $connection = New-Object System.Net.Sockets.TcpClient($ipaddress, $port)
  }

  Catch{
       Write-host "Warning: Port $port is closed - $MyDate" -ForegroundColor Red
     }
  if ($connection.Connected){
     Write-Host "Info: Port $port is open - $MyDate" -ForegroundColor Green
    }
   }
 }
Get-CheckPort -ipaddress '' -port ''

As you can see from the test above, I am to validate that port 80 to the teams.microsoft.com is open

Regards
The Author – Blogabout.Cloud

Export/Import your Conditional Access policy baselines for your customers

I have recently come across an amazing new PowerShell module which allows you to export/import your Conditional Access policies. This module is brilliant if your someone like who loves a bit of PowerShell and baselines their configuration so it can be reused for other customers.

Before we continue, I wanted to highlight the author of this module Daniel Chronlund. Make sure you follow his blog and give him a #FF on Twitter.
https://danielchronlund.com
http://twitter.com/danielchronlund

So first of all, lets install the module on your client machine

Install-Module -Name DCToolbox

Once installed, lets see how we use this module by running the following command.

Get-Command -Module DCToolbox

Before running the above commands I suggest running the following command as it provides useful examples.

Get-DCHelp
Copy-DCExample

The below command provides you with 4 options to choice from;

Option NumberOption NameDescription
1Microsoft Graph with PowerShell examplesProvides PowerSell scripting examples
2Manage Conditional Access as codeProvides PowerSell scripting examples
3Activate an Azure AD Privileged Identity Management (PIM) roleProvides PowerSell scripting examples
4General PowerShell script templateCreate PowerShell script

Each of this options copy the required coding to your clipboard which you can then import into something like PowerShell ISE.

Microsoft Graph with PowerShell examples

*** Connect Examples ***

Connect to Microsoft Graph with delegated credentials.

$Parameters = @{
ClientID = ''
ClientSecret = ''
}
$AccessToken = Connect-DCMsGraphAsDelegated @Parameters

Connect to Microsoft Graph with application credentials.

$Parameters = @{
TenantName = 'example.onmicrosoft.com'
ClientID = ''
ClientSecret = ''
}
$AccessToken = Connect-DCMsGraphAsApplication @Parameters

*** Microsoft Graph Query Examples ***

GET data from Microsoft Graph.

$Parameters = @{
AccessToken = $AccessToken
GraphMethod = 'GET'
GraphUri = 'https://graph.microsoft.com/v1.0/users'
}
Invoke-DCMsGraphQuery @Parameters

POST changes to Microsoft Graph.

$Parameters = @{
AccessToken = $AccessToken
GraphMethod = 'POST'
GraphUri = 'https://graph.microsoft.com/v1.0/users'
GraphBody = @"

"@
}
Invoke-DCMsGraphQuery @Parameters

PUT changes to Microsoft Graph.

$Parameters = @{
AccessToken = $AccessToken
GraphMethod = 'PUT'
GraphUri = 'https://graph.microsoft.com/v1.0/users'
GraphBody = @"

"@
}
Invoke-DCMsGraphQuery @Parameters

PATCH changes to Microsoft Graph.

$Parameters = @{
AccessToken = $AccessToken
GraphMethod = 'PATCH'
GraphUri = 'https://graph.microsoft.com/v1.0/users'
GraphBody = @"

"@
}
Invoke-DCMsGraphQuery @Parameters

DELETE data from Microsoft Graph.

$Parameters = @{
AccessToken = $AccessToken
GraphMethod = 'DELETE'
GraphUri = 'https://graph.microsoft.com/v1.0/users'
}
Invoke-DCMsGraphQuery @Parameters
<#
Filter examples:
/users?$filter=startswith(givenName,'J')
/users?$filter=givenName eq 'Test'
>

Learn more about the Graph commands.

help Connect-DCMsGraphAsDelegated -Full
help Connect-DCMsGraphAsApplication -Full
help Invoke-DCMsGraphQuery -Full

Manage Conditional Acces as code


You first need to register a new application in your Azure AD according to this article:
https://danielchronlund.com/2018/11/19/fetch-data-from-microsoft-graph-with-powershell-paging-support/

The following Microsoft Graph API permissions are required for this to work:
Policy.ReadWrite.ConditionalAccess
Policy.Read.All
Directory.Read.All
Agreement.Read.All
Application.Read.All

Also, the user running this (the one who signs in when the authentication pops up) must have the appropriate permissions in Azure AD (Global Admin, Security Admin, Conditional Access Admin, etc).

Export your Conditional Access policies to a JSON file for backup.

$Parameters = @{
ClientID = ''
ClientSecret = ''
FilePath = 'C:\Temp\Conditional Access Backup.json'
}
Export-DCConditionalAccessPolicyDesign @Parameters

Import Conditional Access policies from a JSON file exported by Export-DCConditionalAccessPolicyDesign.

$Parameters = @{
ClientID = ''
ClientSecret = ''
FilePath = 'C:\Temp\Conditional Access Backup.json'
SkipReportOnlyMode = $false
DeleteAllExistingPolicies = $false
}
Import-DCConditionalAccessPolicyDesign @Parameters

Export Conditional Access policy design report to Excel.

$Parameters = @{
ClientID = ''
ClientSecret = ''
}
New-DCConditionalAccessPolicyDesignReport @Parameters

Export Conditional Access Assignment Report to Excel.

$Parameters = @{
ClientID = ''
ClientSecret = ''
IncludeGroupMembers = $false
}
New-DCConditionalAccessAssignmentReport @Parameters

Learn more about the different Conditional Access commands in DCToolbox.

help Export-DCConditionalAccessPolicyDesign -Full
help Import-DCConditionalAccessPolicyDesign -Full
help New-DCConditionalAccessPolicyDesignReport -Full
help New-DCConditionalAccessAssignmentReport -Full

Activate an Azure AD Privileged Identity Management (PIM) role.

Enable-DCAzureADPIMRole

User sign-in will popup and the after signing in, the following will happen:

VERBOSE: Connecting to Azure AD...

*** Activate PIM Role ***

[1] User Account Administrator
[2] Application Administrator
[3] Security Administrator
[0] Exit

Choice: 3
Duration [1 hour(s)]: 1
Reason: Need to do some security work!
VERBOSE: Activating PIM role...
VERBOSE: Security Administrator has been activated until 11/13/2020 11:41:01!

Learn more about Enable-DCAzureADPIMRole.

help Enable-DCAzureADPIMRole -Full

Privileged Identity Management | My roles: https://portal.azure.com/#blade/Microsoft_Azure_PIMCommon/ActivationMenuBlade/aadmigratedroles

Privileged Identity Management | Azure AD roles | Overview: https://portal.azure.com/#blade/Microsoft_Azure_PIMCommon/ResourceMenuBlade/aadoverview/resourceId//resourceType/tenant/provider/aadroles

General PowerShell script template

<#
    .SYNOPSIS
        A simple script template.

    .DESCRIPTION
        Write a description of what the script does and how to use it.
        
    .PARAMETER Parameter1
        Inputs a string into the script.
            
    .PARAMETER Parameter2
        Inputs an integer into the script.
            
    .PARAMETER Parameter3
        Sets a script switch.

    .INPUTS
        None

    .OUTPUTS
        System.String

    .NOTES
        Version:        1.0
        Author:         Daniel Chronlund
        Creation Date:  2021-01-01

    .EXAMPLE
        Script-Template -Parameter "Text" -Verbose

    .EXAMPLE
        Script-Template -Parameter "Text" -Verbose
#>

# ----- [Initialisations] -----

# Script parameters.
param (
    [parameter(Mandatory = $true)]
    [string]$Parameter1 = "Text",

    [parameter(Mandatory = $true)]
    [int32]$Parameter2 = 1,

    [parameter(Mandatory = $false)]
    [switch]$Parameter3
)

# Set Error Action - Possible choices: Stop, SilentlyContinue
$ErrorActionPreference = "Stop"

# ----- [Declarations] -----

# Variable 1 description.
$Variable1 = ""

# Variable 2 description.
$Variable2 = ""

# ----- [Functions] -----

function function1
{
    <#
        .SYNOPSIS
            A brief description of the function1 function.
        
        .DESCRIPTION
            A detailed description of the function1 function.
        
        .PARAMETER Parameter1
            A description of the Parameter1 parameter.
        
        .EXAMPLE
            function1 -Parameter1 'Value1'
    #>

    param (
        [parameter(Mandatory = $true)]
        [string]$Parameter1
    )

    $Output = $Parameter1

    $Output
}

# ----- [Execution] -----

# Do the following.
function1 -Parameter1 'Test'

# ----- [End] -----

Now you have all knowledge and tools required to backup for Conditional Access Policies for use with other tenants.

Regards
The Author – Blogabout.Cloud

PowerShell Tip: Do all your Microsoft Teams have owners?

In many organisations where governance around the creation of Microsoft Teams is currently not in place. You may come across Teams where the owner may have left the organisation but the Team is still active. So using PowerShell we can find which Teams have or do not have owners.

Prerequistes

Microsoft Teams Module needs to be installed

Install-Module MicrosoftTeams

PowerShell Script

The following script will obtain all the required information and export to CSV.

$availableTeams = Get-Team
  $teams = @()
  foreach($team in $availableTeams)
  {
 
    Write-host "Discovered the following team:"  -NoNewline
    Write-host $team.DisplayName -ForegroundColor Yellow
    $users = Get-TeamUser -GroupId $team.GroupId
    $channel = Get-TeamChannel -GroupId $Team.GroupId
    $owners = @($users | Where-Object {$_.Role -eq 'owner'})
    $members = @($users | Where-Object {$_.Role -eq 'member'}).Length
    $guests = @($users | Where-Object {$_.Role -eq 'guest'}).Length

    Write-Host "Number of Owners =",($owners.count),"| Number of Members =",($members.count),"| Number of Guests =",($guests.count)

    $teamObject = New-Object -TypeName PSObject
    $teamObject | Add-Member -MemberType $NoteProperty -Name DisplayName -Value $team.DisplayName
    $teamObject | Add-Member -MemberType $NoteProperty -Name GroupID -Value $team.GroupId
    $teamObject | Add-Member -MemberType $NoteProperty -Name Alias -Value $team.MailNickName
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamVisibility" -Value $Team.Visibility
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamArchived" -Value $Team.Archived
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamOwners" -Value ($owners).Count
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamMembers" -Value ($members).Count
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamGuests" -Value ($guests).Count
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamOverallUserCount" -Value $users.Count
    $teamObject | Add-Member -Membertype $noteproperty -Name "TeamChannelCount" -Value $channel.Count


    if($owners.Count -eq 1)
    {
      $teamObject | Add-Member -MemberType $NoteProperty -Name 'Owner' -Value $owners[0].User
    }

    write-host ' Analysis Completed' -BackgroundColor DarkGreen
    $teams += $teamObject
  }

  $teams | Export-Csv -Path $env:USERPROFILE\desktop\Get-MicrosoftTeamOwnerInfo.csv
  write-host "Output exported to: $env:USERPROFILE\desktop\Get-MicrosoftTeamOwnerInfo.csv" -BackgroundColor DarkGreen
}

Regards,
The Author – Blogabout.Cloud

PowerShell Tip: Obtaining the ImmutableID from your Active Directory Objects on-prem and in the cloud

When working with Azure Active Directory Connect you may experience issues with account duplicating due to the ImmutableID not matching. If it does happen its is a pain to resolve as you have to;

Desynchronize the affected accounts
Delete from the Deleted Users OU in Azure Active Directory
Obtain the on-premises ImmutableID
Obtain the cloud ImmutableID
Compare the IDs
Set the cloud ID with the on-premises ID

Now wouldnt it be easier if someone had a bunch of PowerShell commands to help you get the ImmutableID. This is where I come in

Obtaining ImmutableID from on-premises Active Directory Object

The following PowerShell script extracts all the ImmutableID’s from every single Active Directory User Object and store in a CSV file on your desktop.

$reportoutput=@()
$users = Get-ADUser -Filter * -Properties *
$users | Foreach-Object {

    $user = $_
    $immutableid = "[System.Convert]::ToBase64String($user.ObjectGUID.tobytearray())"
    $userid = $user | select @{Name='Access Rights';Expression={[string]::join(', ', $immutableid)}}

    $report = New-Object -TypeName PSObject
    $report | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $user.UserPrincipalName
    $report | Add-Member -MemberType NoteProperty -Name 'SamAccountName' -Value $user.samaccountname
    $report | Add-Member -MemberType NoteProperty -Name 'ImmutableID' -Value $immutableid
    $reportoutput += $report
}
 # Report
$reportoutput | Export-Csv -Path $env:USERPROFILE\desktop\ImmutableID4AD.csv -NoTypeInformation -Encoding UTF8 }

Obtaining ImmutableID from Azure Active Directory Object

The following PowerShell script extracts all the ImmutableID’s from every single Azure Active Directory User Object and store in a CSV file on your desktop.

$reportoutput=@()
$users = Get-AzureADUser -All $true
$users | Foreach-Object {

    $user = $_

    $report = New-Object -TypeName PSObject
    $report | Add-Member -MemberType NoteProperty -Name 'UserPrincipalName' -Value $user.UserPrincipalName
    $report | Add-Member -MemberType NoteProperty -Name 'SamAccountName' -Value $user.samaccountname
    $report | Add-Member -MemberType NoteProperty -Name 'ImmutableID' -Value $user.immutableid
    $report | Add-Member -MemberType NoteProperty -Name 'DisplayName' -Value $user.displayname
    $reportoutput += $report
}
 # Report
$reportoutput | Export-Csv -Path $env:USERPROFILE\onedrive\desktop\ImmutableID4AAD.csv -NoTypeInformation -Encoding UTF8 }

Recommendation

When I have had to compare the two exports at scale for an entire environment, it can be a complete nightmare but the ImportExcel module was brilliant in getting the data merged into a single sheet.

https://www.powershellgallery.com/packages/ImportExcel/7.1.1

Regards
The Author – Blogabout.Cloud

Quick Tips: How do I restore a deleted Microsoft Teams Team using PowerShell.

Sometimes accidents happen and things get deleted by mistake but what do you do if you delete a Microsoft Team by mistake?

When the team is deleted, it is held in the “recycle bin” for 30 days until it is permanently deleted. The following is the process of restoring a deleted team in Microsoft Teams.

  • Once Team is deleted, option to recover it exists for up to 30 days
  • All of it including (Channels, files, tabs, etc.) will reappear as it was before
  • Restore can take up to 4 hours
  • To restore, from exchange admin center, select recipients, then groups
  • Locate the group (only if soft deleted)
  • Select the group and choose restore

This is where my favourite Microsoft tool comes into action, PowerShell

Prerequisites

AzureAD Module is installed
Global Admin to your Azure AD Tenant

Process

Launch PowerShell as an administrator


1
Connect-AzureAD

When a team is created in Microsoft Teams, it creates an Office 365 group. In order to restore the group you will need to obtain the Id using the following cmdlet


1
Get-AzureADMSDeletedGroup

1
Restore-ADMSDeletedDirectoryObject –ID &lt;objectID>

As mentioned above the restore could take up to 4 hours to complete.

Regards
The Author – Blogabout.Cloud