Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ function Push-CIPPStandard {
Write-Information "We'll be running $FunctionName"

if ($Standard -in @('IntuneTemplate', 'ConditionalAccessTemplate')) {
$API = "$Standard_$($Item.templateId)_$($Item.Settings.TemplateList.value)"
$API = "$($Standard)_$($Item.templateId)_$($Item.Settings.TemplateList.value)"
} else {
$API = "$Standard_$($Item.templateId)"
$API = "$($Standard)_$($Item.templateId)"
}

$Rerun = Test-CIPPRerun -Type Standard -Tenant $Tenant -API $API
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Function Invoke-ExecAssignApp {
function Invoke-ExecAssignApp {
<#
.FUNCTIONALITY
Entrypoint
Expand All @@ -16,37 +16,91 @@ Function Invoke-ExecAssignApp {
$TenantFilter = $Request.Query.tenantFilter ?? $Request.Body.tenantFilter
$appFilter = $Request.Query.ID ?? $Request.Body.ID
$AssignTo = $Request.Query.AssignTo ?? $Request.Body.AssignTo
$AssignBody = switch ($AssignTo) {
$Intent = $Request.Query.Intent ?? $Request.Body.Intent
$AppType = $Request.Query.AppType ?? $Request.Body.AppType
$GroupNamesRaw = $Request.Query.GroupNames ?? $Request.Body.GroupNames
$GroupIdsRaw = $Request.Query.GroupIds ?? $Request.Body.GroupIds
$AssignmentMode = $Request.Body.assignmentMode

'AllUsers' {
@'
{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allLicensedUsersAssignmentTarget"},"intent":"Required","settings":null}]}
'@
$Intent = if ([string]::IsNullOrWhiteSpace($Intent)) { 'Required' } else { $Intent }

if ([string]::IsNullOrWhiteSpace($AssignmentMode)) {
$AssignmentMode = 'replace'
} else {
$AssignmentMode = $AssignmentMode.ToLower()
if ($AssignmentMode -notin @('replace', 'append')) {
throw "Unsupported AssignmentMode value '$AssignmentMode'. Valid options are 'replace' or 'append'."
}
}

function Get-StandardizedAssignmentList {
param($InputObject)

'AllDevices' {
@'
{"mobileAppAssignments":[{"@odata.type":"#microsoft.graph.mobileAppAssignment","target":{"@odata.type":"#microsoft.graph.allDevicesAssignmentTarget"},"intent":"Required","settings":null}]}
'@
if ($null -eq $InputObject -or ($InputObject -is [string] -and [string]::IsNullOrWhiteSpace($InputObject))) {
return @()
}
if ($InputObject -is [string]) {
return ($InputObject -split ',') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
}
if ($InputObject -is [System.Collections.IEnumerable]) {
return @($InputObject | Where-Object { $_ })
}
return @($InputObject)
}

$GroupNames = Get-StandardizedAssignmentList -InputObject $GroupNamesRaw
$GroupIds = Get-StandardizedAssignmentList -InputObject $GroupIdsRaw

if (-not $AssignTo -and $GroupIds.Count -eq 0 -and $GroupNames.Count -eq 0) {
throw 'No assignment target provided. Supply AssignTo, GroupNames, or GroupIds.'
}

'Both' {
@'
{"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}]}
'@
# Try to get the application type if not provided. Mostly just useful for ppl using the API that dont know the application type.
if (-not $AppType) {
try {
$AppMetadata = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter" -tenantid $TenantFilter
$odataType = $AppMetadata.'@odata.type'
if ($odataType) {
$AppType = ($odataType -replace '#microsoft.graph.', '') -replace 'App$'
}
} catch {
Write-Warning "Unable to resolve application type for $appFilter. Continuing without assignment settings."
}
}

$targetLabel = if ($AssignTo) {
$AssignTo
} elseif ($GroupNames.Count -gt 0) {
($GroupNames -join ', ')
} elseif ($GroupIds.Count -gt 0) {
"GroupIds: $($GroupIds -join ',')"
} else {
'CustomGroupAssignment'
}

$setParams = @{
ApplicationId = $appFilter
TenantFilter = $TenantFilter
Intent = $Intent
APIName = $APIName
Headers = $Headers
GroupName = ($AssignTo ? $AssignTo : $targetLabel)
AssignmentMode = $AssignmentMode
}

if ($AppType) {
$setParams.AppType = $AppType
}

if ($GroupIds.Count -gt 0) {
$setParams.GroupIds = $GroupIds
}

try {
$null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appFilter/assign" -tenantid $TenantFilter -body $AssignBody
$Result = "Successfully assigned app $($appFilter) to $($AssignTo)"
Write-LogMessage -headers $Headers -API $APIName -tenant $($TenantFilter) -message $Result -Sev Info
$Result = Set-CIPPAssignedApplication @setParams
$StatusCode = [HttpStatusCode]::OK

} catch {
$ErrorMessage = Get-CippException -Exception $_
$Result = "Failed to assign app $($appFilter) to $($AssignTo). Error: $($ErrorMessage.NormalizedError)"
Write-LogMessage -headers $Headers -API $APIName -tenant $TenantFilter -message $Result -Sev 'Error' -LogData $ErrorMessage
$Result = $_.Exception.Message
$StatusCode = [HttpStatusCode]::InternalServerError
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,19 @@ function Invoke-ListStandardsCompare {

$Table = Get-CIPPTable -TableName 'CippStandardsReports'
$TenantFilter = $Request.Query.tenantFilter
$TemplateFilter = $Request.Query.templateId

$Filters = [system.collections.generic.list[string]]::new()
if ($TenantFilter) {
$Table.Filter = "PartitionKey eq '{0}'" -f $TenantFilter
$Filters.Add("PartitionKey eq '{0}'" -f $TenantFilter)
}
if ($TemplateFilter) {
$Filters.Add("TemplateId eq '{0}'" -f $TemplateFilter)
}
$Filter = $Filters -join ' and '

$Tenants = Get-Tenants -IncludeErrors
$Standards = Get-CIPPAzDataTableEntity @Table | Where-Object { $_.PartitionKey -in $Tenants.defaultDomainName }

#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
<#$Results | ForEach-Object {
$Object = $_
$Object.PSObject.Properties | ForEach-Object {
if ($_.Name -like 'standards_*') {
if ($_.Value -is [System.Boolean]) {
$_.Value = [bool]$_.Value
} elseif ($_.Value -like '*{*') {
$_.Value = ConvertFrom-Json -InputObject $_.Value -ErrorAction SilentlyContinue
} else {
$_.Value = [string]$_.Value
}

$Key = $_.Name.replace('standards_', 'standards.')
$Key = $Key.replace('IntuneTemplate_', 'IntuneTemplate.')
$Key = $Key -replace '__', '-'

$object | Add-Member -MemberType NoteProperty -Name $Key -Value $_.Value -Force
$object.PSObject.Properties.Remove($_.Name)
}
}
}#>
$Standards = Get-CIPPAzDataTableEntity @Table -Filter $Filter | Where-Object { $_.PartitionKey -in $Tenants.defaultDomainName }

$TenantStandards = @{}
$Results = [System.Collections.Generic.List[object]]::new()
Expand Down Expand Up @@ -75,6 +59,7 @@ function Invoke-ListStandardsCompare {
$TenantStandards[$Tenant][$FieldName] = @{
Value = $FieldValue
LastRefresh = $Standard.TimeStamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
TemplateId = $Standard.TemplateId
}
}

Expand Down
149 changes: 107 additions & 42 deletions Modules/CIPPCore/Public/Set-CIPPAssignedApplication.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,36 @@ function Set-CIPPAssignedApplication {
$AppType,
$ApplicationId,
$TenantFilter,
$GroupIds,
$AssignmentMode = 'replace',
$APIName = 'Assign Application',
$Headers
)
Write-Host "GroupName: $GroupName Intent: $Intent AppType: $AppType ApplicationId: $ApplicationId TenantFilter: $TenantFilter APIName: $APIName"
try {
$assignmentSettings = $null
if ($AppType) {
$assignmentSettings = @{
'@odata.type' = "#microsoft.graph.$($AppType)AppAssignmentSettings"
}

switch ($AppType) {
'Win32Lob' {
$assignmentSettings.notifications = 'hideAll'
}
'WinGet' {
$assignmentSettings.notifications = 'hideAll'
}
'macOsVpp' {
$assignmentSettings.useDeviceLicensing = $true
}
default {
# No additional settings
}
}
}

# Build the assignment object
$MobileAppAssignment = switch ($GroupName) {
'AllUsers' {
@(@{
Expand All @@ -19,12 +44,7 @@ function Set-CIPPAssignedApplication {
'@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget'
}
intent = $Intent
settings = @{
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
notifications = 'hideAll'
installTimeSettings = $null
restartSettings = $null
}
settings = $assignmentSettings
})
break
}
Expand All @@ -35,12 +55,7 @@ function Set-CIPPAssignedApplication {
'@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget'
}
intent = $Intent
settings = @{
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
notifications = 'hideAll'
installTimeSettings = $null
restartSettings = $null
}
settings = $assignmentSettings
})
break
}
Expand All @@ -52,71 +67,121 @@ function Set-CIPPAssignedApplication {
'@odata.type' = '#microsoft.graph.allLicensedUsersAssignmentTarget'
}
intent = $Intent
settings = @{
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
notifications = 'hideAll'
installTimeSettings = $null
restartSettings = $null
}
settings = $assignmentSettings
},
@{
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
target = @{
'@odata.type' = '#microsoft.graph.allDevicesAssignmentTarget'
}
intent = $Intent
settings = @{
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
notifications = 'hideAll'
installTimeSettings = $null
restartSettings = $null
}
settings = $assignmentSettings
}
)
}
default {
$GroupNames = $GroupName.Split(',')
$GroupIds = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $TenantFilter | ForEach-Object {
$Group = $_
foreach ($SingleName in $GroupNames) {
if ($_.displayname -like $SingleName) {
$group.id
$resolvedGroupIds = @()
if ($PSBoundParameters.ContainsKey('GroupIds') -and $GroupIds) {
$resolvedGroupIds = $GroupIds
} else {
$GroupNames = $GroupName.Split(',')
$resolvedGroupIds = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/groups' -tenantid $TenantFilter | ForEach-Object {
$Group = $_
foreach ($SingleName in $GroupNames) {
if ($_.displayName -like $SingleName) {
$group.id
}
}
}
Write-Information "found $($resolvedGroupIds) groups"
}
Write-Information "found $($GroupIds) groups"
foreach ($Group in $GroupIds) {

# We ain't found nothing so we panic
if (-not $resolvedGroupIds) {
throw 'No matching groups resolved for assignment request.'
}

foreach ($Group in $resolvedGroupIds) {
@{
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
target = @{
'@odata.type' = '#microsoft.graph.groupAssignmentTarget'
groupId = $Group
}
intent = $Intent
settings = @{
'@odata.type' = "#microsoft.graph.$($appType)AppAssignmentSettings"
notifications = 'hideAll'
installTimeSettings = $null
restartSettings = $null
settings = $assignmentSettings
}
}
}
}

# If we're appending, we need to get existing assignments
if ($AssignmentMode -eq 'append') {
try {
$ExistingAssignments = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($ApplicationId)/assignments" -tenantid $TenantFilter
} catch {
Write-Warning "Unable to retrieve existing assignments for $ApplicationId. Proceeding with new assignments only. Error: $($_.Exception.Message)"
$ExistingAssignments = @()
}
}

# Deduplicate current assignments so the new ones override existing ones
if ($ExistingAssignments) {
$ExistingAssignments = $ExistingAssignments | ForEach-Object {
$ExistingAssignment = $_
switch ($ExistingAssignment.target.'@odata.type') {
'#microsoft.graph.groupAssignmentTarget' {
if ($ExistingAssignment.target.groupId -notin $MobileAppAssignment.target.groupId) {
$ExistingAssignment
}
}
default {
if ($ExistingAssignment.target.'@odata.type' -notin $MobileAppAssignment.target.'@odata.type') {
$ExistingAssignment
}
}
}
}
}

$FinalAssignments = [System.Collections.Generic.List[object]]::new()
if ($AssignmentMode -eq 'append' -and $ExistingAssignments) {
$ExistingAssignments | ForEach-Object {
$FinalAssignments.Add(@{
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
target = $_.target
intent = $_.intent
settings = $_.settings
})
}

$MobileAppAssignment | ForEach-Object {
$FinalAssignments.Add(@{
'@odata.type' = '#microsoft.graph.mobileAppAssignment'
target = $_.target
intent = $_.intent
settings = $_.settings
})
}
} else {
$FinalAssignments = $MobileAppAssignment
}

$DefaultAssignmentObject = [PSCustomObject]@{
mobileAppAssignments = @(
$MobileAppAssignment
$FinalAssignments
)
}
if ($PSCmdlet.ShouldProcess($GroupName, "Assigning Application $ApplicationId")) {
Start-Sleep -Seconds 1
# Write-Information (ConvertTo-Json $DefaultAssignmentObject -Depth 10)
$null = New-GraphPOSTRequest -uri "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$($ApplicationId)/assign" -tenantid $TenantFilter -type POST -body ($DefaultAssignmentObject | ConvertTo-Json -Compress -Depth 10)
Write-LogMessage -headers $Headers -API $APIName -message "Assigned Application to $($GroupName)" -Sev 'Info' -tenant $TenantFilter
Write-LogMessage -headers $Headers -API $APIName -message "Assigned Application $ApplicationId to $($GroupName)" -Sev 'Info' -tenant $TenantFilter
}
return "Assigned Application to $($GroupName)"
return "Assigned Application $ApplicationId to $($GroupName)"
} catch {
$ErrorMessage = Get-CippException -Exception $_
Write-LogMessage -headers $Headers -API $APIName -message "Could not assign application to $GroupName. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage
return "Could not assign application to $GroupName. Error: $($ErrorMessage.NormalizedError)"
Write-LogMessage -headers $Headers -API $APIName -message "Could not assign application $ApplicationId to $GroupName. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $TenantFilter -LogData $ErrorMessage
throw "Could not assign application $ApplicationId to $GroupName. Error: $($ErrorMessage.NormalizedError)"
}
}
Loading