Skip to content

Commit 188019a

Browse files
authored
Merge pull request #207 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents e40ccce + 6cbaf45 commit 188019a

File tree

5 files changed

+151
-39
lines changed

5 files changed

+151
-39
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/CIPP/Scheduler/Invoke-ListScheduledItemDetails.ps1

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,36 @@ function Invoke-ListScheduledItemDetails {
6464
$Results = Get-CIPPAzDataTableEntity @ResultsTable -Filter $ResultsFilter
6565

6666
if (!$Results) {
67-
$ResultData = ($Task.Results | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? $Task.Results
67+
try {
68+
# Handle the case when we need to use Task.Results
69+
if ($Task.Results) {
70+
# Try to safely parse JSON or use the raw value if parsing fails
71+
try {
72+
if ($Task.Results -is [string]) {
73+
$ResultString = $Task.Results.ToString().Trim()
74+
if (($ResultString -match '^\[.*\]$') -or ($ResultString -match '^\{.*\}$')) {
75+
$ResultData = $Task.Results | ConvertFrom-Json -ErrorAction Stop
76+
} else {
77+
# Not valid JSON format, use as is
78+
$ResultData = $Task.Results
79+
}
80+
} else {
81+
# Already an object, use as is
82+
$ResultData = $Task.Results
83+
}
84+
} catch {
85+
# If JSON parsing fails, use raw value
86+
Write-LogMessage -API $APIName -message "Error parsing Task.Results as JSON: $_" -Sev 'Warning'
87+
$ResultData = $Task.Results
88+
}
89+
} else {
90+
$ResultData = $null
91+
}
92+
} catch {
93+
Write-LogMessage -API $APIName -message "Error processing Task.Results: $_" -Sev 'Error'
94+
$ResultData = $null
95+
}
96+
6897
$Results = @(
6998
[PSCustomObject]@{
7099
RowKey = $Task.Tenant
@@ -73,17 +102,43 @@ function Invoke-ListScheduledItemDetails {
73102
}
74103
)
75104
}
105+
76106
# Process the results if they exist
77107
$ProcessedResults = [System.Collections.Generic.List[object]]::new()
78108
foreach ($Result in $Results) {
79109
try {
80-
if ($Result.Results) {
81-
$ParsedResults = ($Result.Results | ConvertFrom-Json -ErrorAction SilentlyContinue) ?? $Result.Results
82-
if (!$ParsedResults -or 'null' -eq $ParsedResults) {
110+
if ($null -ne $Result.Results) {
111+
# Safe handling based on result type
112+
if ($Result.Results -is [array] -or $Result.Results -is [System.Collections.ICollection]) {
113+
# Already a collection, use as is
114+
$ParsedResults = $Result.Results
115+
} elseif ($Result.Results -is [string]) {
116+
$ResultString = $Result.Results.ToString().Trim()
117+
# Only try to parse as JSON if it looks like JSON
118+
if (($ResultString -match '^\[.*\]$') -or ($ResultString -match '^\{.*\}$')) {
119+
try {
120+
$ParsedResults = $Result.Results | ConvertFrom-Json -ErrorAction Stop
121+
} catch {
122+
Write-LogMessage -API $APIName -message "Failed to parse result as JSON: $_" -Sev 'Warning'
123+
# On failure, keep as string
124+
$ParsedResults = $Result.Results
125+
}
126+
} else {
127+
# Not valid JSON format
128+
$ParsedResults = $Result.Results
129+
}
130+
} else {
131+
# Any other object type
132+
$ParsedResults = $Result.Results
133+
}
134+
135+
# Ensure results is always an array
136+
if ($null -eq $ParsedResults -or 'null' -eq $ParsedResults) {
83137
$Result.Results = @()
84138
} else {
85139
$Result.Results = @($ParsedResults)
86140
}
141+
87142
# Store tenant information with the result
88143
$TenantId = $Result.RowKey
89144
$TenantInfo = Get-Tenants -TenantFilter $TenantId -ErrorAction SilentlyContinue
@@ -95,6 +150,8 @@ function Invoke-ListScheduledItemDetails {
95150
}
96151
} catch {
97152
Write-LogMessage -API $APIName -message "Error processing results for task $RowKey with tenant $($Result.RowKey): $_" -Sev 'Error'
153+
# Set Results to an empty array to prevent further errors
154+
$Result.Results = @()
98155
}
99156
$EndResult = $Result | Select-Object Timestamp, @{n = 'Tenant'; Expression = { $_.RowKey } }, Results
100157
$ProcessedResults.Add($EndResult)

Modules/CIPPCore/Public/New-CIPPTemplateRun.ps1

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ function New-CIPPTemplateRun {
1313
$data
1414
} | Sort-Object -Property displayName
1515

16+
function Get-SanitizedFilename {
17+
param (
18+
[string]$filename
19+
)
20+
$filename = $filename -replace '\s', '_' -replace '[^\w\d_]', ''
21+
return $filename
22+
}
1623

1724
$Tasks = foreach ($key in $TemplateSettings.Keys) {
1825
if ($TemplateSettings[$key] -eq $true) {
1926
$key
2027
}
2128
}
2229
if ($TemplateSettings.templateRepo) {
23-
Write-Host 'Grabbing data from required community repo'
30+
Write-Information 'Grabbing data from community repo'
2431
try {
2532
$Files = (Get-GitHubFileTree -FullName $TemplateSettings.templateRepo.value -Branch $TemplateSettings.templateRepoBranch.value).tree | Where-Object { $_.path -match '.json$' -and $_.path -notmatch 'NativeImport' } | Select-Object *, @{n = 'html_url'; e = { "https://github.com/$($SplatParams.FullName)/tree/$($SplatParams.Branch)/$($_.path)" } }, @{n = 'name'; e = { ($_.path -split '/')[ -1 ] -replace '\.json$', '' } }
2633
#if there is a migration table file, file the file. Store the file contents in $migrationtable
@@ -30,20 +37,29 @@ function New-CIPPTemplateRun {
3037
}
3138
foreach ($File in $Files) {
3239
if ($File.name -eq 'MigrationTable' -or $file.name -eq 'ALLOWED COUNTRIES') { continue }
33-
$ExistingTemplate = $ExistingTemplates | Where-Object { $_.displayName -eq $File.name } | Select-Object -First 1
34-
$Template = (Get-GitHubFileContents -FullName $TemplateSettings.templateRepo.value -Branch $TemplateSettings.templateRepoBranch.value -Path $File.path).content | ConvertFrom-Json
35-
if ($ExistingTemplate) {
36-
$UpdateNeeded = $false
37-
if ($ExistingTemplate.sha -ne $File.sha -or !$ExistingTemplate.sha) {
38-
$UpdateNeeded = $true
39-
}
40-
if ($UpdateNeeded) {
41-
Write-Host "Template $($File.name) needs to be updated as the SHA is different"
42-
Import-CommunityTemplate -Template $Template -SHA $File.sha -MigrationTable $MigrationTable
43-
}
40+
$ExistingTemplate = $ExistingTemplates | Where-Object { (![string]::IsNullOrEmpty($_.displayName) -and (Get-SanitizedFilename -filename $_.displayName) -eq $File.name) -or (![string]::IsNullOrEmpty($_.templateName) -and (Get-SanitizedFilename -filename $_.templateName) -eq $File.name ) } | Select-Object -First 1
41+
42+
$UpdateNeeded = $false
43+
if ($ExistingTemplate -and $ExistingTemplate.SHA -ne $File.sha) {
44+
$Name = $ExistingTemplate.displayName ?? $ExistingTemplate.templateName
45+
Write-Information "Existing template $($Name) found, but SHA is different. Updating template."
46+
$UpdateNeeded = $true
47+
"Template $($Name) needs to be updated as the SHA is different"
4448
} else {
45-
Write-Host "Template $($File.name) needs to be created"
49+
Write-Information "Existing template $($File.name) found, but SHA is the same. No update needed."
50+
"Template $($File.name) found, but SHA is the same. No update needed."
51+
}
52+
53+
if (!$ExistingTemplate -or $UpdateNeeded) {
54+
$Template = (Get-GitHubFileContents -FullName $TemplateSettings.templateRepo.value -Branch $TemplateSettings.templateRepoBranch.value -Path $File.path).content | ConvertFrom-Json
4655
Import-CommunityTemplate -Template $Template -SHA $File.sha -MigrationTable $MigrationTable
56+
if ($UpdateNeeded) {
57+
Write-Information "Template $($File.name) needs to be updated as the SHA is different"
58+
"Template $($File.name) updated"
59+
} else {
60+
Write-Information "Template $($File.name) needs to be created"
61+
"Template $($File.name) created"
62+
}
4763
}
4864
}
4965
} catch {
@@ -53,12 +69,12 @@ function New-CIPPTemplateRun {
5369
}
5470
} else {
5571
foreach ($Task in $Tasks) {
56-
Write-Host "Working on task $Task"
72+
Write-Information "Working on task $Task"
5773
switch ($Task) {
5874
'ca' {
59-
Write-Host "Template Conditional Access Policies for $TenantFilter"
75+
Write-Information "Template Conditional Access Policies for $TenantFilter"
6076
$Policies = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/conditionalAccess/policies?$top=999' -tenantid $TenantFilter
61-
Write-Host 'Creating templates for found Conditional Access Policies'
77+
Write-Information 'Creating templates for found Conditional Access Policies'
6278
foreach ($policy in $policies) {
6379
try {
6480
$Template = New-CIPPCATemplate -TenantFilter $TenantFilter -JSON $policy
@@ -89,7 +105,7 @@ function New-CIPPTemplateRun {
89105
}
90106
}
91107
'intuneconfig' {
92-
Write-Host "Backup Intune Configuration Policies for $TenantFilter"
108+
Write-Information "Backup Intune Configuration Policies for $TenantFilter"
93109
$GraphURLS = @("https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations?`$select=id,displayName,lastModifiedDateTime,roleScopeTagIds,microsoft.graph.unsupportedDeviceConfiguration/originalEntityTypeName&`$expand=assignments&top=1000"
94110
'https://graph.microsoft.com/beta/deviceManagement/windowsDriverUpdateProfiles'
95111
"https://graph.microsoft.com/beta/deviceManagement/groupPolicyConfigurations?`$expand=assignments&top=999"
@@ -146,12 +162,12 @@ function New-CIPPTemplateRun {
146162
}
147163
}
148164
} catch {
149-
Write-Host "Failed to backup $url"
165+
Write-Information "Failed to backup $url"
150166
}
151167
}
152168
}
153169
'intunecompliance' {
154-
Write-Host "Backup Intune Compliance Policies for $TenantFilter"
170+
Write-Information "Backup Intune Compliance Policies for $TenantFilter"
155171
New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicies?$top=999' -tenantid $TenantFilter | ForEach-Object {
156172
$Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'deviceCompliancePolicies' -ID $_.ID
157173
$ExistingPolicy = $ExistingTemplates | Where-Object { $_.displayName -eq $Template.DisplayName } | Select-Object -First 1
@@ -192,7 +208,7 @@ function New-CIPPTemplateRun {
192208
}
193209

194210
'intuneprotection' {
195-
Write-Host "Backup Intune Protection Policies for $TenantFilter"
211+
Write-Information "Backup Intune Protection Policies for $TenantFilter"
196212
New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceAppManagement/managedAppPolicies?$top=999' -tenantid $TenantFilter | ForEach-Object {
197213
$Template = New-CIPPIntuneTemplate -TenantFilter $TenantFilter -URLName 'managedAppPolicies' -ID $_.ID
198214
$ExistingPolicy = $ExistingTemplates | Where-Object { $_.displayName -eq $Template.DisplayName } | Select-Object -First 1

Modules/CIPPCore/Public/Remove-CIPPGroups.ps1

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ function Remove-CIPPGroups {
1111
if (-not $userid) {
1212
$userid = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($Username)" -tenantid $Tenantfilter).id
1313
}
14-
$AllGroups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/?`$select=displayName,mailEnabled,id,groupTypes" -tenantid $tenantFilter)
14+
$AllGroups = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/?`$select=displayName,mailEnabled,id,groupTypes,assignedLicenses&`$top=999" -tenantid $tenantFilter)
1515

1616
$Returnval = (New-GraphPostRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/GetMemberGroups" -tenantid $tenantFilter -type POST -body '{"securityEnabledOnly": false}').value | ForEach-Object -Parallel {
1717
Import-Module '.\Modules\AzBobbyTables'
@@ -22,18 +22,23 @@ function Remove-CIPPGroups {
2222
$Groupname = ($using:AllGroups | Where-Object -Property id -EQ $group).displayName
2323
$IsMailEnabled = ($using:AllGroups | Where-Object -Property id -EQ $group).mailEnabled
2424
$IsM365Group = $null -ne ($using:AllGroups | Where-Object { $_.id -eq $group -and $_.groupTypes -contains 'Unified' })
25+
$IsLicensed = ($using:AllGroups | Where-Object -Property id -EQ $group).assignedLicenses.Count -gt 0
2526

26-
if ($IsM365Group) {
27-
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
28-
} elseif (-not $IsMailEnabled) {
29-
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
30-
} elseif ($IsMailEnabled) {
31-
$Params = @{ Identity = $Groupname; Member = $using:userid ; BypassSecurityGroupManagerCheck = $true }
32-
New-ExoRequest -tenantid $using:tenantFilter -cmdlet 'Remove-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true
33-
}
27+
if ($IsLicensed) {
28+
"Could not remove $($using:Username) from $Groupname. This is because the group has licenses assigned to it."
29+
} else {
30+
if ($IsM365Group) {
31+
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
32+
} elseif (-not $IsMailEnabled) {
33+
$null = New-GraphPostRequest -uri "https://graph.microsoft.com/beta/groups/$_/members/$($using:userid)/`$ref" -tenantid $using:tenantFilter -type DELETE -body '' -Verbose
34+
} elseif ($IsMailEnabled) {
35+
$Params = @{ Identity = $Groupname; Member = $using:userid ; BypassSecurityGroupManagerCheck = $true }
36+
New-ExoRequest -tenantid $using:tenantFilter -cmdlet 'Remove-DistributionGroupMember' -cmdParams $params -UseSystemMailbox $true
37+
}
3438

35-
Write-LogMessage -headers $using:Headers -API $($using:APIName) -message "Removed $($using:Username) from $groupname" -Sev 'Info' -tenant $using:TenantFilter
36-
"Successfully removed $($using:Username) from group $Groupname"
39+
Write-LogMessage -headers $using:Headers -API $($using:APIName) -message "Removed $($using:Username) from $groupname" -Sev 'Info' -tenant $using:TenantFilter
40+
"Successfully removed $($using:Username) from group $Groupname"
41+
}
3742
} catch {
3843
$ErrorMessage = Get-CippException -Exception $_
3944
Write-LogMessage -headers $using:Headers -API $($using:APIName) -message "Could not remove $($using:Username) from group $groupname : $($ErrorMessage.NormalizedError)" -Sev 'Error' -tenant $using:TenantFilter -LogData $ErrorMessage

Modules/CIPPCore/Public/Remove-CIPPLicense.ps1

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,36 @@ function Remove-CIPPLicense {
3535
try {
3636
$ConvertTable = Import-Csv ConversionTable.csv
3737
$User = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)" -tenantid $tenantFilter
38+
$GroupMemberships = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/users/$($userid)/memberOf/microsoft.graph.group?`$select=id,displayName,assignedLicenses" -tenantid $tenantFilter
39+
$LicenseGroups = $GroupMemberships | Where-Object { ($_.assignedLicenses | Measure-Object).Count -gt 0 }
40+
41+
if ($LicenseGroups) {
42+
# remove user from groups with licenses, these can only be graph groups
43+
$RemoveRequests = foreach ($LicenseGroup in $LicenseGroups) {
44+
@{
45+
id = $LicenseGroup.id
46+
method = 'DELETE'
47+
url = "groups/$($LicenseGroup.id)/members/$($User.id)/`$ref"
48+
}
49+
}
50+
51+
Write-Information 'Removing user from groups with licenses'
52+
$RemoveResults = New-GraphBulkRequest -tenantid $tenantFilter -requests @($RemoveRequests)
53+
Write-Information ($RemoveResults | ConvertTo-Json -Depth 5)
54+
$RemoveResults | ForEach-Object {
55+
$Group = $GroupMemberships | Where-Object { $_.id -eq $_.id }
56+
$GroupName = $Group | Select-Object -ExpandProperty displayName
57+
58+
if ($_.status -eq 204) {
59+
Write-LogMessage -headers $Headers -API $APIName -message "Removed $($User.displayName) from license group $GroupName" -Sev 'Info' -tenant $TenantFilter
60+
"Removed $($User.displayName) from license group $GroupName"
61+
} else {
62+
Write-LogMessage -headers $Headers -API $APIName -message "Failed to remove $($User.displayName) from license group $GroupName. This is likely because its a Dynamic Group or synced with active directory." -Sev 'Error' -tenant $TenantFilter
63+
"Failed to remove $($User.displayName) from license group $GroupName. This is likely because its a Dynamic Group or synced with active directory."
64+
}
65+
}
66+
}
67+
3868
if (!$username) { $username = $User.userPrincipalName }
3969
$CurrentLicenses = $User.assignedlicenses.skuid
4070
$ConvertedLicense = $(($ConvertTable | Where-Object { $_.guid -in $CurrentLicenses }).'Product_Display_Name' | Sort-Object -Unique) -join ', '

0 commit comments

Comments
 (0)