Category Archives: PowerShell

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
           

2010 Service Release – Intune in a half shell, Shell Power!!

The 2010 Service Release has since a number of updates as shown below;

SectionItemLink
App ManagementApps that require enrollment are hidden when enrollment is set to unavailableLink
Improvements to iOS Company Portal privacy message customizationLink
Android app protection policies (MAM) on COPE devicesLink
Max Company Portal version age for Android devicesLink
Mac LOB apps will be supported as managed apps on macOS 11 and higherLink
Enable Outlook S/MIME emails to be always signed or encryptedLink
Device ConfigurationUse the Connect Automatically setting on Android Enterprise basic Wi-Fi profilesLink
New user experience and new Enable direct download setting on macOS devices using associated domainsLink
New lockout password settings on macOS devicesLink
Required password type default setting is changing on Android Enterprise devicesLink
Device enrollmentIntune support for provisioning Azure Active Directory shared devicesLink
Device SecurityApp protection policy support on Android and iOS/iPadOS for additional partnersLink
Endpoint Manager Security tasks include details about misconfigured settings from Microsoft Defender ATP TVMLink
Endpoint security Firewall policies for tenant attached devicesLink
Expanded settings to manage hardware device installation through block and allow listsLink
Improvements to endpoint security Firewall rulesLink
Use Microsoft Defender for Endpoint in compliance policies for iOSLink
Security Experience profiles for Endpoint Security Antivirus policy now have tri-state optionsLink
Updated version of the Edge security baselineLink
Monitor and troubleshootNew Windows 10 feature update failures reportLink
Updates to Antivirus reportsLink
Updated Help and Support for Microsoft Endpoint ManagerLink
ScriptsCollect custom device or user properties using shell scripts on managed MacsLink

But what I wanted to pick up on is the new PowerShell elements within this service release as its something I regular do within my own Windows 10 deployments.

These are really cool additions.

View PowerShell scripts in the Intune Troubleshooting pane

You can now view your assigned PowerShell scripts in the Troubleshooting pane. PowerShell scripts provide Windows 10 client communication with Intune to run enterprise management tasks, such as advanced device configuration and troubleshooting. For more information, see Use PowerShell scripts on Windows 10 devices in Intune.

Win32 app support for Workplace join (WPJ) devices

Existing Win32 apps are supported for Workplace join (WPJ) devices. PowerShell scripts, which are not officially supported on WPJ devices, can be deployed to WPJ devices. Specifically, device context PowerShell scripts work on WPJ devices, but user context PowerShell scripts are ignored by design. User context scripts will be ignored on WPJ devices and will not be reported to the Microsoft Endpoint Manager console. For more information about PowerShell, see Use PowerShell scripts on Windows 10 devices in Intune.

Regards,
The Author – Blogabout.Cloud

Using PowerShell to view the Current Status of Microsoft 365 with Office 365 Service Communications API

With Office 365 Service Communications API you can pull the required Current Status using PowerShell providing you have completed the prerequisites below.

Screenshot

Prerequisites

  • PowerShell script
  • TenantID
  • ClientID
  • ClientSecret

The TenantID, ClientID and ClientSecret are obtained from your Application but please do remember you need to have a copy of the ClientSecret as once you move away from the secret. You wont be able to see it again.

The Script

Clear-Host
 <#Information
 
    Author: thewatchernode
    Contact: author@blogabout.cloud
    Published: 30th Ocotber 2020

    .DESCRIPTION
    Tool to gather Microsoft 365 Health Status.

    # IMPORTANT 
    Office 365 Service Communications API needs to be configured with your Tenant. http://www.blogabout.cloud/2020/10/1884/

    Version Changes            
    
    : 0.1 Initial Script Build
    : 1.0 Initial Build Release
     
    Credit:
     

    .EXAMPLE
    .\Get-Microsoft365Status.ps1

    Description
    -----------
    Runs script with default values.


    .INPUTS
    None. You cannot pipe objects to this script.
#>
 #region To be configured by the script runner

# Objects
$tenantId = ''
$client_id = ''
$client_secret = ''
#endregion

Function Get-M365Status {
# Construct URI for OAuth Token
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"

# Construct Body for OAuth Token
$body = @{
    client_id     = $client_id
    scope         = "https://manage.office.com/.default"
    client_secret = $client_secret
    grant_type    = "client_credentials"
}

# Get OAuth 2.0 Token
$tokenRequest = try {

    Invoke-RestMethod -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -ErrorAction Stop

}
catch [System.Net.WebException] {

    Write-Warning "Exception was caught: $($_.Exception.Message)"
    
}

$token = $tokenRequest.access_token

# Get Office 365 Status
$m365status = try {

    Invoke-RestMethod -Method Get -Uri "https://manage.office.com/api/v1.0/$tenantid/ServiceComms/CurrentStatus" -ContentType "application/json" -Headers @{Authorization = "Bearer $token"} -ErrorAction Stop

}
catch [System.Net.WebException] {

    Write-Warning "Exception was caught: $($_.Exception.Message)" 
    
} 

# List service overview status
$m365status.Value | Format-Table WorkloadDisplayName, StatusDisplayName, Status, IncidentIds
}
Write-host 'Version information - You are running script version 1.5' -ForegroundColor $White -BackgroundColor $DarkGray
  @'
  ┌─────────────────────────────────────────────────────────────┐
           Gather the status of Microsoft 365 Service Health

               Follow @thewatchernode on Twitter                               
  └─────────────────────────────────────────────────────────────┘
'@
Start-Transcript -Path $InstallDir\M365Status_Log.txt
Get-M365Status
Stop-Transcript


1
 

Download

https://github.com/TheWatcherNode/blogaboutcloud/blob/master/Get-Microsoft365Status.ps1

Regards
The Author – Blogabout.Cloud

How to uninstall PowerShell modules with dependencies within a script.

PowerShell module can sometimes become a nightmare if there are dependencies on other modules. I have been recently looking at my AzureADPreview which had one dependency for;

– Microsoft365DSC module

If you run the standard uninstall-module -name AzureADPreview you will receive an error stating the module cannot be uninstalled due to the dependency of the other module as shown below;

If you run install-module AzureADPreview you are likely to receive a duplicate install of the same module which will potential cause issues later down the line.

So,

The following function has made this possible.

Function Uninstall-AllModules
{
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory = $true)]
[string]
$TargetModule,
[Parameter(Mandatory = $false)]
[string]
$Version,
[switch]
$Force
)

$AllModules = @()

‘Creating list of dependencies…’
$target = Find-Module $TargetModule
$target.Dependencies | ForEach-Object {
$AllModules += New-Object -TypeName psobject -Property @{ name = $_.name}
}
$AllModules += New-Object -TypeName psobject -Property @{ name = $TargetModule}

$cnt = 1;
foreach ($module in $AllModules)
{
Write-Host (“[$cnt] – ” + ‘Uninstalling {0} version’ -f $module.name);
$cnt++;
try
{
if ($PSCmdlet.ShouldProcess($module.name, ‘Uninstall’))
{
Uninstall-Module -Name $module.name -Force:$Force -ErrorAction Stop;
};
}
catch
{
Write-Host (“`t” + $_.Exception.Message)
}
}
}

## – Example using -WhatIf parameter:
Uninstall-AllModules -TargetModule AzureAD -Force

Regards,
The Author – Blogabout.Cloud