The Line Up

Serious topics like DNS and email security do indeed require us to get our ducks in a row. In this article, we discuss what DNS records should be in place for Exchange Online, why these records are needed, and how to validate that DNS is properly configured. First, we review DNS Domain components for Exchange Online, what the records do, review methods to query DNS records, and build a PowerShell script using the best method. With this script, we use PowerShell to create a report to verify that these records exist. The Domain Health Checker PowerShell module also provides some feedback for DNS records, which we can use to rate whether the domain follows best practices. We end with a report covering DNS health.

Duck One – DNS Domains, Records, and CompAuth

Mail flow and mail security rely on several DNS records to function properly.  Below is a list of the records that an organization would use for Exchange Online, and while not all are required, they should be configured for each Accepted Domain in Exchange Online. Here are those records and some additional concepts to keep in mind:

MX Record: Record used to lookup the destination mail server for an email domain.

Sender Policy Framework (SPF) Record: important for telling SMTP recipient servers what SMTP sending servers are authorized for a domain and as such. Without a properly configured SPF record a domain’s SMTP reputation is at risk. The SPF record exists in the form of a TXT record which could look like this:

v=spf1 -all

DKIM Record: Use in conjunction with an SPF record to reduce malicious spoofing. Sample DKIM Record:

Host name:            selector1._domainkey
Points to address or value:
TTL:                3600

DMARC Record: Works together with SPF and DKIM to authenticate a sender’s email server. Sample DMARC Record:   3600    IN      TXT     "v=DMARC1; p=none; pct=100;;; fo=1"

Note that there were recent changes to Microsoft 365 support which you can read about on this blog.

DNSSEC: Adds a level of protection to your email Domains.

CompAuth: Exchange Online checks the SPF, DMARC, and DKIM records and combines them into a concept known as CompAuth. CompAuth can determine if a message has been successfully authenticated.

On Demand Migration

Migrate all your workloads and Active Directory with one comprehensive Office 365 tenant-to-tenant migration solution.

Duck Two – The Preparation

Many ways exist to check DNS records and validate an organization’s DNS configuration:

Manually: The DNS Provider website checks each domain manually for the records and verifies that the txt is valid [free SPF checker]. The downside is knowing if a record is misconfigured, or even missing.

Built-In PowerShell: Use the Resolve-DnsName cmdlet to resolve DNS names for a domain. The downside is that the cmdlet does not provide feedback other than the DNS record itself.

NSLookup: Queries DNS records and can be used to resolve email DNS records.

DomainHealthChecker: Custom PowerShell module written and maintained by Martien van Dijk which can perform queries for email-related DNS records and also provides advisory feedback on these records. With ease of use and consistent updates since mid-2021, this module should be added to your PowerShell toolkit for DNS queries.

After validating DNS records, a chart of a DNS domain’s health is useful for reporting and auditing.

To overcome the limitations of the available methods, this article uses cmdlets from the Exchange Online and Domain Health Checker PowerShell modules to fetch data, analyze the results, and output an easy-to-read HTML chart.

Duck Three – PowerShell

Let’s look at the code to check a domain’s DNS records and assess an organization’s DNS health for Exchange Online.

After connecting to Exchange Online and loading the DomainHealthChecker module, we retrieve a list of Accepted Domains:

$AcceptedDomains = (Get-AcceptedDomain).Name

With a list of Accepted domains we can analyze the DNS configuration for each domain using a Foreach loop:

Foreach ($AcceptedDomain in $AcceptedDomains) {
$SpfRecord = Get-SPFRecord $AcceptedDomain
$DmarcRecord = Get-DMARCRecord $AcceptedDomain
$DkimRecord = Get-DKIMRecord $AcceptedDomain
$DnsSec = Get-DNSSec $AcceptedDomain

First up is the base MX record. We use a system of points to determine the health of a domain. In the report, the lower the point total for a domain, the healthier its status is. For example, for the MX record, if a record exists, then no points are added whereas if there isn’t an MX record, we add 2 points.  Accepted domains should always have an MX record. It is possible to have a domain without an MX record. Scoring these assessment of the DNS configuration raises awareness about potential issues for the domain.

Try {

              $MXRecord = Resolve-DnsName $AcceptedDomain -Server $DNSServer -Type MX -ErrorAction STOP
              $MX = 'X' # To be marked on the report for this domain
              $MXRecordResult = 0   # MX record found (o pts)
} Catch {
              $MXRecordResult = 2   # No MX record found (2 pts)

Next up is the SPF record, which requires some more analysis. We examine an SPF record (txt record, and not the deprecated SPF DNS type) using several criteria. One is to see if the record exists. Second, is the record over 255 characters? We check the length for the variable $SpfRecord using the SPFRecordLenght property(misspelled in the PS module – reported), but also to check if the record exists (length > 0). The Domain Health Checker module also provides an ‘advisory’ which can be used for additional scoring:

$SPFRecordLength = [int32]$SpfRecord.SPFRecordLenght
#SPF Length Analysis
If ($SPFRecordLength -eq 0) {
    $SpfRecordLengthResult = 2 # Red / Risk
    $SpfRecordExists = $False
If (($SPFRecordLength -gt 0) -and ($SPFRecordLength -lt 150)) {
    $SpfRecordLengthResult = 0 # Green / No Risk
    $SpfRecordExists = $True
If ($SPFRecordLength -gt 200) {
    $SpfRecordExists = $True
    If ($SPFRecordLength -lt 250) {
        $SpfRecordLengthResult = 1 # Yellow / Low Risk
    } Else {
        $SpfRecordLengthResult = 2 # Red / Risk / Too large

Now we rate the Advisory. There are three levels and the script adds an increased number of points to the domain’s overall score based on the level:

# If the record exists, check the
If ($SpfRecordExists) {
    # SPF Advisory Analysis
    If ($SPFRecord.SPFAdvisory -like '*not sufficiently*') {
        $SPFAdvisoryLevel = 1 # Yellow / Low Risk
    } Else {
        $SPFAdvisoryLevel = 0 # Green / No Risk
    If ($SPFRecord.SPFAdvisory -eq 'Domain does not have an SPF record. To prevent abuse of this domain, please add an SPF record to it.') {
        $SPFAdvisoryLevel = 2 # Red / Risk

Last, for reporting purposes, we take the overall score for the SPF record and apply an appropriate color code (Yellow representing Caution while Red is a Warning).

If ($SpfRecordExists) {
    $SPF = 'X'  # To be marked on the report for this domain
    $SpfOverallResults = $SpfRecordLengthResult + $SPFAdvisoryLevel
    If ($SpfOverallResults -eq 4) { $SPFColor = "$Red" }
    If (($SpfOverallResults -gt 0) -and ($SpfOverallResults -lt 4)) { $SPFColor = "$Yellow" }

Next, we analyze the data stored in the $DmarcRecord variable. The script checks first to see if the record exists, and if it exists and what is the DMARC ‘p’ value:

# DMARC Existence
If ([string]::IsNullOrWhiteSpace($DmarcRecord.DmarcRecord) ) {
    $DmarcRecordExists = $False
    $DmarcColor = "$Red"
} Else {
    $DmarcRecordExists = $True                
    $DmarcExistResult = 0
If ($DmarcRecordExists) {
    # DMARC Record setting check / rating
$DMARCAdvisoryLevel  = Switch ($DmarcRecord.DmarcRecord) {
              { $_ -like "*p=none*"} {"2"}
              { $_ -like "*p=quarantine*"} {"1"}
              { $_ -like "*p=reject*"} {"0"}
    # Prep for chart
    $Dmarc = 'X' # To be marked on the report for this domain
    $DmarcOverallResults = $DMARCExistResult + $DMARCAdvisoryLevel
    If ($DmarcOverallResults -eq 1) { $DmarcColor = "$Yellow" }
    If ($DmarcOverallResults -eq 2) { $DmarcColor = "$Red" }

The last check validates if the DNS domain is secured according to the DNS SEC standard, which is now supported by Exchange Online – see the ICANN standard and this article for further information.

If ($DnsSec -like '*DNSSEC is enabled on your domain.') {
              $DnsSec = 'X' # To be marked on the report for this domain
              $DNSSecResult = 0
} Else {
              $DNSSecResult = 2
              $DnsSec = ''

The Last Duck – The Report

Taking all of the above data, we can convert it into a chart using either straight PowerShell to output line of an HTML file or use the PSWriteHTML module. With the first method, I created a sample chart (Figure 1)

Getting Your DNS Ducks in a Row
Figure 1: Sample output from a DNS assessment

Figure 1: Sample output from a DNS assessment

All the Ducks in a Row

At the beginning, we discussed reviewing DNS settings for Exchange Online before explaining how to discover, rate, and report the DNS data using PowerShell. The end result is a report that administrators can use to validate a good configuration or to find flaws to fix (evident in Figure 1). With malicious actors constantly attempting new attack vectors, the least we can do is to make sure our DNS Ducks are in a Row. Hopefully, the script (downloadable from GitHub) will help you to do just that.

About the Author

Damian Scoles

Damian Scoles is an eight-time Microsoft MVP, specializing in Exchange, Office 365 and PowerShell. He is currently based out of the Chicago area and started out managing Exchange 5.5 and Windows NT. He has worked with Office 365 since BPOS and has experience with Azure AD, Security and Compliance Admin Centers, and Exchange Online. Contributions to the community include helping on TechNet forums, creating PowerShell scripts that are located in the TechNet Gallery, writing detailed PowerShell / Office365 / Exchange blog articles (, tweets ( and creating PowerShell videos on YouTube ( He has written five PowerShell books and is actively working on the Microsoft 365 Security for IT Pros book as well.

Leave a Reply