Public/Exchange/Protection/New-ExTransportRuleFromJson.ps1
|
<#
.SYNOPSIS Creates Exchange Online transport (mail flow) rules from one or more JSON definition files. .DESCRIPTION This function reads JSON files describing Exchange Online transport rules and provisions them by splatting their content to New-TransportRule. The recommended layout is one JSON file per rule, which makes the rule set version-controllable and reproducible across tenants. Each JSON file must be a single object whose keys match New-TransportRule parameter names. If a rule with the same Name already exists in the tenant, the rule is skipped (use -Force to recreate it after deletion). Supports -WhatIf and -Confirm via SupportsShouldProcess. Use -GenerateCmdlets to emit the equivalent New-TransportRule cmdlets to a file instead of executing them. The function expects a connected ExchangeOnlineManagement session (Connect-ExchangeOnline). .PARAMETER Path Path to a JSON file or to a directory containing one or more JSON files. When a directory is provided, every *.json file in the directory is processed in alphabetical order. .PARAMETER Force If a rule with the same Name already exists, remove it before creating the new one. Without this switch, existing rules are skipped with a warning. .PARAMETER GenerateCmdlets If specified, the function generates the New-TransportRule cmdlets and saves them to a file instead of executing them. .PARAMETER OutputFile Path to the output file used by -GenerateCmdlets. If omitted while -GenerateCmdlets is set, defaults to a timestamped file in the user profile (cross-platform). Ignored unless -GenerateCmdlets is specified. .EXAMPLE Connect-ExchangeOnline New-ExTransportRuleFromJson -Path "C:\eop-rules\Block-Outbound-OnMicrosoft.json" Creates a single transport rule from the specified JSON file. .EXAMPLE New-ExTransportRuleFromJson -Path "C:\eop-rules" -WhatIf Lists every transport rule that would be created from the JSON files in the directory without applying any change. .EXAMPLE New-ExTransportRuleFromJson -Path "C:\eop-rules" -Force Creates every rule defined in the directory; pre-existing rules with the same Name are removed first. .EXAMPLE New-ExTransportRuleFromJson -Path "C:\eop-rules" -GenerateCmdlets -OutputFile "C:\temp\eop-rules.ps1" Emits the equivalent New-TransportRule cmdlets to the specified file without executing them. Useful for review or for running the rule provisioning from another host. .NOTES Prerequisites: - PowerShell 5.1 or later. JSON content is converted to an ordered hashtable through a local helper to stay compatible with Windows PowerShell 5.1, which does not support ConvertFrom-Json -AsHashtable. - ExchangeOnlineManagement module installed and an active session opened with Connect-ExchangeOnline before running the function (unless -GenerateCmdlets is specified, which only emits the cmdlet text). - The signed-in account must hold a role with permission to manage mail flow rules, typically Exchange Administrator or a member of the Organization Management role group. .LINK https://ps365.clidsys.com/docs/commands/New-ExTransportRuleFromJson #> function New-ExTransportRuleFromJson { [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [string[]]$Path, [Parameter(Mandatory = $false)] [switch]$Force, [Parameter(Mandatory = $false)] [switch]$GenerateCmdlets, [Parameter(Mandatory = $false)] [string]$OutputFile ) begin { [System.Collections.Generic.List[string]]$commands = @() [System.Collections.Generic.List[PSCustomObject]]$resultsArray = @() if ($GenerateCmdlets.IsPresent) { if ([string]::IsNullOrWhiteSpace($OutputFile)) { $userProfile = [Environment]::GetFolderPath('UserProfile') if ([string]::IsNullOrWhiteSpace($userProfile)) { $userProfile = $HOME } $OutputFile = Join-Path $userProfile "$(Get-Date -Format 'yyyy-MM-dd_HHmmss')-NewExTransportRuleFromJson-Commands.ps1" } } else { if (-not (Get-Command -Name New-TransportRule -ErrorAction SilentlyContinue)) { Write-Error 'New-TransportRule is not available. Connect to Exchange Online first with Connect-ExchangeOnline.' return } } } process { foreach ($p in $Path) { if (-not (Test-Path -LiteralPath $p)) { Write-Warning "Path not found: $p" continue } $item = Get-Item -LiteralPath $p if ($item.PSIsContainer) { $jsonFiles = Get-ChildItem -LiteralPath $p -Filter '*.json' -File | Sort-Object Name } else { $jsonFiles = @($item) } if (-not $jsonFiles -or $jsonFiles.Count -eq 0) { Write-Warning "No JSON file found in: $p" continue } foreach ($file in $jsonFiles) { Write-Host -ForegroundColor Cyan "Processing: $($file.FullName)" try { $jsonObject = Get-Content -LiteralPath $file.FullName -Raw -Encoding UTF8 | ConvertFrom-Json -ErrorAction Stop $ruleParams = ConvertTo-OrderedHashtable -InputObject $jsonObject } catch { Write-Host -ForegroundColor Red "[$($file.Name)] Invalid JSON: $($_.Exception.Message)" $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $null Status = 'InvalidJson' Error = $_.Exception.Message }) continue } if (-not $ruleParams.Contains('Name') -or [string]::IsNullOrWhiteSpace([string]$ruleParams['Name'])) { Write-Host -ForegroundColor Red "[$($file.Name)] Missing mandatory 'Name' property." $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $null Status = 'MissingName' Error = "Missing 'Name' property" }) continue } $ruleName = [string]$ruleParams['Name'] if ($GenerateCmdlets.IsPresent) { $commands.Add((ConvertTo-NewTransportRuleCommand -RuleParams $ruleParams)) $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'CmdletGenerated' Error = $null }) continue } $existing = Get-TransportRule -Identity $ruleName -ErrorAction SilentlyContinue if ($existing) { if ($Force.IsPresent) { if ($PSCmdlet.ShouldProcess($ruleName, 'Remove existing transport rule')) { try { Remove-TransportRule -Identity $ruleName -Confirm:$false -ErrorAction Stop Write-Host -ForegroundColor Yellow "[$ruleName] Existing rule removed (-Force)." } catch { Write-Host -ForegroundColor Red "[$ruleName] Failed to remove existing rule: $($_.Exception.Message)" $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'RemoveFailed' Error = $_.Exception.Message }) continue } } if (Get-TransportRule -Identity $ruleName -ErrorAction SilentlyContinue) { Write-Host -ForegroundColor Yellow "[$ruleName] Removal not performed (declined or skipped); creation aborted." $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'RemoveDeclined' Error = $null }) continue } } else { Write-Host -ForegroundColor Yellow "[$ruleName] Already exists. Use -Force to recreate." $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'AlreadyExists' Error = $null }) continue } } if ($PSCmdlet.ShouldProcess($ruleName, 'New-TransportRule')) { try { $null = New-TransportRule @ruleParams -ErrorAction Stop Write-Host -ForegroundColor Green "[$ruleName] Created." $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'Created' Error = $null }) } catch { Write-Host -ForegroundColor Red "[$ruleName] $($_.Exception.Message)" $resultsArray.Add([PSCustomObject]@{ File = $file.FullName Name = $ruleName Status = 'Failed' Error = $_.Exception.Message }) } } } } } end { if ($GenerateCmdlets.IsPresent -and $commands.Count -gt 0) { $commands | Out-File -FilePath $OutputFile -Encoding UTF8 Write-Host -ForegroundColor Cyan "Commands generated in file: $OutputFile" } return $resultsArray } } |