Get-DmarcRecord.psm1
<#
.HelpInfoURI 'https://github.com/Cyber-Jacob/Get-DMARCRecord/blob/main/Help/Get-DMARCRecord.md' #> function Get-DMARCRecord { param ( [Parameter( Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, HelpMessage="Enter a list of domains. Can be a singular domain, a path, or a PS variable containing a list of domains." )] [Alias("Domains","List")] $Name, [Parameter( Mandatory=$false)] [string]$Server, [Parameter( Mandatory=$false)] [switch]$DisplayErrors, [Parameter( Mandatory=$false)] [switch]$ListUnsuccessfulDomains, [Parameter( Mandatory=$false)] [switch]$ListSuccessfulDomains, [Parameter( Mandatory=$false)] [switch]$CountRecords ) begin { <#Initialize counting variables and processing arrays#> $successful = 0 $failures = 0 $masterrecord = @() $errors = @() $unsuccessful_domains = @() $successful_domains = @() $splat_parameters = @{ 'Type' = 'TXT' 'ErrorAction' = 'Stop' } if ($PSBoundParameters.ContainsKey('Server')) { $splat_parameters['Server'] = $Server } if (-not ($Name -is [array]) -and ($Name -like "*\*" -or $Name -like "*/*")){ #This block targets potential file paths versus a single domain or set of domains. if (Test-Path $Name -PathType Leaf) { $Name = Get-Content -Path $Name } else { Write-Error "Path '$Name' does not exist." } } } process { foreach ($domain in $Name) { <#Set the domain name for the Resolve-DnsName query string by editing splat_parameters. Having this here allows us to parse domains from the position 0 Name parameter as singular items, or as many can fit into a variable or file.#> $splat_parameters['Name'] = "_dmarc.$domain" try { <#Query statement to check DMARC records.#> $query = resolve-dnsname @splat_parameters <#If the query we are returned contains _dmarc. as part of its' subdomain and a text record matching "V=DMARC1", then we treat it as valid and successful Write the query to show the user what is happening and let them know what they see in terms of valid records.#> if ($query.Name -match "_dmarc." -and $query.Type -match "TXT" -and $query.Strings -match "v=DMARC1") { $successful += 1 $masterrecord += $query write-output $query $successful_domains += $domain } elseif ($query.Name -notcontains "_dmarc." -or $query.Type -notcontains "TXT") { <#Determine if there is no DMARC record; in some cases DNS servers will reply with different record types like an SOA record. We will count this as NO DMARC found instead of a none-dmarc record.#> $failures += 1 $errors += [PSCustomObject]@{ Domain = $domain Message = "_dmarc.$domain : No DMARC record found for $domain" } $unsuccessful_domains += $domain Write-Error -Message "_dmarc.$domain : No DMARC record found for $domain" } else { <#add the query to the failed section, count it as an error, and log the error if there is a text record but it isn't a technically valid DMARC record.#> $failures += 1 $errors += [PSCustomObject]@{ Domain = $domain Message = "_dmarc.$domain : None-DMARC record found for $domain" } $unsuccessful_domains += $domain Write-Error -Message "_dmarc.$domain : None-DMARC record found for $domain" } } catch { <#If a terminating exception happens, count it as an error and log the error. Since we are using -erroraction stop, Resolve-DNSName terminates in an error when a domain that is queried doesn't exist. This will happen if there is NO record of any type published at _dmarc.$domain. The error handling above addresses invalid TXT records placed on _dmarc.$domain.#> $failures += 1 $errors += [PSCustomObject]@{ Domain = $domain Message = $_.Exception.Message } $unsuccessful_domains += $domain Write-Error -Message "$_" } } } End { $total_records = $failures + $successful if ($PSBoundParameters.ContainsKey("CountRecords")) { Write-Output "$total_records domain(s) processed." Write-Output "$successful DMARC record(s) found." Write-Output "$failures DMARC record(s) not found." Write-Output "See successful domains with -ListSuccessfulDomains, see unsuccessful domains with -ListUnsuccessfulDomains, get error information with -DisplayErrors, suppress this explanation by omitting the -CountRecords option." } if ($PSBoundParameters.Containskey("DisplayErrors")){ Write-Output "====================" Write-Output "Errors are: `n" $errors | ForEach-Object {Write-Output $_} } if ($PSBoundParameters.ContainsKey("ListUnsuccessfulDomains")){ Write-Output "====================" Write-Output "Domains without DMARC records: `n" $unsuccessful_domains | ForEach-Object {Write-Output $_} } if ($PSBoundParameters.Containskey("ListSuccessfulDomains")) { Write-Output "====================" Write-Output "Domains with DMARC records: `n " $successful_domains | ForEach-Object {Write-Output $_} } } } Export-ModuleMember -Function Get-DMARCRecord |