Skip to content

Commit 4dc5de7

Browse files
Merge pull request KelvinTegelaar#1722 from kris6673/appConfigProtection
Feat: Intune assignments overhaul
2 parents d529de4 + 6179443 commit 4dc5de7

14 files changed

+710
-168
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/Applications/Invoke-ListApps.ps1

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Function Invoke-ListApps {
1+
function Invoke-ListApps {
22
<#
33
.FUNCTIONALITY
44
Entrypoint
@@ -10,7 +10,57 @@ Function Invoke-ListApps {
1010
# Interact with query parameters or the body of the request.
1111
$TenantFilter = $Request.Query.TenantFilter
1212
try {
13-
$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?`$top=999&`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName&" -tenantid $TenantFilter
13+
# Use bulk requests to get groups and apps with assignments
14+
$BulkRequests = @(
15+
@{
16+
id = 'Groups'
17+
method = 'GET'
18+
url = '/groups?$top=999&$select=id,displayName'
19+
}
20+
@{
21+
id = 'Apps'
22+
method = 'GET'
23+
url = "/deviceAppManagement/mobileApps?`$top=999&`$expand=assignments&`$filter=(microsoft.graph.managedApp/appAvailability%20eq%20null%20or%20microsoft.graph.managedApp/appAvailability%20eq%20%27lineOfBusiness%27%20or%20isAssigned%20eq%20true)&`$orderby=displayName"
24+
}
25+
)
26+
27+
$BulkResults = New-GraphBulkRequest -Requests $BulkRequests -tenantid $TenantFilter
28+
29+
# Extract groups for resolving assignment names
30+
$Groups = ($BulkResults | Where-Object { $_.id -eq 'Groups' }).body.value
31+
$Apps = ($BulkResults | Where-Object { $_.id -eq 'Apps' }).body.value
32+
33+
$GraphRequest = foreach ($App in $Apps) {
34+
# Process assignments
35+
$AppAssignment = [System.Collections.Generic.List[string]]::new()
36+
$AppExclude = [System.Collections.Generic.List[string]]::new()
37+
38+
if ($App.assignments) {
39+
foreach ($Assignment in $App.assignments) {
40+
$target = $Assignment.target
41+
$intent = $Assignment.intent
42+
$intentSuffix = if ($intent) { " ($intent)" } else { '' }
43+
44+
switch ($target.'@odata.type') {
45+
'#microsoft.graph.allDevicesAssignmentTarget' { $AppAssignment.Add("All Devices$intentSuffix") }
46+
'#microsoft.graph.allLicensedUsersAssignmentTarget' { $AppAssignment.Add("All Licensed Users$intentSuffix") }
47+
'#microsoft.graph.groupAssignmentTarget' {
48+
$groupName = ($Groups | Where-Object { $_.id -eq $target.groupId }).displayName
49+
if ($groupName) { $AppAssignment.Add("$groupName$intentSuffix") }
50+
}
51+
'#microsoft.graph.exclusionGroupAssignmentTarget' {
52+
$groupName = ($Groups | Where-Object { $_.id -eq $target.groupId }).displayName
53+
if ($groupName) { $AppExclude.Add($groupName) }
54+
}
55+
}
56+
}
57+
}
58+
59+
$App | Add-Member -NotePropertyName 'AppAssignment' -NotePropertyValue ($AppAssignment -join ', ') -Force
60+
$App | Add-Member -NotePropertyName 'AppExclude' -NotePropertyValue ($AppExclude -join ', ') -Force
61+
$App
62+
}
63+
1464
$StatusCode = [HttpStatusCode]::OK
1565
} catch {
1666
$ErrorMessage = Get-NormalizedError -Message $_.Exception.Message

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddIntuneTemplate.ps1

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Function Invoke-AddIntuneTemplate {
1+
function Invoke-AddIntuneTemplate {
22
<#
33
.FUNCTIONALITY
44
Entrypoint,AnyTenant
@@ -9,10 +9,12 @@ Function Invoke-AddIntuneTemplate {
99
param($Request, $TriggerMetadata)
1010

1111
$APIName = $Request.Params.CIPPEndpoint
12+
$Headers = $Request.Headers
13+
1214
$GUID = (New-Guid).GUID
1315
try {
1416
if ($Request.Body.RawJSON) {
15-
if (!$Request.Body.displayName) { throw 'You must enter a displayname' }
17+
if (!$Request.Body.displayName) { throw 'You must enter a displayName' }
1618
if ($null -eq ($Request.Body.RawJSON | ConvertFrom-Json)) { throw 'the JSON is invalid' }
1719

1820

@@ -30,9 +32,10 @@ Function Invoke-AddIntuneTemplate {
3032
RowKey = "$GUID"
3133
PartitionKey = 'IntuneTemplate'
3234
}
33-
Write-LogMessage -headers $Request.Headers -API $APINAME -message "Created intune policy template named $($Request.Body.displayName) with GUID $GUID" -Sev 'Debug'
35+
Write-LogMessage -headers $Headers -API $APIName -message "Created intune policy template named $($Request.Body.displayName) with GUID $GUID" -Sev 'Debug'
3436

35-
$body = [pscustomobject]@{'Results' = 'Successfully added template' }
37+
$Result = 'Successfully added template'
38+
$StatusCode = [HttpStatusCode]::OK
3639
} else {
3740
$TenantFilter = $Request.Body.tenantFilter ?? $Request.Query.tenantFilter
3841
$URLName = $Request.Body.URLName ?? $Request.Query.URLName
@@ -54,19 +57,21 @@ Function Invoke-AddIntuneTemplate {
5457
RowKey = "$GUID"
5558
PartitionKey = 'IntuneTemplate'
5659
}
57-
Write-LogMessage -headers $Request.Headers -API $APINAME -message "Created intune policy template $($Request.Body.displayName) with GUID $GUID using an original policy from a tenant" -Sev 'Debug'
60+
Write-LogMessage -headers $Headers -API $APIName -message "Created intune policy template $($Request.Body.displayName) with GUID $GUID using an original policy from a tenant" -Sev 'Debug'
5861

59-
$body = [pscustomobject]@{'Results' = 'Successfully added template' }
62+
$Result = 'Successfully added template'
63+
$StatusCode = [HttpStatusCode]::OK
6064
}
6165
} catch {
62-
Write-LogMessage -headers $Request.Headers -API $APINAME -message "Intune Template Deployment failed: $($_.Exception.Message)" -Sev 'Error'
63-
$body = [pscustomobject]@{'Results' = "Intune Template Deployment failed: $($_.Exception.Message)" }
66+
$StatusCode = [HttpStatusCode]::InternalServerError
67+
$ErrorMessage = Get-CippException -Exception $_
68+
$Result = "Intune Template Deployment failed: $($ErrorMessage.NormalizedMessage)"
69+
Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev 'Error' -LogData $ErrorMessage
6470
}
6571

6672

6773
return ([HttpResponseContext]@{
68-
StatusCode = [HttpStatusCode]::OK
69-
Body = $body
74+
StatusCode = $StatusCode
75+
Body = @{'Results' = $Result }
7076
})
71-
7277
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Endpoint/MEM/Invoke-AddPolicy.ps1

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,43 @@ function Invoke-AddPolicy {
99
param($Request, $TriggerMetadata)
1010

1111
$APIName = $Request.Params.CIPPEndpoint
12+
$Headers = $Request.Headers
1213
$Tenants = $Request.Body.tenantFilter.value ? $Request.Body.tenantFilter.value : $Request.Body.tenantFilter
13-
if ('AllTenants' -in $Tenants) { $Tetnants = (Get-Tenants).defaultDomainName }
14-
$displayname = $Request.Body.displayName
14+
if ('AllTenants' -in $Tenants) { $Tenants = (Get-Tenants).defaultDomainName }
15+
16+
$DisplayName = $Request.Body.displayName
1517
$description = $Request.Body.Description
1618
$AssignTo = if ($Request.Body.AssignTo -ne 'on') { $Request.Body.AssignTo }
1719
$ExcludeGroup = $Request.Body.excludeGroup
18-
$Request.body.customGroup ? ($AssignTo = $Request.body.customGroup) : $null
20+
$Request.Body.customGroup ? ($AssignTo = $Request.Body.customGroup) : $null
1921
$RawJSON = $Request.Body.RAWJson
2022

21-
$results = foreach ($Tenant in $tenants) {
22-
if ($Request.Body.replacemap.$tenant) {
23-
([pscustomobject]$Request.Body.replacemap.$tenant).psobject.properties | ForEach-Object { $RawJson = $RawJson -replace $_.name, $_.value }
23+
$Results = foreach ($Tenant in $Tenants) {
24+
if ($Request.Body.replacemap.$Tenant) {
25+
([pscustomobject]$Request.Body.replacemap.$Tenant).PSObject.Properties | ForEach-Object { $RawJSON = $RawJSON -replace $_.name, $_.value }
2426
}
2527
try {
2628
Write-Host 'Calling Adding policy'
27-
Set-CIPPIntunePolicy -TemplateType $Request.body.TemplateType -Description $description -DisplayName $displayname -RawJSON $RawJSON -AssignTo $AssignTo -ExcludeGroup $ExcludeGroup -tenantFilter $Tenant -Headers $Request.Headers
28-
Write-LogMessage -headers $Request.Headers -API $APINAME -tenant $($Tenant) -message "Added policy $($Displayname)" -Sev 'Info'
29+
$params = @{
30+
TemplateType = $Request.Body.TemplateType
31+
Description = $description
32+
DisplayName = $DisplayName
33+
RawJSON = $RawJSON
34+
AssignTo = $AssignTo
35+
ExcludeGroup = $ExcludeGroup
36+
tenantFilter = $Tenant
37+
Headers = $Headers
38+
APIName = $APIName
39+
}
40+
Set-CIPPIntunePolicy @params
2941
} catch {
3042
"$($_.Exception.Message)"
31-
Write-LogMessage -headers $Request.Headers -API $APINAME -tenant $($Tenant) -message "Failed adding policy $($Displayname). Error: $($_.Exception.Message)" -Sev 'Error'
3243
continue
3344
}
34-
3545
}
3646

37-
$body = [pscustomobject]@{'Results' = @($results) }
38-
3947
return ([HttpResponseContext]@{
4048
StatusCode = [HttpStatusCode]::OK
41-
Body = $body
49+
Body = @{'Results' = @($Results) }
4250
})
43-
4451
}
Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Function Invoke-ExecAssignPolicy {
1+
function Invoke-ExecAssignPolicy {
22
<#
33
.FUNCTIONALITY
44
Entrypoint
@@ -10,44 +10,95 @@ Function Invoke-ExecAssignPolicy {
1010

1111
$APIName = $Request.Params.CIPPEndpoint
1212
$Headers = $Request.Headers
13-
Write-LogMessage -headers $Request.Headers -API $APINAME -message 'Accessed this API' -Sev 'Debug'
1413

1514
# Interact with the body of the request
1615
$TenantFilter = $Request.Body.tenantFilter
1716
$ID = $request.Body.ID
1817
$Type = $Request.Body.Type
1918
$AssignTo = $Request.Body.AssignTo
19+
$PlatformType = $Request.Body.platformType
20+
$ExcludeGroup = $Request.Body.excludeGroup
21+
$GroupIdsRaw = $Request.Body.GroupIds
22+
$GroupNamesRaw = $Request.Body.GroupNames
23+
$AssignmentMode = $Request.Body.assignmentMode
24+
$AssignmentFilterName = $Request.Body.AssignmentFilterName
25+
$AssignmentFilterType = $Request.Body.AssignmentFilterType
26+
27+
# Standardize GroupIds input (can be array or comma-separated string)
28+
function Get-StandardizedList {
29+
param($InputObject)
30+
if ($null -eq $InputObject -or ($InputObject -is [string] -and [string]::IsNullOrWhiteSpace($InputObject))) {
31+
return @()
32+
}
33+
if ($InputObject -is [string]) {
34+
return ($InputObject -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
35+
}
36+
if ($InputObject -is [System.Collections.IEnumerable]) {
37+
return @($InputObject | Where-Object { $_ })
38+
}
39+
return @($InputObject)
40+
}
41+
42+
$GroupIds = Get-StandardizedList -InputObject $GroupIdsRaw
43+
$GroupNames = Get-StandardizedList -InputObject $GroupNamesRaw
44+
45+
# Validate and default AssignmentMode
46+
if ([string]::IsNullOrWhiteSpace($AssignmentMode)) {
47+
$AssignmentMode = 'replace'
48+
}
2049

2150
$AssignTo = if ($AssignTo -ne 'on') { $AssignTo }
2251

23-
$results = try {
24-
if ($AssignTo) {
25-
$AssignmentResult = Set-CIPPAssignedPolicy -PolicyId $ID -TenantFilter $TenantFilter -GroupName $AssignTo -Type $Type -Headers $Headers
26-
if ($AssignmentResult) {
27-
# Check if it's a warning message (no groups found)
28-
if ($AssignmentResult -like "*No groups found*") {
29-
$StatusCode = [HttpStatusCode]::BadRequest
30-
} else {
31-
$StatusCode = [HttpStatusCode]::OK
32-
}
33-
$AssignmentResult
34-
} else {
35-
$StatusCode = [HttpStatusCode]::OK
36-
"Successfully edited policy for $($TenantFilter)"
52+
$Results = try {
53+
if ($AssignTo -or @($GroupIds).Count -gt 0) {
54+
$params = @{
55+
PolicyId = $ID
56+
TenantFilter = $TenantFilter
57+
GroupName = $AssignTo
58+
Type = $Type
59+
Headers = $Headers
60+
AssignmentMode = $AssignmentMode
61+
}
62+
63+
if (@($GroupIds).Count -gt 0) {
64+
$params.GroupIds = @($GroupIds)
65+
}
66+
67+
if (@($GroupNames).Count -gt 0) {
68+
$params.GroupNames = @($GroupNames)
69+
}
70+
71+
if (-not [string]::IsNullOrWhiteSpace($PlatformType)) {
72+
$params.PlatformType = $PlatformType
3773
}
74+
75+
if (-not [string]::IsNullOrWhiteSpace($ExcludeGroup)) {
76+
$params.ExcludeGroup = $ExcludeGroup
77+
}
78+
79+
if (-not [string]::IsNullOrWhiteSpace($AssignmentFilterName)) {
80+
$params.AssignmentFilterName = $AssignmentFilterName
81+
}
82+
83+
if (-not [string]::IsNullOrWhiteSpace($AssignmentFilterType)) {
84+
$params.AssignmentFilterType = $AssignmentFilterType
85+
}
86+
87+
Set-CIPPAssignedPolicy @params
88+
$StatusCode = [HttpStatusCode]::OK
3889
} else {
90+
'No assignments specified. No action taken.'
3991
$StatusCode = [HttpStatusCode]::OK
40-
"Successfully edited policy for $($TenantFilter)"
4192
}
4293
} catch {
94+
"$($_.Exception.Message)"
4395
$StatusCode = [HttpStatusCode]::InternalServerError
44-
"Failed to add policy for $($TenantFilter): $($_.Exception.Message)"
4596
}
4697

4798

4899
return ([HttpResponseContext]@{
49100
StatusCode = $StatusCode
50-
Body = @{Results = $results }
101+
Body = @{Results = $Results }
51102
})
52103

53104
}

0 commit comments

Comments
 (0)