Skip to content

Commit 51a90fd

Browse files
Merge pull request KelvinTegelaar#1721 from KelvinTegelaar/dev
Dev to hf
2 parents 7946c42 + 56cc68e commit 51a90fd

File tree

8 files changed

+200
-93
lines changed

8 files changed

+200
-93
lines changed

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Standards/Push-CIPPStandard.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ function Push-CIPPStandard {
1414
Write-Information "We'll be running $FunctionName"
1515

1616
if ($Standard -in @('IntuneTemplate', 'ConditionalAccessTemplate')) {
17-
$API = "$Standard_$($Item.templateId)_$($Item.Settings.TemplateList.value)"
17+
$API = "$($Standard)_$($Item.templateId)_$($Item.Settings.TemplateList.value)"
1818
} else {
19-
$API = "$Standard_$($Item.templateId)"
19+
$API = "$($Standard)_$($Item.templateId)"
2020
}
2121

2222
$Rerun = Test-CIPPRerun -Type Standard -Tenant $Tenant -API $API

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

Lines changed: 75 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Function Invoke-ExecAssignApp {
1+
function Invoke-ExecAssignApp {
22
<#
33
.FUNCTIONALITY
44
Entrypoint
@@ -16,37 +16,91 @@ Function Invoke-ExecAssignApp {
1616
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter
1717
$appFilter = $Request.Query.ID ?? $Request.Body.ID
1818
$AssignTo = $Request.Query.AssignTo ?? $Request.Body.AssignTo
19-
$AssignBody = switch ($AssignTo) {
19+
$Intent = $Request.Query.Intent ?? $Request.Body.Intent
20+
$AppType = $Request.Query.AppType ?? $Request.Body.AppType
21+
$GroupNamesRaw = $Request.Query.GroupNames ?? $Request.Body.GroupNames
22+
$GroupIdsRaw = $Request.Query.GroupIds ?? $Request.Body.GroupIds
23+
$AssignmentMode = $Request.Body.assignmentMode
2024

21-
'AllUsers' {
22-
@'
23-
{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null}]}
24-
'@
25+
$Intent = if ([string]::IsNullOrWhiteSpace($Intent)) { 'Required' } else { $Intent }
26+
27+
if ([string]::IsNullOrWhiteSpace($AssignmentMode)) {
28+
$AssignmentMode = 'replace'
29+
} else {
30+
$AssignmentMode = $AssignmentMode.ToLower()
31+
if ($AssignmentMode -notin @('replace', 'append')) {
32+
throw "Unsupported AssignmentMode value '$AssignmentMode'. Valid options are 'replace' or 'append'."
2533
}
34+
}
35+
36+
function Get-StandardizedAssignmentList {
37+
param($InputObject)
2638

27-
'AllDevices' {
28-
@'
29-
{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]}
30-
'@
39+
if ($null -eq $InputObject -or ($InputObject -is [string] -and [string]::IsNullOrWhiteSpace($InputObject))) {
40+
return @()
41+
}
42+
if ($InputObject -is [string]) {
43+
return ($InputObject -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
44+
}
45+
if ($InputObject -is [System.Collections.IEnumerable]) {
46+
return @($InputObject | Where-Object { $_ })
3147
}
48+
return @($InputObject)
49+
}
50+
51+
$GroupNames = Get-StandardizedAssignmentList -InputObject $GroupNamesRaw
52+
$GroupIds = Get-StandardizedAssignmentList -InputObject $GroupIdsRaw
53+
54+
if (-not $AssignTo -and $GroupIds.Count -eq 0 -and $GroupNames.Count -eq 0) {
55+
throw 'No assignment target provided. Supply AssignTo, GroupNames, or GroupIds.'
56+
}
3257

33-
'Both' {
34-
@'
35-
{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null},{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]}
36-
'@
58+
# Try to get the application type if not provided. Mostly just useful for ppl using the API that dont know the application type.
59+
if (-not $AppType) {
60+
try {
61+
$AppMetadata = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter" -tenantid $TenantFilter
62+
$odataType = $AppMetadata.'@odata.type'
63+
if ($odataType) {
64+
$AppType = ($odataType -replace '#microsoft.graph.', '') -replace 'App$'
65+
}
66+
} catch {
67+
Write-Warning "Unable to resolve application type for $appFilter. Continuing without assignment settings."
3768
}
69+
}
70+
71+
$targetLabel = if ($AssignTo) {
72+
$AssignTo
73+
} elseif ($GroupNames.Count -gt 0) {
74+
($GroupNames -join ', ')
75+
} elseif ($GroupIds.Count -gt 0) {
76+
"GroupIds: $($GroupIds -join ',')"
77+
} else {
78+
'CustomGroupAssignment'
79+
}
80+
81+
$setParams = @{
82+
ApplicationId = $appFilter
83+
TenantFilter = $TenantFilter
84+
Intent = $Intent
85+
APIName = $APIName
86+
Headers = $Headers
87+
GroupName = ($AssignTo ? $AssignTo : $targetLabel)
88+
AssignmentMode = $AssignmentMode
89+
}
3890

91+
if ($AppType) {
92+
$setParams.AppType = $AppType
3993
}
94+
95+
if ($GroupIds.Count -gt 0) {
96+
$setParams.GroupIds = $GroupIds
97+
}
98+
4099
try {
41-
$null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter/assign" -tenantid $TenantFilter -body $AssignBody
42-
$Result = "Successfully assigned app $($appFilter) to $($AssignTo)"
43-
Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message $Result -Sev Info
100+
$Result = Set-CIPPAssignedApplication @setParams
44101
$StatusCode = [HttpStatusCode]::OK
45-
46102
} catch {
47-
$ErrorMessage = Get-CippException -Exception $_
48-
$Result = "Failed to assign app $($appFilter) to $($AssignTo). Error: $($ErrorMessage.NormalizedError)"
49-
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev 'Error' -LogData $ErrorMessage
103+
$Result = $_.Exception.Message
50104
$StatusCode = [HttpStatusCode]::InternalServerError
51105
}
52106

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Tenant/Standards/Invoke-ListStandardsCompare.ps1

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,19 @@ function Invoke-ListStandardsCompare {
1111

1212
$Table = Get-CIPPTable -TableName 'CippStandardsReports'
1313
$TenantFilter = $Request.Query.tenantFilter
14+
$TemplateFilter = $Request.Query.templateId
15+
16+
$Filters = [system.collections.generic.list[string]]::new()
1417
if ($TenantFilter) {
15-
$Table.Filter = "PartitionKey eq '{0}'" -f $TenantFilter
18+
$Filters.Add("PartitionKey eq '{0}'" -f $TenantFilter)
19+
}
20+
if ($TemplateFilter) {
21+
$Filters.Add("TemplateId eq '{0}'" -f $TemplateFilter)
1622
}
23+
$Filter = $Filters -join ' and '
1724

1825
$Tenants = Get-Tenants -IncludeErrors
19-
$Standards = Get-CIPPAzDataTableEntity @Table | Where-Object { $_.PartitionKey -in $Tenants.defaultDomainName }
20-
21-
#in the results we have objects starting with "standards." All these have to be converted from JSON. Do not do this is its a boolean
22-
<#$Results | ForEach-Object {
23-
$Object = $_
24-
$Object.PSObject.Properties | ForEach-Object {
25-
if ($_.Name -like 'standards_*') {
26-
if ($_.Value -is [System.Boolean]) {
27-
$_.Value = [bool]$_.Value
28-
} elseif ($_.Value -like '*{*') {
29-
$_.Value = ConvertFrom-Json -InputObject $_.Value -ErrorAction SilentlyContinue
30-
} else {
31-
$_.Value = [string]$_.Value
32-
}
33-
34-
$Key = $_.Name.replace('standards_', 'standards.')
35-
$Key = $Key.replace('IntuneTemplate_', 'IntuneTemplate.')
36-
$Key = $Key -replace '__', '-'
37-
38-
$object | Add-Member -MemberType NoteProperty -Name $Key -Value $_.Value -Force
39-
$object.PSObject.Properties.Remove($_.Name)
40-
}
41-
}
42-
}#>
26+
$Standards = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.PartitionKey -in $Tenants.defaultDomainName }
4327

4428
$TenantStandards = @{}
4529
$Results = [System.Collections.Generic.List[object]]::new()
@@ -75,6 +59,7 @@ function Invoke-ListStandardsCompare {
7559
$TenantStandards[$Tenant][$FieldName] = @{
7660
Value = $FieldValue
7761
LastRefresh = $Standard.TimeStamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
62+
TemplateId = $Standard.TemplateId
7863
}
7964
}
8065

Modules/CIPPCore/Public/Set-CIPPAssignedApplication.ps1

Lines changed: 107 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,36 @@ function Set-CIPPAssignedApplication {
66
$AppType,
77
$ApplicationId,
88
$TenantFilter,
9+
$GroupIds,
10+
$AssignmentMode = 'replace',
911
$APIName = 'Assign Application',
1012
$Headers
1113
)
1214
Write-Host "GroupName: $GroupName Intent: $Intent AppType: $AppType ApplicationId: $ApplicationId TenantFilter: $TenantFilter APIName: $APIName"
1315
try {
16+
$assignmentSettings = $null
17+
if ($AppType) {
18+
$assignmentSettings = @{
19+
'@odata.type' = "#microsoft.graph.$($AppType)AppAssignmentSettings"
20+
}
21+
22+
switch ($AppType) {
23+
'Win32Lob' {
24+
$assignmentSettings.notifications = 'hideAll'
25+
}
26+
'WinGet' {
27+
$assignmentSettings.notifications = 'hideAll'
28+
}
29+
'macOsVpp' {
30+
$assignmentSettings.useDeviceLicensing = $true
31+
}
32+
default {
33+
# No additional settings
34+
}
35+
}
36+
}
37+
38+
# Build the assignment object
1439
$MobileAppAssignment = switch ($GroupName) {
1540
'AllUsers' {
1641
@(@{
@@ -19,12 +44,7 @@ function Set-CIPPAssignedApplication {
1944
'@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget'
2045
}
2146
intent = $Intent
22-
settings = @{
23-
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
24-
notifications = 'hideAll'
25-
installTimeSettings = $null
26-
restartSettings = $null
27-
}
47+
settings = $assignmentSettings
2848
})
2949
break
3050
}
@@ -35,12 +55,7 @@ function Set-CIPPAssignedApplication {
3555
'@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget'
3656
}
3757
intent = $Intent
38-
settings = @{
39-
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
40-
notifications = 'hideAll'
41-
installTimeSettings = $null
42-
restartSettings = $null
43-
}
58+
settings = $assignmentSettings
4459
})
4560
break
4661
}
@@ -52,71 +67,121 @@ function Set-CIPPAssignedApplication {
5267
'@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget'
5368
}
5469
intent = $Intent
55-
settings = @{
56-
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
57-
notifications = 'hideAll'
58-
installTimeSettings = $null
59-
restartSettings = $null
60-
}
70+
settings = $assignmentSettings
6171
},
6272
@{
6373
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
6474
target = @{
6575
'@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget'
6676
}
6777
intent = $Intent
68-
settings = @{
69-
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
70-
notifications = 'hideAll'
71-
installTimeSettings = $null
72-
restartSettings = $null
73-
}
78+
settings = $assignmentSettings
7479
}
7580
)
7681
}
7782
default {
78-
$GroupNames = $GroupName.Split(',')
79-
$GroupIds = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $TenantFilter | ForEach-Object {
80-
$Group = $_
81-
foreach ($SingleName in $GroupNames) {
82-
if ($_.displayname -like $SingleName) {
83-
$group.id
83+
$resolvedGroupIds = @()
84+
if ($PSBoundParameters.ContainsKey('GroupIds') -and $GroupIds) {
85+
$resolvedGroupIds = $GroupIds
86+
} else {
87+
$GroupNames = $GroupName.Split(',')
88+
$resolvedGroupIds = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $TenantFilter | ForEach-Object {
89+
$Group = $_
90+
foreach ($SingleName in $GroupNames) {
91+
if ($_.displayName -like $SingleName) {
92+
$group.id
93+
}
8494
}
8595
}
96+
Write-Information "found $($resolvedGroupIds) groups"
8697
}
87-
Write-Information "found $($GroupIds) groups"
88-
foreach ($Group in $GroupIds) {
98+
99+
# We ain't found nothing so we panic
100+
if (-not $resolvedGroupIds) {
101+
throw 'No matching groups resolved for assignment request.'
102+
}
103+
104+
foreach ($Group in $resolvedGroupIds) {
89105
@{
90106
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
91107
target = @{
92108
'@odata.type' = '#microsoft.graph.groupAssignmentTarget'
93109
groupId = $Group
94110
}
95111
intent = $Intent
96-
settings = @{
97-
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
98-
notifications = 'hideAll'
99-
installTimeSettings = $null
100-
restartSettings = $null
112+
settings = $assignmentSettings
113+
}
114+
}
115+
}
116+
}
117+
118+
# If we're appending, we need to get existing assignments
119+
if ($AssignmentMode -eq 'append') {
120+
try {
121+
$ExistingAssignments = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($ApplicationId)/assignments" -tenantid $TenantFilter
122+
} catch {
123+
Write-Warning "Unable to retrieve existing assignments for $ApplicationId. Proceeding with new assignments only. Error: $($_.Exception.Message)"
124+
$ExistingAssignments = @()
125+
}
126+
}
127+
128+
# Deduplicate current assignments so the new ones override existing ones
129+
if ($ExistingAssignments) {
130+
$ExistingAssignments = $ExistingAssignments | ForEach-Object {
131+
$ExistingAssignment = $_
132+
switch ($ExistingAssignment.target.'@odata.type') {
133+
'#microsoft.graph.groupAssignmentTarget' {
134+
if ($ExistingAssignment.target.groupId -notin $MobileAppAssignment.target.groupId) {
135+
$ExistingAssignment
136+
}
137+
}
138+
default {
139+
if ($ExistingAssignment.target.'@odata.type' -notin $MobileAppAssignment.target.'@odata.type') {
140+
$ExistingAssignment
101141
}
102142
}
103143
}
104144
}
105145
}
146+
147+
$FinalAssignments = [System.Collections.Generic.List[object]]::new()
148+
if ($AssignmentMode -eq 'append' -and $ExistingAssignments) {
149+
$ExistingAssignments | ForEach-Object {
150+
$FinalAssignments.Add(@{
151+
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
152+
target = $_.target
153+
intent = $_.intent
154+
settings = $_.settings
155+
})
156+
}
157+
158+
$MobileAppAssignment | ForEach-Object {
159+
$FinalAssignments.Add(@{
160+
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
161+
target = $_.target
162+
intent = $_.intent
163+
settings = $_.settings
164+
})
165+
}
166+
} else {
167+
$FinalAssignments = $MobileAppAssignment
168+
}
169+
106170
$DefaultAssignmentObject = [PSCustomObject]@{
107171
mobileAppAssignments = @(
108-
$MobileAppAssignment
172+
$FinalAssignments
109173
)
110174
}
111175
if ($PSCmdlet.ShouldProcess($GroupName, "Assigning Application $ApplicationId")) {
112176
Start-Sleep -Seconds 1
177+
# Write-Information (ConvertTo-Json $DefaultAssignmentObject -Depth 10)
113178
$null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($ApplicationId)/assign" -tenantid $TenantFilter -type POST -body ($DefaultAssignmentObject | ConvertTo-Json -Compress -Depth 10)
114-
Write-LogMessage -headers $Headers -API $APIName -message "Assigned Application to $($GroupName)" -Sev 'Info' -tenant $TenantFilter
179+
Write-LogMessage -headers $Headers -API $APIName -message "Assigned Application $ApplicationId to $($GroupName)" -Sev 'Info' -tenant $TenantFilter
115180
}
116-
return "Assigned Application to $($GroupName)"
181+
return "Assigned Application $ApplicationId to $($GroupName)"
117182
} catch {
118183
$ErrorMessage = Get-CippException -Exception $_
119-
Write-LogMessage -headers $Headers -API $APIName -message "Could not assign application to $GroupName. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage
120-
return "Could not assign application to $GroupName. Error: $($ErrorMessage.NormalizedError)"
184+
Write-LogMessage -headers $Headers -API $APIName -message "Could not assign application $ApplicationId to $GroupName. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage
185+
throw "Could not assign application $ApplicationId to $GroupName. Error: $($ErrorMessage.NormalizedError)"
121186
}
122187
}

0 commit comments

Comments
 (0)