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 with descriptions for safety checks $existingTemplates = @{} try { $listUri = "beta/deviceManagement/notificationMessageTemplates" do { $existingResponse = Invoke-MgGraphRequest -Method GET -Uri $listUri -ErrorAction Stop foreach ($tmpl in $existingResponse.value) { if ($tmpl.displayName -and -not $existingTemplates.ContainsKey($tmpl.displayName)) { $existingTemplates[$tmpl.displayName] = @{ Id = $tmpl.id Description = $tmpl.description } } } $listUri = $existingResponse.'@odata.nextLink' } while ($listUri) } catch { $existingTemplates = @{} } # Build a simple name->id lookup for backwards compatibility in the import section $existingByName = @{} foreach ($key in $existingTemplates.Keys) { $existingByName[$key] = $existingTemplates[$key].Id } # Remove existing notification templates if requested # SAFETY: Only delete templates that have "Imported by Intune-Hydration-Kit" in description # Note: Notification templates may not always have descriptions, so we check what's available if ($RemoveExisting) { foreach ($templateName in $existingTemplates.Keys) { $templateInfo = $existingTemplates[$templateName] # Safety check: Only delete if created by this kit (has hydration marker in description) # Note: notification templates have 'description' field according to Graph API if (-not (Test-HydrationKitObject -Description $templateInfo.Description -ObjectName $templateName)) { Write-Verbose "Skipping '$templateName' - not created by Intune-Hydration-Kit" 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 = $template.displayName if (-not $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 ($existingByName.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 } # 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 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 } |