0

This article “Get-DkimSigningConfig Helper PowerShell Script August 2020” was initially authored on 8-12-2020 (August 12, 2020) and is intended as a free starter guide to understanding concepts and configurations related to Office 365 DKIM, DMARC and more.

Please be advised, this information is not guaranteed to be suitable for complete use as a 100% fool-proof guide to configuration of your email deliverability or DKIM or DMARC. Please note that there is a risk you can cause the ceasing of all proper inbound or outbound email and we would advise you to be very careful.

Many important steps are being omitted from this guide to keep it parsable for the general audience and to avoid making it seem like this is a 100% fool-proof guide. We recommend you do as follows instead: Request a custom Office 365 DKIM Training Workshop using this link especially for any questions or concerns you have about following this guide.

If you get the feeling you might be in for trouble if you try to touch the email settings without being properly prepared, you are RIGHT. You risk making all email stop going in and out, this could cause major losses in your organization. This free Office 365 training guide is intended as a reference guide. However, relying on free guides is not worth it. Before touching your email system, you should check with us about any doubts and questions you have, and best practice guidelines regarding this sort of thing.

Please note, this guide is heavily inspired by and might borrow steps from the following source: (source article – verboon[dot]info) and the original PowerShell script might possible be credited to (source article – verboon[dot]info) as well, please note we do not necessarily endorse that specific website and we do not guarantee the script and steps will work in your specific case. Be careful and beware.

Dynamics Edge is not responsible for any losses attributable to you using the information anywhere on this page or this website. Dynamics Edge does not necessarily endorse nor is necessarily affiliated with any sources mentioned in this article. Moreover, even our services in general do not magically cause us to be liabale for what you do based on what we tell you, everything is AS-IS – on this website and in general – and we advise that you attempt everything on non-production, test environments first and test Office 365 tenants first, even whether you are doing it based on this free article or whether you even have started engaging our services. Please ESPECIALLY take this precaution about the free information we are providing on this page. The information is intentionally incomplete, because some steps vary based on your specific scenario.

If you are looking for detailed instructions how to enable DKIM in Microsoft Office 365 and you have been confused about CNAME DNS TXT and so forth and are not sure what you’re supposed to even put for CNAMEs at all, how to check if you did it right, and so forth, please continue reading on.

Prerequisites

Windows PowerShell
Access to DNS
Access to Exchange Online through PowerShell
PowerShell Script Validate-DkimConfig.ps1 download. You can see the script at the bottom of this post.

Connecting to Exchange Online

First you should connect to Exchange Online using PowerShell as below:

$UserCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking

Is Multi-Factor-Authentication (MFA) Enabled In Your Case?

In the case that you have Multi-factor authentication (MFA) enabled, make sure you follow these instructions below instead to connect to Exchange Online. Once you have the MFA enabled module installed, you can run the below command and once that has loaded run Connect-EXOPSSession.

Please note that this command for MFA in particular was not tested exhaustively by Dynamics Edge – the non-MFA version was tested and found to be working – we cannot guarantee either version will work for you. If it is acceptable to temporarily suspend your MFA for the specific user, you may want to consider turning off MFA for the user for a specific, small window of time, presuming this is allowed for your company policies. Do not disable MFA and do not do anything in this article without professional guidance, both from your company, and from an expert such as Dynamics Edge. We highly recommend that you request a custom Office 365 DKIM Training and Consulting Workshop using this link especially for any questions or concerns you have about following this guide, and what you may need to know that is not spelled out for you in this guide.

$CreateEXOPSSession = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -ErrorAction SilentlyContinue -Force | Select -Last 1).DirectoryName
. “$CreateEXOPSSession\CreateExoPSSession.ps1”

If all went fine, you should see something confirming your successful issuing of this command.

Check current DKIM configuration status

Run the following command below to see current DKIM configuration

Get-DkimSigningConfig

Gather required settings for DNS

To enable DKIM we must add two CNAME records to DNS, we use the Validate-DkimConfig cmdlet to provide us with the detailed information we must set in DNS

Load the functions included in validate-dkimconfig.ps1 and then run validate-dkimconfig.

Replace “example.com” below with your domain.

PS C:\temp> . .\Validate-DkimConfig.ps1
PS C:\temp> validate-dkimconfig -domain example.com

Please note the dot at the beginning, a space, and then another dot before the slash – this is NOT a typo. It is

. .\Validate-DkimConfig.ps1

Typing in just one dot, or in other words typing .\Validate-DkimConfig.ps1 you should get an error when then typing validate-dkimconfig -domain example.com

This is NOT a typo.

It’s intentionally like this:
. .\Validate-DkimConfig.ps1

Registering DKIM in DNS

Supposing you host DNS in Azure, add the CNAMES there. If you use another DNS, use your dNS control panel to add them over there.

Then run the following command again below.

PS C:\temp> validate-dkimconfig -domain example.com

If you check that the DNS records are active, you should see output saying the keys match andthe CNAMES are active and match the CNAMES for the O365 DKIM Config.

Enable DKIM

Now that we have the DNS records published, it is time to enable DKIM. This is actually done by running the following command below:

New-DkimSigningConfig -DomainName example.com -Enabled $true
Get-DkimSigningConfig

Finally, you should run the following command again to validate all is configured correctly as below.

PS C:\temp> validate-dkimconfig -domain example.com

Request a custom Office 365 DMARC Training Workshop using this link.

Request a custom Office 365 Email Deliverability Training Workshop using this link.

We also recommend you run the validate-dkimconfig -domain command with your onmicrosoft.com domain as well to really understand what this script does. We also recommend you check every line of the script yourself to make sure it does what you think it does. Some organizations do not allow the use of scripts found on the internet – check with your company policy on this. If your company requires a 3rd party professional to do things like this, we highly recommend that you request a custom Office 365 DKIM Training and Consulting Workshop using this link

Please note as of 8-12-2020 we found the script for our purposes largely useful. We do not guarantee that it will work for your scenarios nonetheless, but we can tell you that for some of our purposes we had success.

Note that as of 8-12-2020 we noticed the use of 2048 bit keys, and some lines in the PowerShell were causing false negative reports on key mismatches. Some of the original code in the original PowerShell truncates the 2048 bit keys. This can possibly be solved with small modification to the script.

We solved this for our purposes (and it worked) by replacing all instances of any lines similar to the below:

$txt1Dns.Strings[0].Trim()

with something along the lines of this below:

[system.String]::Join("", $txt1Dns.Strings.Trim()).Trim()

This modification should be the case in our version as of 8-12-2020, if you use specifically our version when you test in your non-production or sandbox Office 365 tenants. We cannot guarantee we made modifications that will solve all possible scenarios, nor can we guarantee this script works at all for your scenario. We are also leaving out a LOT from this guide – for any questions or concerns we recommend you request a custom Office 365 DKIM Training and Consulting Workshop using this link.

Script start:

function Validate-DkimConfig
{
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$false)]
        [string]$domain,
        [parameter(Mandatory=$false)]
        [switch]$showAll
    )

    $notFound=$false;

    if ($domain) {
        $config = Get-DkimSigningConfig -Identity $domain -ErrorAction SilentlyContinue
        if ($config) {
            Validate-DkimConfigDomain $config -showAll:$showAll
        } else {
            $notFound=$true;
        }
    }
    else {
        $configs = Get-DkimSigningConfig
        if ($configs -and $configs.Count -gt 0) {
            foreach ($config in $configs) { Validate-DkimConfigDomain $config -showAll:$showAll}
        } else { 
            Write-Host
            Write-Host "No DKIM Signing Configs Found" -ForegroundColor Yellow
            Write-Host
        }
    }

    if ($notFound -and $domain) {
        Write-Host
        Write-Host "Config for domain $($domain) Not Found" -ForegroundColor Yellow
        Write-Host

        if (!$domain.EndsWith("onmicrosoft.com") -and !$domain.EndsWith("microsoftonline.com")) {
            Validate-DkimCnameOnly $domain
        }
    }
}

# Performs the main validation of a configuration
function Validate-DkimConfigDomain
{
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$true)]
        $config,
        [parameter(Mandatory=$false)]
        [switch]$showAll
    )

    # Display the configuration
    $domain = $config.Domain;
    $onmicrosoft = if ($domain.EndsWith("onmicrosoft.com") -or $domain.EndsWith("microsoftonline.com")) { $true } else { $false }
    $actions = @()

    Write-Host "Config for $domain Found..." -ForegroundColor Yellow
    if ($showAll) {
        $config | fl
    }
    else {
        $config | Select Identity, Enabled, Status, Selector1CNAME, Selector2CNAME, KeyCreationTime, LastChecked, RotateOnDate, SelectorBeforeRotateonDate, SelectorAfterRotateonDate | fl
    }

    if (!$config.Enabled) {
        Write-Host "Config $($config.Name) Not Enabled" -ForegroundColor Yellow
        Write-Host
        $actions += "Config $($config.Name) needs to be Enabled"
    }

    # Get the DNS ENtries
    Write-Host "Locating DNS Entries..." -ForegroundColor Yellow
    $cname1 = "selector1._domainkey.$($domain)"
    $cname2 = "selector2._domainkey.$($domain)"
    $txt1 = $config.Selector1CNAME;
    $txt2 = $config.Selector2CNAME;

    $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue
    $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue
    $txt1Dns = Resolve-DnsName -Name $txt1 -Type TXT -ErrorAction SilentlyContinue
    $txt2Dns = Resolve-DnsName -Name $txt2 -Type TXT -ErrorAction SilentlyContinue

    # Validate Entries
    Write-Host "Validating DNS Entries..." -ForegroundColor Yellow    

    Write-Host    
    Write-Host "Config CNAME1 : $($config.Selector1CNAME)"
    if (!$onmicrosoft) {
        if ($cname1Dns -and $cname1Dns.NameHost) {
            Write-Host "DNS    CNAME1 : $($cname1Dns.NameHost)"
            Write-Host "TXT Hostname  : $($cname1)"  
            $match = if ($config.Selector1CNAME.Trim() -eq $cname1Dns.NameHost.Trim()) { $true } else { $false }
            if ($match) { 
                write-host "Matched       : $($match)" -ForegroundColor Green
            } else {
                write-host "Matched       : $($match)" -ForegroundColor Red
                $actions += "Publish CNAME TXT Entry $($cname1) with value $($txt1)"
            }
        }
        else {
            write-host "DNS NotFound  : $($cname1)" -ForegroundColor Red
            $actions += "Publish DNS CNAME Entry $($cname1) with value $($txt1)"
        }              
    }

    Write-Host
    Write-Host "Config CNAME2 : $($config.Selector2CNAME)"
    if (!$onmicrosoft) {
        if ($cname2Dns -and $cname2Dns.NameHost) {
            Write-Host "DNS    CNAME2 : $($cname2Dns.NameHost)"
            Write-Host "TXT Hostname  : $($cname2)"
            $match = if ($config.Selector2CNAME.Trim() -eq $cname2Dns.NameHost.Trim()) { $true } else { $false }
            if ($match) { 
                write-host "Matched       : $($match)" -ForegroundColor Green
            } else {
                write-host "Matched       : $($match)" -ForegroundColor Red
                $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)"
            }
        }
        else {
            write-host "DNS NotFound  : $($cname2)" -ForegroundColor Red
            $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)"
        }        
    }

    Write-Host
    Write-Host "Config   TXT1 : $($config.Selector1PublicKey)"
    if ($txt1Dns -and $txt1Dns.Strings) {
        $key = [system.String]::Join("", $txt1Dns.Strings.Trim()).Trim()
        Write-Host "DNS      TXT1 : $($key)"
        $match = if (Compare-PublicAndConfigKeys $key $config.Selector1PublicKey) { $true } else { $false }
        if ($match) { 
            write-host "Key Match     : $($match)" -ForegroundColor Green
        } else {
            write-host "Key Match     : $($match)" -ForegroundColor Red
            $actions += "Public Key in TXT Entry $($txt1) needs to be republished..."
        }
    }
    else {
        write-host "DNS NotFound  : $($txt1)" -ForegroundColor Red
        $actions += "Microsoft TXT Entry $($txt1) not found so Signing Config needs to be recreated..."
    }

    Write-Host
    Write-Host "Config   TXT2 : $($config.Selector2PublicKey)"
    if ($txt2Dns -and $txt2Dns.Strings) {
        $key = [system.String]::Join("", $txt2Dns.Strings.Trim()).Trim()
        Write-Host "DNS      TXT2 : $($key)"
        $match = if (Compare-PublicAndConfigKeys $key $config.Selector2PublicKey) { $true } else { $false }
        if ($match) { 
            write-host "Key Match     : $($match)" -ForegroundColor Green
        } else {
            write-host "Key Match     : $($match)" -ForegroundColor Red
            $actions += "Public Key in TXT Entry $($txt2) needs to be republished..."
        }        
    }
    else {
        write-host "DNS NotFound  : $($txt2)" -ForegroundColor Red
        $actions += "Microsoft TXT Entry $($txt2) not found so Signing Config needs to be recreated..."
    }

    # Write out neccessary Actions
    Write-Host
    if ($actions.Count -gt 0) {
        Write-Host "Required Actions..." -ForegroundColor Yellow
        foreach ($action in $actions) { write-host $action}
    }
}

# Performs a validation of the Dkim CNAMES
function Validate-DkimCnameOnly
{
    [cmdletbinding()]
    Param(
        [parameter(Mandatory=$true)]
        $domain
    )

    # Get the DNS Entries
    Write-Host "Locating DNS Entries..." -ForegroundColor Yellow
    $cname1 = "selector1._domainkey.$($domain)"
    $cname2 = "selector2._domainkey.$($domain)"

    $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue
    $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue

    Write-Host    

    if ($cname1Dns) {
        Write-Host "DNS CNAME1 : $($cname1)" -ForegroundColor Green
        Write-Host "Host Value : $($cname1Dns.NameHost)"
    }
    else {
        write-host "CNAME1 NotFound  : $($cname1)" -ForegroundColor Red
    }

    if ($cname2Dns) {
        Write-Host "DNS CNAME2 : $($cname2)" -ForegroundColor Green
        Write-Host "Host Value : $($cname2Dns.NameHost)"
    }
    else {
        write-host "CNAME2 NotFound  : $($cname2)" -ForegroundColor Red
    }           

    Write-Host
}

# Compares public and published keys
function Compare-PublicAndConfigKeys([string] $publicKey, [string] $configKey)
{
    $match = $false;

    if (![string]::IsNullOrWhiteSpace($publicKey) -and ![string]::IsNullOrWhiteSpace($configKey)) {     
        $regex = "p=(.*?);"
        $foundPublic = $publicKey -match $regex
        $publicValue = if ($foundPublic) { $matches[1] } else { $null }
        $foundConfig = $configKey -match $regex
        $configValue = if ($foundConfig) { $matches[1] } else { $null } 

        if ($foundPublic -and $foundConfig) {
            if ($publicValue.Trim() -eq $configValue.Trim()) {
                $match = $true;
            }
        }
    }

    $match;
}

We hope you enjoyed this and found this helpful.

It is risky to attempt following through with this guide or this configuration yourself, and if you were wondering if things might be missing from this guide – yes there are things missing and you may have issues doing this yourself. You may even have issues doing this on a non-production, test domain! So if possible, we strongly recommend you request a custom Office 365 DKIM Training and Consulting Workshop using this link.

Have a Question ?

Fill out this short form, one of our Experts will contact you soon.

Call Us Today For Your Free Consultation