mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 10:55:38 +02:00
246 lines
11 KiB
PowerShell
246 lines
11 KiB
PowerShell
#ImportOrder 220
|
|
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("TypeNotFound","", Justification = "")]
|
|
class AppProtectionGroup : IntunePolicyGroupBase
|
|
{
|
|
AppProtectionGroup() : Base()
|
|
{
|
|
$this.Init()
|
|
}
|
|
|
|
Init()
|
|
{
|
|
$this._ID = "AppProtection"
|
|
$this._Name = "App protection policies"
|
|
$this._Icon = "AppProtection"
|
|
}
|
|
}
|
|
|
|
#########################################################################################
|
|
#
|
|
# App Protection
|
|
#
|
|
#########################################################################################
|
|
|
|
# region App Protection
|
|
class AppProtectionType : IntunePolicyTypeBase
|
|
{
|
|
AppProtectionType() : Base()
|
|
{
|
|
$this.Init()
|
|
}
|
|
|
|
Init()
|
|
{
|
|
$this._PolicyGroup = (Get-SingletonObject "AppProtectionGroup")
|
|
$this._PolicyName = "App protection policy"
|
|
$this._ID = "AppProtection"
|
|
$this._SubTypeColumn = "ManagementType=Management type"
|
|
# Endpoint rejects a name $filter (verified live 2026-08-27) - searches filter client-side.
|
|
$this._SupportsNameFilter = $false
|
|
$this._API = "deviceAppManagement/managedAppPolicies"
|
|
$this._Permissions = @("DeviceManagementApps.ReadWrite.All")
|
|
$this._Dependencies = @("Applications")
|
|
$this._ObjectClass = "AppProtectionPolicyObject"
|
|
$this._ExpandAssignmentsList = $false
|
|
# Assignments are fetched per platform collection - see
|
|
# GetAssignmentsBaseURL below. _AssignmentsViaExpand stays $false:
|
|
# once the URL targets a concrete subtype the plain navigation GET
|
|
# works, and it returns just the assignments instead of the whole policy.
|
|
$this._PropertiesToRemove = @('exemptAppLockerFiles')
|
|
$this._PropertiesToRemoveForUpdate = @("protectedAppLockerFiles","version") # ToDo: !!! Add support for protectedAppLockerFiles?
|
|
$this._VerifyObject = $true
|
|
# CheckPolicy is a complete @odata.type matcher (the managedAppPolicies allowlist),
|
|
# so a rejection is authoritative - do not let the folder-trust fallback rescue a
|
|
# foreign object misplaced in this type's export folder.
|
|
$this._StrictODataTypeCheck = $true
|
|
$this._Icon = "AppConfiguration"
|
|
|
|
if($null -ne $this._PolicyGroup) {
|
|
$this._PolicyGroup.AddPolicyType($this)
|
|
}
|
|
}
|
|
|
|
# managedAppPolicies is Collection(managedAppPolicy), and managedAppPolicy
|
|
# declares no navigation properties - so both {API}/{id}/assignments and
|
|
# {API}/{id}?$expand=assignments return 400 ("Could not find a property named
|
|
# 'assignments' on type 'microsoft.graph.managedAppPolicy'"). Every concrete
|
|
# subtype inherits `assignments`, so route through the per-platform collection
|
|
# (_objectClass, set in the object's constructor via Get-AppConfigurationClass).
|
|
# defaultManagedAppProtection is the exception - it has no assignments at all.
|
|
[String]GetAssignmentsBaseURL([PSCustomObject]$PolicyObject)
|
|
{
|
|
if(-not $PolicyObject._objectClass) { return $this._API }
|
|
if($PolicyObject._objectClass -eq "defaultManagedAppProtections") { return $null }
|
|
|
|
return "deviceAppManagement/$($PolicyObject._objectClass)"
|
|
}
|
|
|
|
[Hashtable]PreImportCommand([PSCustomObject]$PolicyObject)
|
|
{
|
|
# apps is a navigation property set later via targetApps (PostImportCommand),
|
|
# not an inline body property - strip it from the POST body.
|
|
Remove-Property $PolicyObject.JsonObject "apps"
|
|
Remove-Property $PolicyObject.JsonObject "apps@odata.context"
|
|
|
|
# The polymorphic managedAppPolicies collection (used for listing) rejects
|
|
# POST, so import must target the per-platform collection. _objectClass is
|
|
# the metadata-derived endpoint segment (e.g. iosManagedAppProtections),
|
|
# set on the object at construction via Get-AppConfigurationClass.
|
|
if($PolicyObject._objectClass)
|
|
{
|
|
return @{"API"="deviceAppManagement/$($PolicyObject._objectClass)"}
|
|
}
|
|
|
|
return (@{})
|
|
}
|
|
|
|
PostImportCommand([PSCustomObject]$PolicyObject, [PSCustomObject]$SourceObject)
|
|
{
|
|
if($SourceObject.Object.Apps) {
|
|
# No "@odata.type" on the created object so reload new object
|
|
#$newObject = (Invoke-MSGraphAPI "$($objectType.API)?`$filter=id eq '$($obj.Id)'").Value
|
|
$newObject = Invoke-MSGraphAPI -Url "$($this.API)/$($PolicyObject.Id)" -TokenId $PolicyObject._TokenID
|
|
if($newObject)
|
|
{
|
|
try
|
|
{
|
|
$apps = [PSCustomObject]@{
|
|
appGroupType = $PolicyObject.Object.appGroupType
|
|
apps = @($SourceObject.Object.Apps)
|
|
}
|
|
$json = $apps | ConvertTo-Json -Depth 20
|
|
|
|
# Created object carries no @odata.type; use the source object's
|
|
# metadata-derived endpoint segment (_objectClass).
|
|
if($SourceObject._objectClass)
|
|
{
|
|
Invoke-MSGraphAPI -Url "deviceAppManagement/$($SourceObject._objectClass)/$($PolicyObject.Id)/targetApps" -Content $json -HttpMethod POST -TokenId $PolicyObject._TokenID | Out-Null
|
|
}
|
|
}
|
|
catch {
|
|
# The policy was created; only the targetApps association failed. Keep
|
|
# going but make the partial import visible instead of swallowing it.
|
|
Write-LogError "Failed to assign target apps to imported App protection policy '$($PolicyObject.displayName)' ($($PolicyObject.Id)) via deviceAppManagement/$($SourceObject._objectClass)/$($PolicyObject.Id)/targetApps" $_.Exception
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Hashtable]PreImportAssignmentsCommand([PSCustomObject]$PolicyObject, [PSCustomObject]$SourceObject)
|
|
{
|
|
# /assign is bound to the concrete platform subtype; the polymorphic
|
|
# managedAppPolicies collection returns 400 for the assign action.
|
|
if($SourceObject._objectClass)
|
|
{
|
|
return @{"API"="deviceAppManagement/$($SourceObject._objectClass)/$($PolicyObject.Id)/assign"}
|
|
}
|
|
return $null
|
|
}
|
|
|
|
[Hashtable]PreUpdateCommand([PSCustomObject]$PolicyObject, [PSCustomObject]$ExistingObject)
|
|
{
|
|
# managedAppPolicies rejects PATCH, and apps is a navigation property set
|
|
# via targetApps rather than inline. Re-post apps to the per-platform
|
|
# endpoint, strip them from the PATCH body, then PATCH that endpoint.
|
|
#
|
|
# Routing note: an imported object's POST response carries no
|
|
# @odata.type, so ITS _objectClass can be null - the update object came
|
|
# from a file that always has the type, so prefer that one.
|
|
if(-not $ExistingObject._objectClass -and $PolicyObject._objectClass)
|
|
{
|
|
$ExistingObject._objectClass = $PolicyObject._objectClass
|
|
}
|
|
|
|
if($PolicyObject.JsonObject.apps -and $ExistingObject._objectClass)
|
|
{
|
|
try
|
|
{
|
|
$apps = [PSCustomObject]@{
|
|
appGroupType = $PolicyObject.JsonObject.appGroupType
|
|
apps = @($PolicyObject.JsonObject.apps)
|
|
}
|
|
$json = $apps | ConvertTo-Json -Depth 20
|
|
Invoke-MSGraphAPI -Url "deviceAppManagement/$($ExistingObject._objectClass)/$($ExistingObject.Id)/targetApps" -Content $json -HttpMethod POST -TokenId $ExistingObject._TokenID | Out-Null
|
|
}
|
|
catch {
|
|
# The policy PATCH still proceeds; surface the failed targetApps update.
|
|
Write-LogError "Failed to update target apps for App protection policy '$($ExistingObject.displayName)' ($($ExistingObject.Id)) via deviceAppManagement/$($ExistingObject._objectClass)/$($ExistingObject.Id)/targetApps" $_.Exception
|
|
}
|
|
}
|
|
Remove-Property $PolicyObject.JsonObject "apps"
|
|
Remove-Property $PolicyObject.JsonObject "apps@odata.context"
|
|
# assignments is a navigation property (managed via the /assign action),
|
|
# not inline-PATCHable - PATCHing it 400s on the platform entity type.
|
|
Remove-Property $PolicyObject.JsonObject "assignments"
|
|
Remove-Property $PolicyObject.JsonObject "assignments@odata.context"
|
|
|
|
if($ExistingObject._objectClass)
|
|
{
|
|
return @{"API"="deviceAppManagement/$($ExistingObject._objectClass)/$($ExistingObject.Id)"}
|
|
}
|
|
return $null
|
|
}
|
|
|
|
[Boolean]CheckPolicy([PSCustomObject]$PolicyObject)
|
|
{
|
|
# App Protection owns the polymorphic deviceAppManagement/managedAppPolicies
|
|
# collection: the per-platform *ManagedAppProtection variants (ios / android /
|
|
# windows / default) plus the two Windows Information Protection policy types.
|
|
# An explicit allowlist is required: file objects carry only @odata.type (no
|
|
# top-level @odata.id), so this runs as the file->type discriminator. A previous
|
|
# "accept everything except targetedManagedAppConfiguration" greedily claimed
|
|
# unrelated policy types (Compliance, CA, etc.) when resolving from an export
|
|
# folder. targetedManagedAppConfiguration is App Config, not App Protection
|
|
# (see AppConfigurationManagedAppType.CheckPolicy).
|
|
$odata = [string]$PolicyObject.'@odata.type'
|
|
if($odata -match 'ManagedAppProtection$' -or
|
|
$odata -eq '#microsoft.graph.mdmWindowsInformationProtectionPolicy' -or
|
|
$odata -eq '#microsoft.graph.windowsInformationProtectionPolicy')
|
|
{
|
|
return $true
|
|
}
|
|
|
|
return $false
|
|
}
|
|
}
|
|
|
|
Class AppProtectionPolicyObject : IntunePolicyBase
|
|
{
|
|
Hidden [string]$_objectClass = $null
|
|
Hidden [string]$_managemntType = $null
|
|
|
|
AppProtectionPolicyObject([PSCustomObject]$JsonObj) : Base($JsonObj) { $this.Init() }
|
|
|
|
AppProtectionPolicyObject() : Base()
|
|
{
|
|
$this.Init()
|
|
}
|
|
|
|
Hidden Init()
|
|
{
|
|
$this._PolicyType = (Get-SingletonObject "AppProtectionType")
|
|
|
|
if($this.Object."@odata.type" -eq "#microsoft.graph.mdmWindowsInformationProtectionPolicy") {
|
|
$this._managemntType = "With enrollment"
|
|
}
|
|
elseif($this.Object."@odata.type" -eq "#microsoft.graph.windowsInformationProtectionPolicy") {
|
|
$this._managemntType = "Without enrollment"
|
|
}
|
|
else {
|
|
$this._managemntType = "All app types"
|
|
}
|
|
|
|
Add-ObjectProperty $this "ManagementType" { $this._managemntType }
|
|
|
|
Get-AppConfigurationClass $this
|
|
|
|
if($this.JsonObject."@odata.type" -eq "#microsoft.graph.iosManagedAppProtection") {
|
|
$platformName = Get-LanguageString "AppProtection.iOSPlatformLabel"
|
|
if($platformName) {
|
|
#$this._PlatformName = $platformName
|
|
}
|
|
}
|
|
}
|
|
}
|