Skip to content

Instantly share code, notes, and snippets.

@MrWyss-MSFT
Last active September 10, 2024 13:35
Show Gist options
  • Select an option

  • Save MrWyss-MSFT/c063ab39cf0bcab868e31503b27fe1d8 to your computer and use it in GitHub Desktop.

Select an option

Save MrWyss-MSFT/c063ab39cf0bcab868e31503b27fe1d8 to your computer and use it in GitHub Desktop.
Lists not assigned apps Intune Win32Apps for the user to delete.
<#
.SYNOPSIS
Lists not assigned apps Intune Win32Apps for the user to delete.
.DESCRIPTION
Lists all apps that are not assigned and are Win32 Apps.
The user can select the apps to delete. There is standard output as well as a
CMTrace compatible Logfile (Delete-UnusedIntuneApps-yyyy-m-dd.log)
which will be created in the ScriptRoot.
Requires
- Modules Microsoft.Graph.Intune and Microsoft.Graph.Authentication
- Install-Module Microsoft.Graph.Authentication -Force
- Access to MSGraph with Consent (DeviceManagementApps.ReadWrite.All)
- Tested with PowerShell 7.4
.EXAMPLE
PS> .\Delete-UnusedIntuneAppss.ps1
DryRun Mode
.EXAMPLE
PS> .\Delete-UnusedIntuneApps.ps1 -Force
With Force Mode devices will get wiped
.NOTES
Author: Marius Wyss ([email protected])
Date: 2024-09-10
Version: 1.1
.LINK
https://gist.github.com/MrWyss-MSFT/c063ab39cf0bcab868e31503b27fe1d8
#>
#Requires -Modules @{ ModuleName="Microsoft.Graph.Authentication"; ModuleVersion="2.20.0" }
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false)]
[Switch]
$Force
)
#region Variables
$ScriptName = "Delete-UnusedIntuneApps"
$ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path
$LogFilePath = Join-Path $ScriptDirectory "$ScriptName-$(Get-Date -Format yyyy-M-dd).log"
$PSDefaultParameterValues = @{
"Write-Log:Path" = $LogFilePath
"Write-Log:Component" = $ScriptName
"Write-Log:Type" = "Info"
"Write-Log:ConsoleOutput" = $True
}
#endregion
#region Functions
Function Write-Log {
<#
.SYNOPSIS
Writes CMTrace log file, customized version of https://janikvonrotz.ch/2017/10/26/powershell-logging-in-cmtrace-format/
#>
[CmdletBinding()]
Param(
[parameter(Mandatory = $true)]
[String]$Path,
[parameter(Mandatory = $true, ValueFromPipeline)]
[String]$Message,
[parameter(Mandatory = $true)]
[String]$Component,
[Parameter(Mandatory = $true)]
[ValidateSet("Info", "Warning", "Error")]
[String]$Type,
[Parameter(Mandatory = $false)]
[Switch]$ConsoleOutput
)
switch ($Type) {
"Info" { [int]$Type = 1 }
"Warning" { [int]$Type = 2 }
"Error" { [int]$Type = 3 }
}
if ($ConsoleOutput.IsPresent) {
switch ($Type) {
1 { $ForgroundColor = "White" }
2 { $ForgroundColor = "Yellow" }
3 { $ForgroundColor = "Red" }
}
$OutPut = "{0} : {1}" -f $(Get-Date -Format "MM-d-yyyy HH:mm:ss.ffffff"), $Message
write-host $OutPut -ForegroundColor $ForgroundColor
}
# Create a log entry
$Content = "<![LOG[$Message]LOG]!>" + `
"<time=`"$(Get-Date -Format "HH:mm:ss.ffffff")`" " + `
"date=`"$(Get-Date -Format "M-d-yyyy")`" " + `
"component=`"$Component`" " + `
"context=`"$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)`" " + `
"type=`"$Type`" " + `
"thread=`"$([Threading.Thread]::CurrentThread.ManagedThreadId)`" " + `
"file=`"`">"
# Write the line to the log file
$Content | Out-File -FilePath $Path -Append -Encoding UTF8
}
#endregion
#region Prerequisites Check
if (-not (Get-Module -Name "Microsoft.Graph.Authentication" -ListAvailable)) {
Write-Host "Missing Modules, please run:" -ForegroundColor Yellow
Write-Host @"
Install-Module "Microsoft.Graph.Authentication" -Force
Import-Module "Microsoft.Graph.Authentication"
"@ -ForegroundColor Green
break
}
#endregion
#region Main Code
Write-Log -Message "--- Start $ScriptName ---"
# region Connect to Microsoft Graph
Connect-MgGraph -Scopes "DeviceManagementApps.ReadWrite.All" -NoWelcome
#endregion
# Discovered with the browser developer tools (F12) in https://intune.microsoft.com/#view/Microsoft_Intune_DeviceSettings/AppsMenu/~/allApps
$url = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?" +
"`$filter=((isof('microsoft.graph.win32LobApp') and not(isof('microsoft.graph.win32CatalogApp')))) " +
"and (microsoft.graph.managedApp/appAvailability eq null or microsoft.graph.managedApp/appAvailability eq 'lineOfBusiness' or isAssigned eq true) " +
"and isAssigned eq false" +
"&`$orderby=displayName"
$UnusedApps = Invoke-MgGraphRequest -uri $url
$SelectedApps = $UnusedApps.Value | Select-Object displayName, displayVersion, lastModifiedDateTime, description,id | Out-GridView -OutputMode Multiple -Title "$ScriptName"
Write-Log -Message "Selected Apps: $($SelectedApps.Count), json=$($SelectedApps | ConvertTo-Json)" -Type Info -ConsoleOutput:$false
Write-Output "Selected Apps: $($SelectedApps.Count)"
# Delete the selected apps
ForEach ($App in $SelectedApps) {
$AppId = $App.id
Write-Log -Message "Deleting App: $($App.displayName), id=$AppId, version=$($App.displayVersion)" -Type Warning
try {
if ($Force) {
Invoke-MgRestMethod -Method DELETE -Uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$AppId"
Write-Log -Message "Deleted App: $($App.displayName), id=$App, version=$($App.displayVersion)" -Type Info
} else {
Write-Log -Message "DryRun: Deleted App: $($App.displayName), id=$App, version=$($App.displayVersion)" -Type Info
}
}
catch {
Write-Log -Message "Failed to delete App: $($App.displayName), id=$App, version=$($App.displayVersion)" -Type Error
Write-Log -Message $_.ErrorDetails.Message -Type Error
}
}
Write-Log -Message "--- End $ScriptName ---"
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment