Public/Remove-IntuneImportedObject.ps1

# Auto-generated by module/Build-Module.ps1 from en/scripts/Invoke-IntuneImportCleanupFromReport.ps1
# Edit the source script, then re-run Build-Module.ps1. Do not edit this file directly.

function Remove-IntuneImportedObject {
<#
.SYNOPSIS
    Cleanup objects created by a failed Intune import attempt.

.DESCRIPTION
    Reads RAPPORT-IMPORT*.txt, extracts objects marked CREATED, connects to the target tenant,
    finds objects by name in the expected Graph collection, and deletes only candidates created in
    the supplied import time window. Default mode is preview. Add -Execute to delete.

.NOTES
    The -ImportStartUtc / -ImportEndUtc window prevents deleting pre-existing objects: only objects
    whose createdDateTime falls inside it are removed. Defaults to the last 24 hours (clean up right
    after a failed run). Pass an explicit, narrow window to clean an older import.
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true)][string]$ReportPath,
    [Parameter(Mandatory=$true)][string]$TargetTenantId,
    [datetime]$ImportStartUtc = ([datetime]::UtcNow.AddDays(-1)),
    [datetime]$ImportEndUtc = ([datetime]::UtcNow.AddMinutes(10)),
    [switch]$Execute,
    [switch]$Force,
    [string]$LogPath = (Join-Path (Get-Location) ("CleanupResults_{0}.csv" -f (Get-Date -Format 'yyyyMMdd_HHmmss')))
)

$ErrorActionPreference='Stop'
$GraphBase='https://graph.microsoft.com/beta'
$Results=New-Object System.Collections.Generic.List[object]

$Scopes=@(
 'DeviceManagementConfiguration.ReadWrite.All',
 'DeviceManagementApps.ReadWrite.All',
 'DeviceManagementManagedDevices.ReadWrite.All',
 'DeviceManagementServiceConfig.ReadWrite.All',
 'DeviceManagementScripts.ReadWrite.All',
 'DeviceManagementRBAC.ReadWrite.All'
)

$Catalog=@{
 '01_DeviceConfigurations' = @{ Path='deviceManagement/deviceConfigurations'; NameProp='displayName' }
 '02_ConfigurationPolicies'= @{ Path='deviceManagement/configurationPolicies'; NameProp='name' }
 '03_CompliancePolicies'   = @{ Path='deviceManagement/deviceCompliancePolicies'; NameProp='displayName' }
 '04_ScriptsPowerShell'    = @{ Path='deviceManagement/deviceManagementScripts'; NameProp='displayName' }
 '06_Remediations'         = @{ Path='deviceManagement/deviceHealthScripts'; NameProp='displayName' }
 '07_Filters'              = @{ Path='deviceManagement/assignmentFilters'; NameProp='displayName' }
 '08_ScopeTags'            = @{ Path='deviceManagement/roleScopeTags'; NameProp='displayName' }
 '09_Apps'                 = @{ Path='deviceAppManagement/mobileApps'; NameProp='displayName' }
 '10_AppConfigurations'    = @{ Path='deviceAppManagement/mobileAppConfigurations'; NameProp='displayName' }
 '11_AppProtection'        = @{ Path='deviceAppManagement/managedAppPolicies'; NameProp='displayName' }
 '12_AutopilotProfiles'    = @{ Path='deviceManagement/windowsAutopilotDeploymentProfiles'; NameProp='displayName' }
 '13_NotificationTemplates'= @{ Path='deviceManagement/notificationMessageTemplates'; NameProp='displayName' }
}


function Connect-GraphForIntuneAutomation {
    param(
        [Parameter(Mandatory=$true)][string]$TenantId,
        [string[]]$Scopes
    )
    Import-Module Microsoft.Graph.Authentication -ErrorAction Stop

    $tenantLower = $TenantId.ToLowerInvariant()
    $sourceTenant = $env:INTUNE_AUTO_SOURCE_TENANT_ID
    $targetTenant = $env:INTUNE_AUTO_TARGET_TENANT_ID

    $clientId = $null
    $certThumb = $null

    if ($sourceTenant -and ($tenantLower -eq $sourceTenant.ToLowerInvariant())) {
        $clientId = $env:INTUNE_AUTO_SOURCE_CLIENT_ID
        $certThumb = $env:INTUNE_AUTO_SOURCE_CERT_THUMBPRINT
    }
    if ($targetTenant -and ($tenantLower -eq $targetTenant.ToLowerInvariant())) {
        $clientId = $env:INTUNE_AUTO_TARGET_CLIENT_ID
        $certThumb = $env:INTUNE_AUTO_TARGET_CERT_THUMBPRINT
    }
    if (-not $clientId -and $env:INTUNE_AUTO_CLIENT_ID) { $clientId = $env:INTUNE_AUTO_CLIENT_ID }
    if (-not $certThumb -and $env:INTUNE_AUTO_CERT_THUMBPRINT) { $certThumb = $env:INTUNE_AUTO_CERT_THUMBPRINT }

    $ctx = Get-MgContext -ErrorAction SilentlyContinue

    if ($clientId -and $certThumb) {
        if ($ctx -and $ctx.TenantId -and ($ctx.TenantId.ToLowerInvariant() -eq $tenantLower) -and $ctx.ClientId -and ($ctx.ClientId -eq $clientId)) {
            return
        }
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
        Connect-MgGraph -TenantId $TenantId -ClientId $clientId -CertificateThumbprint $certThumb -ContextScope Process -NoWelcome | Out-Null
        return
    }

    if ($ctx -and $ctx.TenantId -and ($ctx.TenantId.ToLowerInvariant() -eq $tenantLower)) {
        $ctxScopes = @($ctx.Scopes)
        $missing = @()
        foreach ($s in @($Scopes)) {
            if ($ctxScopes -notcontains $s) { $missing += $s }
        }
        if ($missing.Count -eq 0) { return }
        Write-Host ("Missing Graph scopes for {0}: {1}. Re-authentication required." -f $TenantId, ($missing -join ', ')) -ForegroundColor Yellow
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    } elseif ($ctx) {
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    }

    Connect-MgGraph -TenantId $TenantId -Scopes $Scopes -ContextScope Process -NoWelcome | Out-Null
}

function Add-Result($Family,$Name,$Status,$Action,$GraphId,$CreatedDateTime,$Reason,$ErrorMessage){
 $Results.Add([pscustomobject]@{
  DateUtc=(Get-Date).ToUniversalTime().ToString('s')+'Z'; Family=$Family; Name=$Name; Status=$Status; Action=$Action;
  GraphId=$GraphId; CreatedDateTime=$CreatedDateTime; Reason=$Reason; ErrorMessage=$ErrorMessage
 }) | Out-Null
}

function Save-Results(){
 if($Results.Count -gt 0){ $Results | Export-Csv -Path $LogPath -NoTypeInformation -Encoding UTF8; Write-Host "Log: $LogPath" -ForegroundColor Cyan }
}

function Get-CreatedEntriesFromReport(){
 $text=Get-Content -LiteralPath $ReportPath -Raw -Encoding UTF8
 $entries=@()
 foreach($line in ($text -split "`r?`n")){
  if($line -match '^(\S+)\s+\|\s+(.+?)\s+\|\s+CREATED\s*$'){
   $entries += [pscustomobject]@{ Family=$matches[1]; Name=$matches[2].Trim() }
  }
 }
 return $entries
}

function Invoke-Graph($Method,$Uri){
 $full = if($Uri -match '^https://'){$Uri}else{"$GraphBase/$Uri"}
 Invoke-MgGraphRequest -Method $Method -Uri $full
}

function Get-AllGraphItems($Uri){
 $all=@(); $next=if($Uri -match '^https://'){$Uri}else{"$GraphBase/$Uri"}
 do{
  $resp=Invoke-MgGraphRequest -Method GET -Uri $next
  if($resp.value){$all += @($resp.value)} elseif($resp.id){$all += $resp}
  $next=$resp.'@odata.nextLink'
 } while($next)
 return $all
}

function Get-PropertyValue($Obj,[string]$Name){
 if($Obj.PSObject.Properties.Name -contains $Name){ return $Obj.PSObject.Properties[$Name].Value }
 return $null
}

$DisableScopeTags = ($env:INTUNE_AUTO_DISABLE_SCOPE_TAGS -eq '1')
if($DisableScopeTags){ $Scopes = @($Scopes | Where-Object { $_ -ne 'DeviceManagementRBAC.ReadWrite.All' }) }

try{
 if(-not (Test-Path -LiteralPath $ReportPath)){ throw "ReportPath not found: $ReportPath" }
 if(-not $Execute){ Write-Host 'PREVIEW mode: no deletion. Add -Execute to delete.' -ForegroundColor Yellow }
 Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
 Connect-GraphForIntuneAutomation -TenantId $TargetTenantId -Scopes $Scopes
 $ctx=Get-MgContext
 Write-Host "Target tenant: $($ctx.TenantId)" -ForegroundColor Cyan
 $entries=@(Get-CreatedEntriesFromReport)
 Write-Host "CREATED objects in report: $($entries.Count)" -ForegroundColor Cyan
 if($Execute -and (-not $Force)){
  $r=Read-Host "Delete objects created between $($ImportStartUtc.ToString('s'))Z and $($ImportEndUtc.ToString('s'))Z ? [Y/N]"
  if($r -notmatch '^[OoYy]'){ throw 'Cancelled by user.' }
 }
 foreach($g in ($entries | Group-Object Family)){
  if(-not $Catalog.ContainsKey($g.Name)){
   foreach($e in $g.Group){ Add-Result $e.Family $e.Name 'SKIPPED' 'NO_CATALOG' $null $null 'Family not supported by cleanup' $null }
   continue
  }
  if($DisableScopeTags -and $g.Name -eq '08_ScopeTags'){
   foreach($e in $g.Group){ Add-Result $e.Family $e.Name 'SKIPPED' 'RBAC_SCOPE_TAGS_DISABLED' $null $null 'ScopeTags read/delete skipped because DeviceManagementRBAC is unavailable or disabled.' $null }
   Write-Host "Target read: $($g.Name) skipped (RBAC ScopeTags disabled)." -ForegroundColor Yellow
   continue
  }
  $cat=$Catalog[$g.Name]
  Write-Host "Target read: $($g.Name)" -ForegroundColor Cyan
  try { $items=@(Get-AllGraphItems -Uri $cat.Path) }
  catch {
   foreach($e in $g.Group){ Add-Result $e.Family $e.Name 'SKIPPED' 'LOOKUP_FORBIDDEN_OR_FAILED' $null $null 'Target read failed; cleanup skipped for this family.' $_.Exception.Message }
   Write-Host " [SKIPPED] Target read failed for $($g.Name): $($_.Exception.Message)" -ForegroundColor Yellow
   continue
  }
  foreach($e in $g.Group){
   $matches=@($items | Where-Object { (Get-PropertyValue $_ $cat.NameProp) -eq $e.Name })
   if($matches.Count -eq 0){ Add-Result $e.Family $e.Name 'NOT_FOUND' 'LOOKUP' $null $null 'No target object with the same name' $null; continue }
   foreach($m in $matches){
    $created=Get-PropertyValue $m 'createdDateTime'
    $id=Get-PropertyValue $m 'id'
    $inWindow=$true
    if($created){
     try{ $dt=([datetime]$created).ToUniversalTime(); $inWindow=($dt -ge $ImportStartUtc.ToUniversalTime() -and $dt -le $ImportEndUtc.ToUniversalTime()) } catch { $inWindow=$false }
    } else { $inWindow=$false }
    if(-not $inWindow){ Add-Result $e.Family $e.Name 'SKIPPED' 'DATE_GUARD' $id $created 'createdDateTime outside import window or missing' $null; continue }
    if(-not $Execute){ Add-Result $e.Family $e.Name 'PREVIEW_DELETE' 'DELETE' $id $created 'Deletion candidate' $null; Write-Host " [PREVIEW DELETE] $($e.Name)" -ForegroundColor Gray; continue }
    try{ Invoke-Graph -Method DELETE -Uri ("{0}/{1}" -f $cat.Path,$id) | Out-Null; Add-Result $e.Family $e.Name 'DELETED' 'DELETE' $id $created '' $null; Write-Host " [DELETED] $($e.Name)" -ForegroundColor Green }
    catch{ Add-Result $e.Family $e.Name 'ERROR' 'DELETE' $id $created '' $_.Exception.Message; Write-Host " [X] $($e.Name): $($_.Exception.Message)" -ForegroundColor Red }
   }
  }
 }
 Save-Results
}catch{
 Save-Results
 Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
 throw
}
}