Public/Import-IntuneNotificationTemplate.ps1
|
function Import-IntuneNotificationTemplate { <# .SYNOPSIS Imports notification message templates from JSON templates .DESCRIPTION Reads templates from Templates/Notifications and creates notificationMessageTemplates with localized messages. .PARAMETER TemplatePath Path to the notifications template directory (defaults to Templates/Notifications) .EXAMPLE Import-IntuneNotificationTemplate #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter()] [string]$TemplatePath, [Parameter()] [switch]$RemoveExisting ) if (-not $TemplatePath) { $TemplatePath = Join-Path -Path $script:TemplatesPath -ChildPath "Notifications" } if (-not (Test-Path -Path $TemplatePath)) { Write-Warning "Notification template directory not found: $TemplatePath" return @() } $templateFiles = Get-HydrationTemplates -Path $TemplatePath -Recurse -ResourceType "notification template" if (-not $templateFiles -or $templateFiles.Count -eq 0) { Write-Warning "No notification templates found in: $TemplatePath" return @() } $results = @() # Prefetch existing templates for duplicate detection $existingTemplates = @{} try { Get-GraphPagedResults -Uri "beta/deviceManagement/notificationMessageTemplates" -ProcessItems { param($items) foreach ($tmpl in $items) { if ($tmpl.displayName -and -not $existingTemplates.ContainsKey($tmpl.displayName)) { $existingTemplates[$tmpl.displayName] = @{ Id = $tmpl.id } } } } } catch { $existingTemplates = @{} } # Remove existing notification templates if requested # SAFETY: Only delete templates whose names match our template files # Note: Notification templates don't support description field, so we match by name if ($RemoveExisting) { # Build list of template names from our JSON files for name-based matching $templateNames = @{} foreach ($templateFile in $templateFiles) { try { $templateContent = Get-Content -Path $templateFile.FullName -Raw -Encoding utf8 | ConvertFrom-Json if ($templateContent.displayName) { $templateNames[$templateContent.displayName] = $true } } catch { Write-Verbose "Could not read template file: $($templateFile.FullName)" } } foreach ($templateName in $existingTemplates.Keys) { $templateInfo = $existingTemplates[$templateName] # Safety check: Only delete if the name matches one of our template files $escapedPrefix = [regex]::Escape($script:ImportPrefix) $nameForLookup = $templateName -replace "^$escapedPrefix", '' if (-not ($templateNames.ContainsKey($templateName) -or $templateNames.ContainsKey($nameForLookup))) { Write-Verbose "Skipping '$templateName' - not in hydration kit templates" continue } if ($PSCmdlet.ShouldProcess($templateName, "Delete notification template")) { try { Invoke-MgGraphRequest -Method DELETE -Uri "beta/deviceManagement/notificationMessageTemplates/$($templateInfo.Id)" -ErrorAction Stop Write-HydrationLog -Message " Deleted: $templateName" -Level Info $results += New-HydrationResult -Name $templateName -Type 'NotificationTemplate' -Action 'Deleted' -Status 'Success' } catch { $errMessage = Get-GraphErrorMessage -ErrorRecord $_ Write-HydrationLog -Message " Failed: $templateName - $errMessage" -Level Warning $results += New-HydrationResult -Name $templateName -Type 'NotificationTemplate' -Action 'Failed' -Status "Delete failed: $errMessage" } } else { Write-HydrationLog -Message " WouldDelete: $templateName" -Level Info $results += New-HydrationResult -Name $templateName -Type 'NotificationTemplate' -Action 'WouldDelete' -Status 'DryRun' } } return $results } foreach ($templateFile in $templateFiles) { try { $template = Get-Content -Path $templateFile.FullName -Raw -Encoding utf8 | ConvertFrom-Json $displayName = "$($script:ImportPrefix)$($template.displayName)" if (-not $template.displayName) { Write-Warning "Template missing displayName: $($templateFile.FullName)" $results += New-HydrationResult -Name $templateFile.Name -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'Failed' -Status 'Missing displayName' continue } if ($existingTemplates.ContainsKey($displayName)) { Write-HydrationLog -Message " Skipped: $displayName" -Level Info $results += New-HydrationResult -Name $displayName -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'Skipped' -Status 'Already exists' continue } # Check for legacy unprefixed template to prevent duplicates on upgrade if ($existingTemplates.ContainsKey($template.displayName)) { Write-HydrationLog -Message " Skipped: $displayName (legacy match: '$($template.displayName)')" -Level Info $results += New-HydrationResult -Name $displayName -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'Skipped' -Status 'Already exists (legacy name)' continue } # Split template into main body and localized messages $localizedMessages = @() if ($template.localizedMessages) { $localizedMessages = $template.localizedMessages $template.PSObject.Properties.Remove('localizedMessages') | Out-Null } $importBody = Copy-DeepObject -InputObject $template # Apply import prefix to body if ($importBody.displayName) { $importBody.displayName = $displayName } if ($PSCmdlet.ShouldProcess($displayName, "Create notification template")) { $newTemplate = Invoke-MgGraphRequest -Method POST -Uri "beta/deviceManagement/notificationMessageTemplates" -Body ($importBody | ConvertTo-Json -Depth 50) -ContentType "application/json" -ErrorAction Stop Write-HydrationLog -Message " Created: $displayName" -Level Info # Create localized messages if present foreach ($loc in $localizedMessages) { try { $locBody = $loc | ConvertTo-Json -Depth 20 Invoke-MgGraphRequest -Method POST -Uri "beta/deviceManagement/notificationMessageTemplates/$($newTemplate.id)/localizedNotificationMessages" -Body $locBody -ContentType "application/json" -ErrorAction Stop } catch { Write-HydrationLog -Message " Failed to add localized message ($($loc.locale)): $($_.Exception.Message)" -Level Warning } } $results += New-HydrationResult -Name $displayName -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'Created' -Status 'Success' } else { Write-HydrationLog -Message " WouldCreate: $displayName" -Level Info $results += New-HydrationResult -Name $displayName -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'WouldCreate' -Status 'DryRun' } } catch { $errMessage = Get-GraphErrorMessage -ErrorRecord $_ Write-HydrationLog -Message " Failed: $($templateFile.Name) - $errMessage" -Level Warning $results += New-HydrationResult -Name $templateFile.Name -Path $templateFile.FullName -Type 'NotificationTemplate' -Action 'Failed' -Status $errMessage } } return $results } |