Skip to content

Commit 3b3ecba

Browse files
authored
Merge pull request #540 from KelvinTegelaar/dev
[pull] dev from KelvinTegelaar:dev
2 parents 1b7dc89 + b187be2 commit 3b3ecba

File tree

8 files changed

+150
-25
lines changed

8 files changed

+150
-25
lines changed

Modules/CIPPCore/Public/Entrypoints/Activity Triggers/Push-ExecScheduledCommand.ps1

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ function Push-ExecScheduledCommand {
1818
# We don't need to expand groups here as that's handled in the orchestrator
1919
$TenantInfo = Get-Tenants -TenantFilter $Tenant
2020

21+
$CurrentTask = Get-AzDataTableEntity @Table -Filter "PartitionKey eq '$($task.PartitionKey)' and RowKey eq '$($task.RowKey)'"
22+
if (!$CurrentTask) {
23+
Write-Information "The task $($task.Name) for tenant $($task.Tenant) does not exist in the ScheduledTasks table. Exiting."
24+
return
25+
}
26+
if ($CurrentTask.TaskState -eq 'Completed') {
27+
Write-Information "The task $($task.Name) for tenant $($task.Tenant) is already completed. Skipping execution."
28+
return
29+
}
30+
2131
if ($task.Trigger) {
2232
# Extract trigger data from the task and process
2333
$Trigger = if (Test-Json -Json $task.Trigger) { $task.Trigger | ConvertFrom-Json } else { $task.Trigger }
@@ -276,7 +286,7 @@ function Push-ExecScheduledCommand {
276286
Write-Information 'Sent the results to the target. Updating the task state.'
277287

278288
try {
279-
if ($task.Recurrence -eq '0' -or [string]::IsNullOrEmpty($task.Recurrence) -or $Trigger.ExecutionMode.value -eq 'once') {
289+
if ($task.Recurrence -eq '0' -or [string]::IsNullOrEmpty($task.Recurrence) -or $Trigger.ExecutionMode.value -eq 'once' -or $Trigger.ExecutionMode -eq 'once') {
280290
Write-Information 'Recurrence empty or 0. Task is not recurring. Setting task state to completed.'
281291
Update-AzDataTableEntity -Force @Table -Entity @{
282292
PartitionKey = $task.PartitionKey

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,15 @@ function Push-CIPPStandard {
2020
Write-Information "Rerun is set to false. We'll be running $FunctionName"
2121
}
2222
try {
23-
& $FunctionName -Tenant $Item.Tenant -Settings $Item.Settings -ErrorAction Stop
23+
# Convert settings to JSON, replace %variables%, then convert back to object
24+
$SettingsJSON = $Item.Settings | ConvertTo-Json -Depth 10 -Compress
25+
if ($SettingsJSON -match '%') {
26+
$Settings = Get-CIPPTextReplacement -TenantFilter $Item.Tenant -Text $SettingsJSON | ConvertFrom-Json
27+
} else {
28+
$Settings = $Item.Settings
29+
}
30+
31+
& $FunctionName -Tenant $Item.Tenant -Settings $Settings -ErrorAction Stop
2432
Write-Information "Standard $($Standard) completed for tenant $($Tenant)"
2533
} catch {
2634
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Error running standard $($Standard) for tenant $($Tenant) - $($_.Exception.Message)" -sev Error -LogData (Get-CippException -Exception $_)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
function Invoke-AddGroupTeam {
2+
<#
3+
.FUNCTIONALITY
4+
Entrypoint
5+
.ROLE
6+
Identity.Group.ReadWrite
7+
#>
8+
[CmdletBinding()]
9+
param($Request, $TriggerMetadata)
10+
11+
$APIName = $Request.Params.CIPPEndpoint
12+
$TenantFilter = $Request.Body.TenantFilter
13+
$GroupId = $Request.Body.GroupId
14+
15+
$Results = [System.Collections.Generic.List[string]]@()
16+
17+
try {
18+
# Default team settings - can be customized via request body
19+
$TeamSettings = if ($Request.Body.TeamSettings) {
20+
$Request.Body.TeamSettings
21+
} else {
22+
@{
23+
memberSettings = @{
24+
allowCreatePrivateChannels = $true
25+
allowCreateUpdateChannels = $true
26+
}
27+
messagingSettings = @{
28+
allowUserEditMessages = $true
29+
allowUserDeleteMessages = $true
30+
}
31+
funSettings = @{
32+
allowGiphy = $true
33+
giphyContentRating = 'strict'
34+
}
35+
}
36+
}
37+
38+
# Create team from group using PUT request
39+
$GraphParams = @{
40+
uri = "https://graph.microsoft.com/beta/groups/$GroupId/team"
41+
tenantid = $TenantFilter
42+
type = 'PUT'
43+
body = ($TeamSettings | ConvertTo-Json -Depth 10)
44+
AsApp = $true
45+
}
46+
$null = New-GraphPOSTRequest @GraphParams
47+
48+
$Results.Add("Successfully created team from group $GroupId")
49+
Write-LogMessage -API $APIName -tenant $TenantFilter -message "Created team from group $GroupId" -Sev 'Info'
50+
} catch {
51+
$ErrorMessage = Get-CippException -Exception $_
52+
$RawMessage = $_.Exception.Message
53+
54+
# Determine if this is a likely replication delay 404 (exclude owner/membership related 404s)
55+
$Is404 = ($RawMessage -match '404|Not Found' -or $ErrorMessage.NormalizedError -match '404|Not Found')
56+
$IsOwnerRelated = ($RawMessage -match 'owner' -or $ErrorMessage.NormalizedError -match 'owner')
57+
$IsMembershipRelated = ($RawMessage -match 'member' -or $ErrorMessage.NormalizedError -match 'member')
58+
59+
$IsReplicationDelay = $Is404 -and -not ($IsOwnerRelated -or $IsMembershipRelated)
60+
61+
if ($IsReplicationDelay) {
62+
$Results.Add('Failed to create team: The group may have been created too recently. If it was created less than 15 minutes ago, wait and retry. Groups need time to fully replicate before a team can be created.')
63+
Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to create team from group $GroupId - probable replication delay (404). Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage
64+
} else {
65+
$Results.Add("Failed to create team: $($ErrorMessage.NormalizedError)")
66+
Write-LogMessage -API $APIName -tenant $TenantFilter -message "Failed to create team from group $GroupId. Error: $($ErrorMessage.NormalizedError)" -Sev 'Error' -LogData $ErrorMessage
67+
}
68+
}
69+
70+
$Body = @{
71+
Results = @($Results)
72+
}
73+
74+
return ([HttpResponseContext]@{
75+
StatusCode = [HttpStatusCode]::OK
76+
Body = $Body
77+
})
78+
}

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-AddGroupTemplate.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function Invoke-AddGroupTemplate {
2929
'*unified*' { 'm365'; break }
3030
'*m365*' { 'm365'; break }
3131
'*generic*' { 'generic'; break }
32-
'*security*' { 'generic'; break }
32+
'*security*' { 'security'; break }
3333
'*distribution*' { 'distribution'; break }
3434
'*mail*' { 'distribution'; break }
3535
default { $Request.Body.groupType }

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/Identity/Administration/Groups/Invoke-ListGroups.ps1

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,21 @@ function Invoke-ListGroups {
8787
$GraphRequest = [PSCustomObject]@{
8888
groupInfo = ($RawGraphRequest | Where-Object { $_.id -eq 1 }).body | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split '@' | Select-Object -Last 1 } },
8989
@{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -like '*Team*') { $true } else { $false } } },
90-
@{Name = 'calculatedGroupType'; Expression = {
90+
@{Name = 'groupType'; Expression = {
9191
if ($_.groupTypes -contains 'Unified') { 'Microsoft 365' }
9292
elseif ($_.mailEnabled -and $_.securityEnabled) { 'Mail-Enabled Security' }
9393
elseif (-not $_.mailEnabled -and $_.securityEnabled) { 'Security' }
9494
elseif (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (-not $_.securityEnabled)) { 'Distribution List' }
9595
}
96-
}, @{Name = 'dynamicGroupBool'; Expression = { if ($_.groupTypes -contains 'DynamicMembership') { $true } else { $false } } }
96+
},
97+
@{Name = 'calculatedGroupType'; Expression = {
98+
if ($_.groupTypes -contains 'Unified') { 'm365' }
99+
elseif ($_.mailEnabled -and $_.securityEnabled) { 'security' }
100+
elseif (-not $_.mailEnabled -and $_.securityEnabled) { 'generic' }
101+
elseif (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (-not $_.securityEnabled)) { 'distributionList' }
102+
}
103+
},
104+
@{Name = 'dynamicGroupBool'; Expression = { if ($_.groupTypes -contains 'DynamicMembership') { $true } else { $false } } }
97105
members = ($RawGraphRequest | Where-Object { $_.id -eq 2 }).body.value
98106
owners = ($RawGraphRequest | Where-Object { $_.id -eq 3 }).body.value
99107
allowExternal = (!$OnlyAllowInternal)
@@ -104,13 +112,20 @@ function Invoke-ListGroups {
104112
$GraphRequest = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/groups/$($GroupID)/$($members)?`$top=999&select=$SelectString" -tenantid $TenantFilter | Select-Object *, @{ Name = 'primDomain'; Expression = { $_.mail -split '@' | Select-Object -Last 1 } },
105113
@{Name = 'membersCsv'; Expression = { $_.members.userPrincipalName -join ',' } },
106114
@{Name = 'teamsEnabled'; Expression = { if ($_.resourceProvisioningOptions -like '*Team*') { $true }else { $false } } },
107-
@{Name = 'calculatedGroupType'; Expression = {
115+
@{Name = 'groupType'; Expression = {
108116
if ($_.groupTypes -contains 'Unified') { 'Microsoft 365' }
109117
elseif ($_.mailEnabled -and $_.securityEnabled) { 'Mail-Enabled Security' }
110118
elseif (-not $_.mailEnabled -and $_.securityEnabled) { 'Security' }
111119
elseif (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (-not $_.securityEnabled)) { 'Distribution List' }
112120
}
113121
},
122+
@{Name = 'calculatedGroupType'; Expression = {
123+
if ($_.groupTypes -contains 'Unified') { 'm365' }
124+
elseif ($_.mailEnabled -and $_.securityEnabled) { 'security' }
125+
elseif (-not $_.mailEnabled -and $_.securityEnabled) { 'generic' }
126+
elseif (([string]::isNullOrEmpty($_.groupTypes)) -and ($_.mailEnabled) -and (-not $_.securityEnabled)) { 'distributionList' }
127+
}
128+
},
114129
@{Name = 'dynamicGroupBool'; Expression = { if ($_.groupTypes -contains 'DynamicMembership') { $true } else { $false } } }
115130
$GraphRequest = @($GraphRequest | Sort-Object displayName)
116131
}

Modules/CIPPCore/Public/New-CIPPGroup.ps1

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function New-CIPPGroup {
4343
try {
4444
# Normalize group type for consistent handling (accept camelCase from templates)
4545
$NormalizedGroupType = switch -Wildcard ($GroupObject.groupType.ToLower()) {
46+
'mail-enabled security' { 'Security'; break }
4647
'*dynamicdistribution*' { 'DynamicDistribution'; break } # Check this first before *dynamic* and *distribution*
4748
'*dynamic*' { 'Dynamic'; break }
4849
'*generic*' { 'Generic'; break }
@@ -57,7 +58,7 @@ function New-CIPPGroup {
5758
}
5859

5960
# Determine if this group type needs an email address
60-
$GroupTypesNeedingEmail = @('M365', 'Distribution', 'DynamicDistribution')
61+
$GroupTypesNeedingEmail = @('M365', 'Distribution', 'DynamicDistribution', 'Security')
6162
$NeedsEmail = $NormalizedGroupType -in $GroupTypesNeedingEmail
6263

6364
# Determine email address only for group types that need it
@@ -95,13 +96,13 @@ function New-CIPPGroup {
9596
Write-LogMessage -API $APIName -tenant $TenantFilter -message "Creating group $($GroupObject.displayName) of type $NormalizedGroupType$(if ($NeedsEmail) { " with email $Email" })" -Sev Info
9697

9798
# Handle Graph API groups (Security, Generic, AzureRole, Dynamic, M365)
98-
if ($NormalizedGroupType -in @('Generic', 'Security', 'AzureRole', 'Dynamic', 'M365')) {
99+
if ($NormalizedGroupType -in @('Generic', 'AzureRole', 'Dynamic', 'M365')) {
99100
Write-Information "Creating group $($GroupObject.displayName) of type $NormalizedGroupType$(if ($NeedsEmail) { " with email $Email" })"
100101
$BodyParams = [PSCustomObject]@{
101102
'displayName' = $GroupObject.displayName
102103
'description' = $GroupObject.description
103104
'mailNickname' = $MailNickname
104-
'mailEnabled' = ($NormalizedGroupType -in @('Security', 'M365'))
105+
'mailEnabled' = ($NormalizedGroupType -eq 'M365')
105106
'securityEnabled' = $true
106107
'isAssignableToRole' = ($NormalizedGroupType -eq 'AzureRole')
107108
}
@@ -194,13 +195,17 @@ function New-CIPPGroup {
194195

195196
$ExoParams = @{
196197
Name = $GroupObject.displayName
197-
Alias = $GroupObject.username
198+
Alias = $MailNickname
198199
Description = $GroupObject.description
199200
PrimarySmtpAddress = $Email
200201
Type = $GroupObject.groupType
201202
RequireSenderAuthenticationEnabled = [bool]!$GroupObject.allowExternal
202203
}
203204

205+
if ($NormalizedGroupType -eq 'Security') {
206+
$ExoParams.Type = 'Security'
207+
}
208+
204209
# Add owners
205210
if ($GroupObject.owners -and $GroupObject.owners.Count -gt 0) {
206211
$OwnerEmails = $GroupObject.owners | ForEach-Object {

Modules/CIPPCore/Public/Standards/Invoke-CIPPStandardAutopilotProfile.ps1

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,8 @@ function Invoke-CIPPStandardAutopilotProfile {
4949
return $true
5050
} #we're done.
5151
try {
52-
# Replace variables in displayname to prevent duplicates
53-
$DisplayName = Get-CIPPTextReplacement -Text $Settings.DisplayName -TenantFilter $Tenant
54-
5552
$CurrentConfig = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/deviceManagement/windowsAutopilotDeploymentProfiles' -tenantid $Tenant |
56-
Where-Object { $_.displayName -eq $DisplayName } |
53+
Where-Object { $_.displayName -eq $Settings.DisplayName } |
5754
Select-Object -Property displayName, description, deviceNameTemplate, locale, preprovisioningAllowed, hardwareHashExtractionEnabled, outOfBoxExperienceSetting
5855

5956
if ($Settings.NotLocalAdmin -eq $true) { $userType = 'Standard' } else { $userType = 'Administrator' }
@@ -64,7 +61,7 @@ function Invoke-CIPPStandardAutopilotProfile {
6461
$DeploymentMode = 'singleUser'
6562
}
6663

67-
$StateIsCorrect = ($CurrentConfig.displayName -eq $DisplayName) -and
64+
$StateIsCorrect = ($CurrentConfig.displayName -eq $Settings.DisplayName) -and
6865
($CurrentConfig.description -eq $Settings.Description) -and
6966
($CurrentConfig.deviceNameTemplate -eq $Settings.DeviceNameTemplate) -and
7067
([string]::IsNullOrWhiteSpace($CurrentConfig.locale) -and [string]::IsNullOrWhiteSpace($Settings.Languages.value) -or $CurrentConfig.locale -eq $Settings.Languages.value) -and
@@ -84,12 +81,12 @@ function Invoke-CIPPStandardAutopilotProfile {
8481
# Remediate if the state is not correct
8582
if ($Settings.remediate -eq $true) {
8683
if ($StateIsCorrect -eq $true) {
87-
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($DisplayName)' already exists" -sev Info
84+
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' already exists" -sev Info
8885
} else {
8986
try {
9087
$Parameters = @{
9188
tenantFilter = $Tenant
92-
displayName = $DisplayName
89+
displayName = $Settings.DisplayName
9390
description = $Settings.Description
9491
userType = $userType
9592
DeploymentMode = $DeploymentMode
@@ -106,9 +103,9 @@ function Invoke-CIPPStandardAutopilotProfile {
106103

107104
Set-CIPPDefaultAPDeploymentProfile @Parameters
108105
if ($null -eq $CurrentConfig) {
109-
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Autopilot profile '$($DisplayName)'" -sev Info
106+
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Created Autopilot profile '$($Settings.DisplayName)'" -sev Info
110107
} else {
111-
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Autopilot profile '$($DisplayName)'" -sev Info
108+
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Updated Autopilot profile '$($Settings.DisplayName)'" -sev Info
112109
}
113110
} catch {
114111
$ErrorMessage = Get-CippException -Exception $_
@@ -128,10 +125,10 @@ function Invoke-CIPPStandardAutopilotProfile {
128125
# Alert
129126
if ($Settings.alert -eq $true) {
130127
if ($StateIsCorrect -eq $true) {
131-
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($DisplayName)' exists" -sev Info
128+
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' exists" -sev Info
132129
} else {
133-
Write-StandardsAlert -message "Autopilot profile '$($DisplayName)' do not match expected configuration" -object $CurrentConfig -tenant $Tenant -standardName 'AutopilotProfile' -standardId $Settings.standardId
134-
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($DisplayName)' do not match expected configuration" -sev Info
130+
Write-StandardsAlert -message "Autopilot profile '$($Settings.DisplayName)' do not match expected configuration" -object $CurrentConfig -tenant $Tenant -standardName 'AutopilotProfile' -standardId $Settings.standardId
131+
Write-LogMessage -API 'Standards' -tenant $Tenant -message "Autopilot profile '$($Settings.DisplayName)' do not match expected configuration" -sev Info
135132
}
136133
}
137134
}

Modules/CippExtensions/Public/NinjaOne/Invoke-NinjaOneTenantSync.ps1

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,21 @@ function Invoke-NinjaOneTenantSync {
77
$StartQueueTime = Get-Date
88
Write-Information "$(Get-Date) - Starting NinjaOne Sync"
99

10-
# Stagger start
11-
# Check Global Rate Limiting
12-
$CurrentMap = Get-ExtensionRateLimit -ExtensionName 'NinjaOne' -ExtensionPartitionKey 'NinjaOneMapping' -RateLimit 5 -WaitTime 10
10+
$MappingTable = Get-CIPPTable -TableName CippMapping
11+
$CurrentMap = (Get-CIPPAzDataTableEntity @MappingTable -Filter "PartitionKey eq 'NinjaOneMapping'")
12+
$CurrentMap | ForEach-Object {
13+
if ($Null -ne $_.lastEndTime -and $_.lastEndTime -ne '') {
14+
$_.lastEndTime = (Get-Date($_.lastEndTime))
15+
} else {
16+
$_ | Add-Member -NotePropertyName lastEndTime -NotePropertyValue $Null -Force
17+
}
18+
19+
if ($Null -ne $_.lastStartTime -and $_.lastStartTime -ne '') {
20+
$_.lastStartTime = (Get-Date($_.lastStartTime))
21+
} else {
22+
$_ | Add-Member -NotePropertyName lastStartTime -NotePropertyValue $Null -Force
23+
}
24+
}
1325

1426
$StartTime = Get-Date
1527

0 commit comments

Comments
 (0)