All scripts
Microsoft 365 719
SPF, DKIM, and DMARC Record Checker
Checks SPF, DKIM, and DMARC DNS records for one or more domains and flags common mistakes, like a missing rua tag or a DMARC policy still stuck on p=none. Handy for a quick health check when onboarding a new domain into Microsoft 365 or doing a periodic deliverability review.
Test-EmailAuthenticationRecords.ps1
<#
.SYNOPSIS
Checks SPF, DKIM, and DMARC DNS records for one or more domains.
.DESCRIPTION
Queries public DNS for a list of domains and reports whether SPF, DKIM,
and DMARC records exist, and flags a few common mistakes: more than one
SPF record, an SPF record missing spf.protection.outlook.com, a DMARC
policy still set to p=none, and a DMARC record with no rua reporting
address. Useful for a quick health check before or after onboarding a
new domain into Microsoft 365, or during a periodic deliverability
review. DKIM selectors default to the two Microsoft 365 uses
(selector1, selector2) but you can pass your own.
.PARAMETER Domain
One or more domains to check, for example contoso.com.
.PARAMETER DkimSelector
DKIM selectors to check. Defaults to selector1 and selector2, the
Microsoft 365 defaults.
.EXAMPLE
.\Test-EmailAuthenticationRecords.ps1 -Domain contoso.com
.EXAMPLE
.\Test-EmailAuthenticationRecords.ps1 -Domain contoso.com, fabrikam.com -DkimSelector selector1, selector2, google
.AUTHOR
Shehryar Hassan
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string[]]$Domain,
[string[]]$DkimSelector = @('selector1', 'selector2')
)
function Get-SpfResult {
param([string]$Domain)
$result = [ordered]@{
Found = $false
RecordCount = 0
Record = $null
Issues = New-Object System.Collections.Generic.List[string]
}
try {
$txtRecords = Resolve-DnsName -Name $Domain -Type TXT -ErrorAction Stop |
Where-Object { $_.Strings -join '' -match '^v=spf1' }
}
catch {
$result.Issues.Add("DNS lookup failed: $($_.Exception.Message)")
return [pscustomobject]$result
}
$result.RecordCount = ($txtRecords | Measure-Object).Count
if ($result.RecordCount -eq 0) {
$result.Issues.Add('No SPF record found')
return [pscustomobject]$result
}
$result.Found = $true
$result.Record = ($txtRecords[0].Strings -join '')
if ($result.RecordCount -gt 1) {
$result.Issues.Add('More than one SPF record published, only one is allowed')
}
if ($result.Record -notmatch 'spf\.protection\.outlook\.com') {
$result.Issues.Add('SPF record does not include spf.protection.outlook.com')
}
if ($result.Record -notmatch '-all|~all') {
$result.Issues.Add('SPF record has no -all or ~all qualifier at the end')
}
return [pscustomobject]$result
}
function Get-DkimResult {
param([string]$Domain, [string[]]$Selectors)
$found = New-Object System.Collections.Generic.List[string]
foreach ($selector in $Selectors) {
$name = "$selector._domainkey.$Domain"
try {
$record = Resolve-DnsName -Name $name -Type CNAME -ErrorAction Stop
if ($record) {
$found.Add($selector)
}
}
catch {
continue
}
}
[pscustomobject]@{
Found = ($found.Count -gt 0)
SelectorsFound = $found -join ', '
SelectorsChecked = $Selectors -join ', '
}
}
function Get-DmarcResult {
param([string]$Domain)
$result = [ordered]@{
Found = $false
Record = $null
Issues = New-Object System.Collections.Generic.List[string]
}
$name = "_dmarc.$Domain"
try {
$txtRecords = Resolve-DnsName -Name $name -Type TXT -ErrorAction Stop |
Where-Object { $_.Strings -join '' -match '^v=DMARC1' }
}
catch {
$result.Issues.Add('No DMARC record found')
return [pscustomobject]$result
}
if (-not $txtRecords) {
$result.Issues.Add('No DMARC record found')
return [pscustomobject]$result
}
$result.Found = $true
$result.Record = ($txtRecords[0].Strings -join '')
if ($result.Record -match 'p=none') {
$result.Issues.Add('Policy is still p=none, mail is monitored but not enforced')
}
if ($result.Record -notmatch 'rua=') {
$result.Issues.Add('No rua tag, aggregate reports are not being sent anywhere')
}
return [pscustomobject]$result
}
$report = foreach ($d in $Domain) {
Write-Host "Checking $d ..." -ForegroundColor Cyan
$spf = Get-SpfResult -Domain $d
$dkim = Get-DkimResult -Domain $d -Selectors $DkimSelector
$dmarc = Get-DmarcResult -Domain $d
[pscustomobject]@{
Domain = $d
SpfFound = $spf.Found
SpfIssues = ($spf.Issues -join '; ')
DkimFound = $dkim.Found
DkimSelectors = $dkim.SelectorsFound
DmarcFound = $dmarc.Found
DmarcIssues = ($dmarc.Issues -join '; ')
}
}
$report | Format-Table -AutoSize
Read it before you run it, and test in a safe tenant first.