PSPasswordExpiryNotifications.psm1

function Get-HashMaxValue { 
    [CmdletBinding()]
    param ([Object] $hashTable,
        [switch] $Lowest)
    if ($Lowest) { return ($hashTable.GetEnumerator() | Sort-Object value -Descending | Select-Object -Last 1).Value } else { return ($hashTable.GetEnumerator() | Sort-Object value -Descending | Select-Object -First 1).Value }
}
function Get-HTML { 
    [CmdletBinding()]
    param ($text)
    $text = $text.Split("`r")
    foreach ($t in $text) { Write-Host $t }
}
function Send-Email { 
    [CmdletBinding(SupportsShouldProcess = $true)]
    param ([alias('EmailParameters')][System.Collections.IDictionary] $Email,
        [string] $Body,
        [string[]] $Attachment,
        [System.Collections.IDictionary] $InlineAttachments,
        [string] $Subject,
        [string[]] $To,
        [PSCustomObject] $Logger)
    try {
        if ($Email.EmailTo) { $EmailParameters = $Email.Clone() } else {
            $EmailParameters = @{EmailFrom  = $Email.From
                EmailTo                     = $Email.To
                EmailCC                     = $Email.CC
                EmailBCC                    = $Email.BCC
                EmailReplyTo                = $Email.ReplyTo
                EmailServer                 = $Email.Server
                EmailServerPassword         = $Email.Password
                EmailServerPasswordAsSecure = $Email.PasswordAsSecure
                EmailServerPasswordFromFile = $Email.PasswordFromFile
                EmailServerPort             = $Email.Port
                EmailServerLogin            = $Email.Login
                EmailServerEnableSSL        = $Email.EnableSsl
                EmailEncoding               = $Email.Encoding
                EmailEncodingSubject        = $Email.EncodingSubject
                EmailEncodingBody           = $Email.EncodingBody
                EmailSubject                = $Email.Subject
                EmailPriority               = $Email.Priority
                EmailDeliveryNotifications  = $Email.DeliveryNotifications
                EmailUseDefaultCredentials  = $Email.UseDefaultCredentials
            }
        }
    } catch {
        return @{Status = $False
            Error       = $($_.Exception.Message)
            SentTo      = ''
        }
    }
    $SmtpClient = [System.Net.Mail.SmtpClient]::new()
    if ($EmailParameters.EmailServer) { $SmtpClient.Host = $EmailParameters.EmailServer } else {
        return @{Status = $False
            Error       = "Email Server Host is not set."
            SentTo      = ''
        }
    }
    if ($EmailParameters.EmailServerPort) { $SmtpClient.Port = $EmailParameters.EmailServerPort } else {
        return @{Status = $False
            Error       = "Email Server Port is not set."
            SentTo      = ''
        }
    }
    if ($EmailParameters.EmailServerLogin) {
        $Credentials = Request-Credentials -UserName $EmailParameters.EmailServerLogin -Password $EmailParameters.EmailServerPassword -AsSecure:$EmailParameters.EmailServerPasswordAsSecure -FromFile:$EmailParameters.EmailServerPasswordFromFile -NetworkCredentials
        $SmtpClient.Credentials = $Credentials
    }
    if ($EmailParameters.EmailServerEnableSSL) { $SmtpClient.EnableSsl = $EmailParameters.EmailServerEnableSSL }
    $MailMessage = [System.Net.Mail.MailMessage]::new()
    $MailMessage.From = $EmailParameters.EmailFrom
    if ($To) { foreach ($T in $To) { $MailMessage.To.add($($T)) } } else { if ($EmailParameters.Emailto) { foreach ($To in $EmailParameters.Emailto) { $MailMessage.To.add($($To)) } } }
    if ($EmailParameters.EmailCC) { foreach ($CC in $EmailParameters.EmailCC) { $MailMessage.CC.add($($CC)) } }
    if ($EmailParameters.EmailBCC) { foreach ($BCC in $EmailParameters.EmailBCC) { $MailMessage.BCC.add($($BCC)) } }
    if ($EmailParameters.EmailReplyTo) { $MailMessage.ReplyTo = $EmailParameters.EmailReplyTo }
    $MailMessage.IsBodyHtml = $true
    if ($Subject -eq '') { $MailMessage.Subject = $EmailParameters.EmailSubject } else { $MailMessage.Subject = $Subject }
    $MailMessage.Priority = [System.Net.Mail.MailPriority]::$($EmailParameters.EmailPriority)
    if ($EmailParameters.EmailEncodingSubject) { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingSubject) } else { $MailMessage.SubjectEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) }
    if ($EmailParameters.EmailEncodingBody) { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncodingBody) } else { $MailMessage.BodyEncoding = [System.Text.Encoding]::$($EmailParameters.EmailEncoding) }
    if ($EmailParameters.EmailUseDefaultCredentials) { $SmtpClient.UseDefaultCredentials = $EmailParameters.EmailUseDefaultCredentials }
    if ($EmailParameters.EmailDeliveryNotifications) { $MailMessage.DeliveryNotificationOptions = $EmailParameters.EmailDeliveryNotifications }
    if ($PSBoundParameters.ContainsKey('InlineAttachments')) {
        $BodyPart = [Net.Mail.AlternateView]::CreateAlternateViewFromString($Body, 'text/html')
        $MailMessage.AlternateViews.Add($BodyPart)
        foreach ($Entry in $InlineAttachments.GetEnumerator()) {
            try {
                $FilePath = $Entry.Value
                Write-Verbose $FilePath
                if ($Entry.Value.StartsWith('http')) {
                    $FileName = $Entry.Value.Substring($Entry.Value.LastIndexOf("/") + 1)
                    $FilePath = Join-Path $env:temp $FileName
                    Invoke-WebRequest -Uri $Entry.Value -OutFile $FilePath
                }
                $ContentType = Get-MimeType -FileName $FilePath
                $InAttachment = [Net.Mail.LinkedResource]::new($FilePath, $ContentType)
                $InAttachment.ContentId = $Entry.Key
                $BodyPart.LinkedResources.Add($InAttachment)
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Error "Error inlining attachments: $ErrorMessage"
            }
        }
    } else { $MailMessage.Body = $Body }
    if ($PSBoundParameters.ContainsKey('Attachment')) {
        foreach ($Attach in $Attachment) {
            if (Test-Path -LiteralPath $Attach) {
                try {
                    $File = [Net.Mail.Attachment]::new($Attach)
                    $MailMessage.Attachments.Add($File)
                } catch {
                    $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                    if ($Logger) { $Logger.AddErrorRecord("Error attaching file $Attach`: $ErrorMessage") } else { Write-Error "Error attaching file $Attach`: $ErrorMessage" }
                }
            }
        }
    }
    try {
        $MailSentTo = "$($MailMessage.To) $($MailMessage.CC) $($MailMessage.BCC)".Trim()
        if ($pscmdlet.ShouldProcess("$MailSentTo", "Send-Email")) {
            $SmtpClient.Send($MailMessage)
            $MailMessage.Dispose()
            return @{Status = $True
                Error       = ""
                SentTo      = $MailSentTo
            }
        }
    } catch {
        $MailMessage.Dispose()
        return @{Status = $False
            Error       = $($_.Exception.Message)
            SentTo      = ""
        }
    }
}
function Set-EmailBody { 
    [CmdletBinding()]
    param([Object] $TableData,
        [alias('TableWelcomeMessage')][string] $TableMessageWelcome,
        [string] $TableMessageNoData = 'No changes happened during that period.')
    $Body = "<p><i><u>$TableMessageWelcome</u></i></p>"
    if ($($TableData | Measure-Object).Count -gt 0) { $Body += $TableData | ConvertTo-Html -Fragment | Out-String } else { $Body += "<p><i>$TableMessageNoData</i></p>" }
    return $body
}
function Set-EmailBodyReplacementTable { 
    [CmdletBinding()]
    [alias('Set-EmailBodyTableReplacement')]
    param ([string] $Body,
        [string] $TableName,
        [Array] $TableData)
    $TableData = $TableData | ConvertTo-Html -Fragment | Out-String
    $Body = $Body -replace "<<$TableName>>", $TableData
    return $Body
}
function Set-EmailFormatting { 
    [CmdletBinding()]
    param ($Template,
        [System.Collections.IDictionary] $FormattingParameters,
        [System.Collections.IDictionary] $ConfigurationParameters,
        [PSCustomObject] $Logger,
        [switch] $SkipNewLines,
        [string[]] $AddAfterOpening,
        [string[]] $AddBeforeClosing,
        [string] $Image)
    if ($ConfigurationParameters) { $WriteParameters = $ConfigurationParameters.DisplayConsole } else { $WriteParameters = @{ShowTime = $true; LogFile = ""; TimeFormat = "yyyy-MM-dd HH:mm:ss" } }
    if ($Image) { $Template = $Template -replace '<<Image>>', $Image }
    $Body = "<body>"
    if ($AddAfterOpening) { $Body += $AddAfterOpening }
    if (-not $SkipNewLines) {
        $Template = $Template.Split("`n")
        if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML <BR> tags...") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "<BR>", " tags." -Color White, Yellow, White, Yellow }
        foreach ($t in $Template) { $Body += "$t<br>" }
    } else { $Body += $Template }
    foreach ($style in $FormattingParameters.Styles.GetEnumerator()) {
        foreach ($value in $style.Value) {
            if ($value -eq "") { continue }
            if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML $($style.Name) tag for $value.") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "$($style.Name)", " tag for ", "$value", ' tags...' -Color White, Yellow, White, Yellow, White, Yellow }
            $Body = $Body.Replace($value, "<$($style.Name)>$value</$($style.Name)>")
        }
    }
    foreach ($color in $FormattingParameters.Colors.GetEnumerator()) {
        foreach ($value in $color.Value) {
            if ($value -eq "") { continue }
            if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding HTML $($color.Name) tag for $value.") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "$($color.Name)", " tag for ", "$value", ' tags...' -Color White, Yellow, White, Yellow, White, Yellow }
            $Body = $Body.Replace($value, "<span style=color:$($color.Name)>$value</span>")
        }
    }
    foreach ($links in $FormattingParameters.Links.GetEnumerator()) {
        foreach ($link in $links.Value) {
            if ($link.Link -like "*@*") {
                if ($Logger) { $Logger.AddInfoRecord("Preparing template - adding EMAIL Links for $($links.Key).") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " EMAIL ", "Links for", " $($links.Key)..." -Color White, Yellow, White, White, Yellow, White }
                $Body = $Body -replace "<<$($links.Key)>>", "<span style=color:$($link.Color)><a href='mailto:$($link.Link)?subject=$($Link.Subject)'>$($Link.Text)</a></span>"
            } else {
                if ($Logger) { $Logger.AddInfoRecord("[i] Preparing template - adding HTML Links for $($links.Key)") } else { Write-Color @WriteParameters -Text "[i] Preparing template ", "adding", " HTML ", "Links for", " $($links.Key)..." -Color White, Yellow, White, White, Yellow, White }
                $Body = $Body -replace "<<$($links.Key)>>", "<span style=color:$($link.Color)><a href='$($link.Link)'>$($Link.Text)</a></span>"
            }
        }
    }
    if ($AddAfterOpening) { $Body += $AddBeforeClosing }
    $Body += '</body>'
    if ($ConfigurationParameters) { if ($ConfigurationParameters.DisplayTemplateHTML -eq $true) { Get-HTML($Body) } }
    return $Body
}
function Set-EmailHead { 
    param([System.Collections.IDictionary] $FormattingOptions)
    $head = @"
    <head>
        <style>
        BODY {
            background-color: white;
            font-family: $($FormattingOptions.FontFamily);
            font-size: $($FormattingOptions.FontSize);
        }
 
        TABLE {
            border-width: 1px;
            border-style: solid;
            border-color: black;
            border-collapse: collapse;
            font-family: $($FormattingOptions.FontTableDataFamily);
            font-size: $($FormattingOptions.FontTableDataSize);
        }
 
        TH {
            border-width: 1px;
            padding: 3px;
            border-style: solid;
            border-color: black;
            background-color: #00297A;
            color: white;
            font-family: $($FormattingOptions.FontTableHeadingFamily);
            font-size: $($FormattingOptions.FontTableHeadingSize);
        }
        TR {
            font-family: $($FormattingOptions.FontTableDataFamily);
            font-size: $($FormattingOptions.FontTableDataSize);
        }
 
        UL {
            font-family: $($FormattingOptions.FontFamily);
            font-size: $($FormattingOptions.FontSize);
        }
 
        LI {
            font-family: $($FormattingOptions.FontFamily);
            font-size: $($FormattingOptions.FontSize);
        }
 
        TD {
            border-width: 1px;
            padding-right: 2px;
            padding-left: 2px;
            padding-top: 0px;
            padding-bottom: 0px;
            border-style: solid;
            border-color: black;
            background-color: white;
            font-family: $($FormattingOptions.FontTableDataFamily);
            font-size: $($FormattingOptions.FontTableDataSize);
        }
 
        H2 {
            font-family: $($FormattingOptions.FontHeadingFamily);
            font-size: $($FormattingOptions.FontHeadingSize);
        }
 
        P {
            font-family: $($FormattingOptions.FontFamily);
            font-size: $($FormattingOptions.FontSize);
        }
    </style>
    </head>
"@

    return $Head
}
function Set-EmailReportBranding { 
    param([alias('FormattingOptions')] $FormattingParameters)
    if ($FormattingParameters.CompanyBranding.Link) { $Report = "<a style=`"text-decoration:none`" href=`"$($FormattingParameters.CompanyBranding.Link)`" class=`"clink logo-container`">" } else { $Report = '' }
    if ($FormattingParameters.CompanyBranding.Inline) { $Report += "<img width=<fix> height=<fix> src=`"cid:logo`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" } else { $Report += "<img width=<fix> height=<fix> src=`"$($FormattingParameters.CompanyBranding.Logo)`" border=`"0`" class=`"company-logo`" alt=`"company-logo`"></a>" }
    if ($FormattingParameters.CompanyBranding.Width -ne "") { $Report = $Report -replace "width=<fix>", "width=$($FormattingParameters.CompanyBranding.Width)" } else { $Report = $Report -replace "width=<fix>", "" }
    if ($FormattingParameters.CompanyBranding.Height -ne "") { $Report = $Report -replace "height=<fix>", "height=$($FormattingParameters.CompanyBranding.Height)" } else { $Report = $Report -replace "height=<fix>", "" }
    return $Report
}
function Write-Color { 
    <#
    .SYNOPSIS
        Write-Color is a wrapper around Write-Host.
 
        It provides:
        - Easy manipulation of colors,
        - Logging output to file (log)
        - Nice formatting options out of the box.
 
    .DESCRIPTION
        Author: przemyslaw.klys at evotec.pl
        Project website: https://evotec.xyz/hub/scripts/write-color-ps1/
        Project support: https://github.com/EvotecIT/PSWriteColor
 
        Original idea: Josh (https://stackoverflow.com/users/81769/josh)
 
    .EXAMPLE
    Write-Color -Text "Red ", "Green ", "Yellow " -Color Red,Green,Yellow
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                    "followed by red ",
                    "and then we have Magenta... ",
                    "isn't it fun? ",
                    "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan
 
    .EXAMPLE
    Write-Color -Text "This is text in Green ",
                    "followed by red ",
                    "and then we have Magenta... ",
                    "isn't it fun? ",
                    "Here goes DarkCyan" -Color Green,Red,Magenta,White,DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1
 
    .EXAMPLE
    Write-Color "1. ", "Option 1" -Color Yellow, Green
    Write-Color "2. ", "Option 2" -Color Yellow, Green
    Write-Color "3. ", "Option 3" -Color Yellow, Green
    Write-Color "4. ", "Option 4" -Color Yellow, Green
    Write-Color "9. ", "Press 9 to exit" -Color Yellow, Gray -LinesBefore 1
 
    .EXAMPLE
    Write-Color -LinesBefore 2 -Text "This little ","message is ", "written to log ", "file as well." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt" -TimeFormat "yyyy-MM-dd HH:mm:ss"
    Write-Color -Text "This can get ","handy if ", "want to display things, and log actions to file ", "at the same time." `
                -Color Yellow, White, Green, Red, Red -LogFile "C:\testing.txt"
 
    .EXAMPLE
    # Added in 0.5
    Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow
    wc -t "my text" -c yellow -b green
    wc -text "my text" -c red
 
    .NOTES
        Additional Notes:
        - TimeFormat https://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx
    #>

    [alias('Write-Colour')]
    [CmdletBinding()]
    param ([alias ('T')] [String[]]$Text,
        [alias ('C', 'ForegroundColor', 'FGC')] [ConsoleColor[]]$Color = [ConsoleColor]::White,
        [alias ('B', 'BGC')] [ConsoleColor[]]$BackGroundColor = $null,
        [alias ('Indent')][int] $StartTab = 0,
        [int] $LinesBefore = 0,
        [int] $LinesAfter = 0,
        [int] $StartSpaces = 0,
        [alias ('L')] [string] $LogFile = '',
        [Alias('DateFormat', 'TimeFormat')][string] $DateTimeFormat = 'yyyy-MM-dd HH:mm:ss',
        [alias ('LogTimeStamp')][bool] $LogTime = $true,
        [ValidateSet('unknown', 'string', 'unicode', 'bigendianunicode', 'utf8', 'utf7', 'utf32', 'ascii', 'default', 'oem')][string]$Encoding = 'Unicode',
        [switch] $ShowTime,
        [switch] $NoNewLine)
    $DefaultColor = $Color[0]
    if ($null -ne $BackGroundColor -and $BackGroundColor.Count -ne $Color.Count) {
        Write-Error "Colors, BackGroundColors parameters count doesn't match. Terminated."
        return
    }
    if ($LinesBefore -ne 0) { for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host -Object "`n" -NoNewline } }
    if ($StartTab -ne 0) { for ($i = 0; $i -lt $StartTab; $i++) { Write-Host -Object "`t" -NoNewLine } }
    if ($StartSpaces -ne 0) { for ($i = 0; $i -lt $StartSpaces; $i++) { Write-Host -Object ' ' -NoNewLine } }
    if ($ShowTime) { Write-Host -Object "[$([datetime]::Now.ToString($DateTimeFormat))]" -NoNewline }
    if ($Text.Count -ne 0) {
        if ($Color.Count -ge $Text.Count) { if ($null -eq $BackGroundColor) { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewLine } } else { for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewLine } } } else {
            if ($null -eq $BackGroundColor) {
                for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -NoNewLine }
                for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -NoNewLine }
            } else {
                for ($i = 0; $i -lt $Color.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $Color[$i] -BackgroundColor $BackGroundColor[$i] -NoNewLine }
                for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host -Object $Text[$i] -ForegroundColor $DefaultColor -BackgroundColor $BackGroundColor[0] -NoNewLine }
            }
        }
    }
    if ($NoNewLine -eq $true) { Write-Host -NoNewline } else { Write-Host }
    if ($LinesAfter -ne 0) { for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host -Object "`n" -NoNewline } }
    if ($Text.Count -and $LogFile) {
        $TextToFile = ""
        for ($i = 0; $i -lt $Text.Length; $i++) { $TextToFile += $Text[$i] }
        try { if ($LogTime) { "[$([datetime]::Now.ToString($DateTimeFormat))]$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } else { "$TextToFile" | Out-File -FilePath $LogFile -Encoding $Encoding -Append -ErrorAction Stop } } catch { $PSCmdlet.WriteError($_) }
    }
}
function Get-MimeType { 
    [CmdletBinding()]
    param ([Parameter(Mandatory = $true)]
        [string] $FileName)
    $MimeMappings = @{'.jpeg' = 'image/jpeg'
        '.jpg'                = 'image/jpeg'
        '.png'                = 'image/png'
    }
    $Extension = [System.IO.Path]::GetExtension($FileName)
    $ContentType = $MimeMappings[ $Extension ]
    if ([string]::IsNullOrEmpty($ContentType)) { return New-Object System.Net.Mime.ContentType } else { return New-Object System.Net.Mime.ContentType($ContentType) }
}
function Request-Credentials { 
    [CmdletBinding()]
    param(
        [string] $UserName,
        [string] $Password,
        [switch] $AsSecure,
        [switch] $FromFile,
        [switch] $Output,
        [switch] $NetworkCredentials,
        [string] $Service
    )
    if ($FromFile) {
        if (($Password -ne '') -and (Test-Path $Password)) {
            # File is there and we are reading it into Password
            Write-Verbose "Request-Credentials - Reading password from file $Password"
            $Password = Get-Content -Path $Password
        } else {
            # File is not there or couldn't be read
            if ($Output) {
                return @{ Status = $false; Output = $Service; Extended = 'File with password unreadable.' }
            } else {
                Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating."
                return
            }
        }
    }
    if ($AsSecure) {
        try {
            $NewPassword = $Password | ConvertTo-SecureString -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                } else {
                    Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                    return
                }
            } else {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                } else {
                    Write-Warning -Message "Request-Credentials - $ErrorMessage"
                    return
                }
            }
        }

    } else {
        $NewPassword = $Password
    }
    if ($UserName -and $NewPassword) {
        if ($AsSecure) {
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword)
        } else {
            Try {
                $SecurePassword = $Password | ConvertTo-SecureString -asPlainText -Force -ErrorAction Stop
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                    if ($Output) {
                        return  @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                    } else {
                        Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                        return
                    }
                } else {
                    if ($Output) {
                        return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                    } else {
                        Write-Warning -Message "Request-Credentials - $ErrorMessage"
                        return
                    }
                }
            }
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)
        }
    } else {
        if ($Output) {
            return @{ Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' }
        } else {
            Write-Warning -Message 'Request-Credentials - UserName or Password are empty.'
            return
        }
    }
    if ($NetworkCredentials) {
        return $Credentials.GetNetworkCredential()
    } else {
        return $Credentials
    }
}
function Find-LimitedScope {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $ConfigurationParameters,
        [System.Collections.IDictionary] $CachedUsers
    )
    $Forest = Get-ADForest
    $UsersInGroups = if ($ConfigurationParameters.RemindersSendToManager.LimitScope) {
        foreach ($Group in $ConfigurationParameters.RemindersSendToManager.LimitScope.Groups) {
            foreach ($Domain in $Forest.Domains) {
                $Server = Get-ADDomainController -Discover -DomainName $Domain
                try {
                    $GroupMembers = Get-ADGroupMember -Identity $Group -Server $($Server.HostName) -ErrorAction Stop -Recursive
                    #$GroupMembers

                    foreach ($_ in $GroupMembers) {
                        $CachedUsers["$($_.distinguishedName)"]
                    }

                } catch {
                    $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                    Write-Color @WriteParameters '[e] Error: ', $ErrorMessage -Color White, Red
                    continue
                }
            }
        }
    }
    $UsersInGroups
}
$script:WriteParameters = @{
    ShowTime   = $true
    LogFile    = ""
    TimeFormat = "yyyy-MM-dd HH:mm:ss"
}
function Set-EmailReplacements {
    [CmdletBinding()]
    param(
        [string] $Replacement,
        [PSCustomObject] $User,
        [System.Collections.IDictionary] $EmailParameters,
        [System.Collections.IDictionary] $FormattingParameters,
        [int] $Day
    )

    $Replacement = $Replacement -replace "<<DisplayName>>", $user.DisplayName
    $Replacement = $Replacement -replace "<<DateExpiry>>", $user.DateExpiry
    $Replacement = $Replacement -replace "<<GivenName>>", $user.GivenName
    $Replacement = $Replacement -replace "<<Surname>>", $user.Surname
    $Replacement = $Replacement -replace "<<TimeToExpire>>", $Day
    $Replacement = $Replacement -replace "<<ManagerDisplayName>>", $user.Manager
    $Replacement = $Replacement -replace "<<ManagerEmail>>", $user.ManagerEmail

    if ($FormattingParameters.Conditions) {
        foreach ($Key in $FormattingParameters.Conditions.Keys) {
            $Found = $false
            $ReplaceFrom = "<<$Key>>"
            $DefaultReplaceTo = $FormattingParameters.Conditions["$Key"]['DefaultCondition']
            foreach ($Condition in $FormattingParameters.Conditions["$Key"].Keys | Where-Object { $_ -ne 'DefaultCondition' }) {
                if ($FormattingParameters.Conditions["$Key"]["$Condition"]) {
                    foreach ($SubKey in $FormattingParameters.Conditions["$Key"]["$Condition"].Keys) {
                        if ($SubKey -eq $User.$Condition) {
                            $Replacement = $Replacement -replace "$ReplaceFrom", $FormattingParameters.Conditions["$Key"]["$Condition"][$SubKey]
                            $Found = $true
                        }
                    }
                }
            }
            if (-not $Found) {
                $Replacement = $Replacement -replace "$ReplaceFrom", $DefaultReplaceTo
            }
        }
    }
    return $Replacement
}
function Set-EmailReportDetails {
    [CmdletBinding()]
    param(
        [System.Collections.IDictionary] $FormattingOptions,
        [System.Collections.IDictionary] $ReportOptions,
        [timespan] $TimeToGenerate,
        [int] $CountUsersImminent,
        [int] $CountUsersCountdownStarted,
        [int] $CountUsersAlreadyExpired,
        [int] $CountUsersNotified
    )
    $DateReport = Get-Date
    # HTML Report settings
    $Report = @(

        @"
        <p>
            <strong>Report Time:</strong> $DateReport
            <br>
            <strong>Time to generate:</strong> $($TimeToGenerate.Hours) hours, $($TimeToGenerate.Minutes) minutes, $($TimeToGenerate.Seconds) seconds, $($TimeToGenerate.Milliseconds) milliseconds
            <br>
            <strong>Account Executing Report :</strong> $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())
            <br>
            <strong>Users notified: </strong> $CountUsersNotified
            <br>
            <strong>Users expiring countdown started: </strong> $CountUsersCountdownStarted
            <br>
            <strong>Users expiring soon: </strong> $CountUsersImminent
            <br>
            <strong>Users already expired count: </strong> $CountUsersAlreadyExpired
            <br>
        </p>
"@

        foreach ($ip in $ReportOptions.MonitoredIps.Values) {
            "<li>ip:</strong> $ip</li>"
        }
        '</ul>'
        '</p>'
    )
    return $Report
}
function Test-Prerequisits {
    [CmdletBinding()]
    param()
    try {
        $null = Get-ADForest
    } catch {
        if ($_.Exception -match "Unable to find a default server with Active Directory Web Services running.") {
            Write-Color @script:WriteParameters "[-] ", "Active Directory", " not found. Please run this script with access to ", "Domain Controllers." -Color White, Red, White, Red
        }
        Write-Color @script:WriteParameters "[-] ", "Error: $($_.Exception.Message)" -Color White, Red
        Exit
    }
}
function Find-PasswordExpiryCheck {
    [CmdletBinding()]
    param(
        [string] $AdditionalProperties,
        [Array] $ConditionProperties,
        [System.Collections.IDictionary] $WriteParameters,
        [System.Collections.IDictionary] $CachedUsers,
        [System.Collections.IDictionary] $CachedUsersPrepared,
        [System.Collections.IDictionary] $CachedManagers
    )
    if ($null -eq $WriteParameters) {
        $WriteParameters = @{
            ShowTime   = $true
            LogFile    = ""
            TimeFormat = "yyyy-MM-dd HH:mm:ss"
        }
    }


    $Properties = @(
        'Manager', 'DisplayName', 'GivenName', 'Surname', 'SamAccountName', 'EmailAddress', 'msDS-UserPasswordExpiryTimeComputed', 'PasswordExpired', 'PasswordLastSet', 'PasswordNotRequired', 'Enabled', 'PasswordNeverExpires', 'Mail'
        if ($AdditionalProperties) {
            $AdditionalProperties
        }
        if ($ConditionProperties) {
            $ConditionProperties
        }
    )
    # We're caching all users to make sure it's speedy gonzales when querying for Managers
    if (-not $CachedUsers) {
        $CachedUsers = [ordered] @{ }
    }
    if (-not $CachedUsersPrepared) {
        $CachedUsersPrepared = [ordered] @{ }
    }
    if (-not $CachedManagers) {
        $CachedManagers = [ordered] @{ }
    }
    Write-Color @WriteParameters -Text "[i] Discovering forest information" -Color White, Yellow, White, Yellow, White, Yellow, White
    $Forest = Get-ADForest

    $Users = @(
        foreach ($Domain in $Forest.Domains) {
            try {
                Write-Color @WriteParameters -Text "[i] Discovering DC for domain ", "$($Domain)", " in forest ", $Forest.Name -Color White, Yellow, White, Yellow, White, Yellow, White
                $Server = Get-ADDomainController -Discover -DomainName $Domain -ErrorAction Stop
                #$Users = Get-ADUser -Server $Server -Filter { Enabled -eq $True -and PasswordNeverExpires -eq $False -and PasswordLastSet -gt 0 -and PasswordNotRequired -ne $True } -Properties $Properties -ErrorAction Stop
                Write-Color @WriteParameters -Text "[i] Getting users from ", "$($Domain)", " using ", $Server.Hostname -Color White, Yellow, White, Yellow, White, Yellow, White
                # We query all users instead of using filter. Since we need manager field and manager data this way it should be faster (query once - get it all)
                $DomainUsers = Get-ADUser -Server $($Server.HostName) -Filter '*' -Properties $Properties -ErrorAction Stop
                foreach ($_ in $DomainUsers) {
                    Add-Member -InputObject $_ -Value $Domain -Name 'Domain' -Force -Type NoteProperty
                    $CachedUsers["$($_.DistinguishedName)"] = $_
                    # We reuse filtering
                    if ($_.Enabled -eq $true -and $_.PasswordNeverExpires -eq $false -and $_.PasswordLastSet -gt 0 -and $_.PasswordNotRequired -ne $true) {
                        $_
                    }
                }
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                Write-Color @WriteParameters '[e] Error: ', $ErrorMessage -Color White, Red
            }
        }
    )
    Write-Color @WriteParameters -Text "[i] Preparing all users for password expirations in forest ", $Forest.Name -Color White, Yellow, White, Yellow, White, Yellow, White
    $ProcessedUsers = foreach ($_ in $Users) {
        $UserManager = $CachedUsers["$($_.Manager)"]
        if ($AdditionalProperties) {
            # fix this for a user
            $EmailTemp = $_.$AdditionalProperties
            if ($EmailTemp -like '*@*') {
                $EmailAddress = $EmailTemp
            } else {
                $EmailAddress = $_.EmailAddress
            }
            # Fix this for manager as well
            if ($UserManager) {
                if ($UserManager.$AdditionalProperties -like '*@*') {
                    $UserManager.Mail = $UserManager.$AdditionalProperties
                }
            }
        } else {
            $EmailAddress = $_.EmailAddress
        }

        if ($_."msDS-UserPasswordExpiryTimeComputed" -ne 9223372036854775807) {
            # This is standard situation where users password is expiring as needed
            try {
                $DateExpiry = ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed"))
            } catch {
                $DateExpiry = $_."msDS-UserPasswordExpiryTimeComputed"
            }
            try {
                $DaysToExpire = (New-TimeSpan -Start (Get-Date) -End ([datetime]::FromFileTime($_."msDS-UserPasswordExpiryTimeComputed"))).Days
            } catch {
                $DaysToExpire = $null
            }
            $PasswordNeverExpires = $_.PasswordNeverExpires
        } else {
            # This is non-standard situation. This basically means most likely Fine Grained Group Policy is in action where it makes PasswordNeverExpires $true
            # Since FGP policies are a bit special they do not tick the PasswordNeverExpires box, but at the same time value for "msDS-UserPasswordExpiryTimeComputed" is set to 9223372036854775807
            $DateExpiry = $null
            $DaysToExpire = $null
            $PasswordNeverExpires = $true
        }

        $MyUser = [ordered] @{
            UserPrincipalName    = $_.UserPrincipalName
            Domain               = $_.Domain
            SamAccountName       = $_.SamAccountName
            DisplayName          = $_.DisplayName
            GivenName            = $_.GivenName
            Surname              = $_.Surname
            EmailAddress         = $EmailAddress
            PasswordExpired      = $_.PasswordExpired
            PasswordLastSet      = $_.PasswordLastSet
            PasswordNotRequired  = $_.PasswordNotRequired
            PasswordNeverExpires = $PasswordNeverExpires
            Manager              = $UserManager.Name
            ManagerEmail         = $UserManager.Mail
            ManagerDN            = $_.Manager
            DateExpiry           = $DateExpiry
            DaysToExpire         = $DaysToExpire
        }
        foreach ($Property in $ConditionProperties) {
            $MyUser["$Property"] = $_.$Property
        }
        [PSCustomObject] $MyUser
        $CachedUsersPrepared["$($_.DistinguishedName)"] = $MyUser
    }
    foreach ($_ in $CachedUsersPrepared.Keys) {
        $ManagerDN = $CachedUsersPrepared[$_]['ManagerDN']
        if ($ManagerDN) {
            $Manager = $CachedUsers[$ManagerDN]

            $MyUser = [ordered] @{
                UserPrincipalName = $Manager.UserPrincipalName
                Domain            = $Manager.Domain
                SamAccountName    = $Manager.SamAccountName
                DisplayName       = $Manager.DisplayName
                GivenName         = $Manager.GivenName
                Surname           = $Manager.Surname
                DistinguishedName = $ManagerDN
            }
            foreach ($Property in $ConditionProperties) {
                $MyUser["$Property"] = $_.$Property
            }
            $CachedManagers[$ManagerDN] = $MyUser
        }
    }


    $ProcessedUsers
}

#$Test = Find-PasswordExpiryCheck -AdditionalProperties 'extensionAttribute13'
#$Test | Format-Table -AutoSize *

Function Start-PasswordExpiryCheck {
    [CmdletBinding()]
    param (
        [System.Collections.IDictionary] $EmailParameters,
        [System.Collections.IDictionary] $FormattingParameters,
        [System.Collections.IDictionary] $ConfigurationParameters
    )
    $time = [System.Diagnostics.Stopwatch]::StartNew() # Timer Start
    Test-Prerequisits

    $WriteParameters = $ConfigurationParameters.DisplayConsole
    $FieldName = $ConfigurationParameters.RemindersSendToUsers.UseAdditionalField

    $Today = Get-Date
    $CachedUsers = [ordered] @{ }
    $CachedUsersPrepared = [ordered] @{ }
    $CachedManagers = [ordered] @{ }

    [Array] $ConditionProperties = if ($FormattingParameters.Conditions) {
        foreach ($Key in $FormattingParameters.Conditions.Keys) {
            foreach ($Condition in $FormattingParameters.Conditions["$Key"].Keys | Where-Object { $_ -ne 'DefaultCondition' }) {
                $Condition
            }
        }
    }
    $Users = Find-PasswordExpiryCheck -AdditionalProperties $FieldName -ConditionProperties $ConditionProperties -WriteParameters $WriteParameters -CachedUsers $CachedUsers -CachedUsersPrepared $CachedUsersPrepared -CachedManagers $CachedManagers | Sort-Object DateExpiry
    #$UsersWithEmail = @(
    # $Users | Where-Object { $_.EmailAddress -like '*@*' }
    #)
    $UsersExpired = $Users | Where-Object { $null -ne $_.DateExpiry -and $_.DateExpiry -lt $Today }

    $EmailBody = Set-EmailHead -FormattingOptions $FormattingParameters

    $Image = Set-EmailReportBranding -FormattingOptions $FormattingParameters

    $EmailBody += Set-EmailFormatting -Template $FormattingParameters.Template -FormattingParameters $FormattingParameters `
        -ConfigurationParameters $ConfigurationParameters -Image $Image

    $UsersNotified = @(
        [bool] $TestingLimitReached = $false
        #region Send Emails to Users
        if ($ConfigurationParameters.RemindersSendToUsers.Enable -eq $true) {
            Write-Color @WriteParameters '[i] Starting processing ', 'Users', ' section' -Color White, Yellow, White

            if ($ConfigurationParameters.RemindersSendToUsers.Reminders -is [System.Collections.IDictionary]) {
                [Array] $DaysToExpire = ($ConfigurationParameters.RemindersSendToUsers.Reminders).Values | Sort-Object -Unique
            } else {
                [Array] $DaysToExpire = $ConfigurationParameters.RemindersSendToUsers.Reminders | Sort-Object -Unique
            }
            #foreach ($Day in $ConfigurationParameters.RemindersSendToUsers.Reminders.GetEnumerator()) {

            #$Date = (Get-Date).AddDays($Day.Value).Date
            $Count = 0
            foreach ($u in $Users) {
                if ($TestingLimitReached -eq $true) {
                    break
                }
                if ($u.DaysToExpire -in $DaysToExpire) {
                    if ($u.EmailAddress -like '*@*') {
                        $Count++
                        Write-Color @WriteParameters -Text "[i] User ", "$($u.DisplayName)", " expires in ", "$($u.DaysToExpire)", " days (", "$($u.DateExpiry)", ")."  -Color White, Yellow, White, Red, White, Red, White
                        $TemporaryBody = Set-EmailReplacements -Replacement $EmailBody -User $u -FormattingParameters $FormattingParameters -EmailParameters $EmailParameters -Day $u.DaysToExpire
                        $EmailSubject = Set-EmailReplacements -Replacement $EmailParameters.EmailSubject -User $u -FormattingParameters $FormattingParameters -EmailParameters $EmailParameters -Day $u.DaysToExpire
                        #$u.DaysToExpire = $Day.Value

                        if ($ConfigurationParameters.RemindersSendToUsers.RemindersDisplayOnly -eq $true) {
                            Write-Color @WriteParameters -Text "[i] Pretending to send email to ", "$($u.EmailAddress)", " ...", "Success"  -Color White, Green, White, Green
                            $EmailSent = [ordered] @{ }
                            $EmailSent.Status = $false
                            $EmailSent.SentTo = 'N/A'
                        } else {
                            $EmailSplat = @{
                                EmailParameters = $EmailParameters
                                Body            = $TemporaryBody
                                Subject         = $EmailSubject
                            }
                            if ($FormattingParameters.CompanyBranding.Inline) {
                                $EmailSplat.InlineAttachments = @{ logo = $FormattingParameters.CompanyBranding.Logo }
                            }
                            if ($ConfigurationParameters.RemindersSendToUsers.SendToDefaultEmail -eq $false) {
                                Write-Color @WriteParameters -Text "[i] Sending email to ", "$($u.EmailAddress)", " ..."  -Color White, Green -NoNewLine
                                $EmailSplat.To = $u.EmailAddress
                            } else {
                                Write-Color @WriteParameters -Text "[i] Sending email to users is disabled. Sending email to default value: ", "$($EmailParameters.EmailTo) ", "..." -Color White, Yellow, White -NoNewLine
                            }
                            $EmailSent = Send-Email @EmailSplat
                            if ($EmailSent.Status -eq $true) {
                                Write-Color -Text "Done" -Color "Green"
                            } else {
                                Write-Color -Text "Failed!" -Color "Red"
                            }
                        }
                        Add-Member -InputObject $u -NotePropertyName "EmailSent" -NotePropertyValue $EmailSent.Status
                        Add-Member -InputObject $u -NotePropertyName "EmailSentTo" -NotePropertyValue $EmailSent.SentTo
                    } else {
                        Add-Member -InputObject $u -NotePropertyName "EmailSent" -NotePropertyValue $false
                        Add-Member -InputObject $u -NotePropertyName "EmailSentTo" -NotePropertyValue 'Not available'
                        Write-Color @WriteParameters -Text "[i] User ", "$($u.DisplayName)", " expires in ", "$($u.DaysToExpire)", " days (", "$($u.DateExpiry)", "). However user has no email address and will be skipped."  -Color White, Yellow, White, Red, White, Red, White
                    }
                    $u
                    if ($ConfigurationParameters.RemindersSendToUsers.SendCountMaximum -eq $Count) {
                        Write-Color @WriteParameters -Text "[i] Sending email to maximum number of users ", "$($ConfigurationParameters.RemindersSendToUsers.SendCountMaximum) ", "has been reached. Skipping..." -Color White, Yellow, White
                        $TestingLimitReached = $true
                        break
                    }
                }
            }
            #}
            Write-Color @WriteParameters '[i] Ending processing ', 'Users', ' section' -Color White, Yellow, White
        } else {
            Write-Color @WriteParameters '[i] Skipping processing ', 'Users', ' section' -Color White, Yellow, White
        }
    )
    #endregion

    #region Send Emails to Managers
    $ManagersReceived = if ($ConfigurationParameters.RemindersSendToManager.Enable -eq $true) {
        Write-Color @WriteParameters '[i] Starting processing ', 'Managers', ' section' -Color White, Yellow, White
        # preparing email
        $EmailSubject = $ConfigurationParameters.RemindersSendToManager.ManagersEmailSubject
        $EmailBody = Set-EmailHead -FormattingOptions $FormattingParameters
        $EmailReportBranding = Set-EmailReportBranding -FormattingOptions $FormattingParameters
        $EmailBody += Set-EmailFormatting -Template $FormattingParameters.TemplateForManagers `
            -FormattingParameters $FormattingParameters `
            -ConfigurationParameters $ConfigurationParameters `
            -AddAfter $EmailReportBranding

        # preparing manager lists
        if ($ConfigurationParameters.RemindersSendToManager.LimitScope.Groups) {
            # send emails to managers only if those people are in limited scope groups
            [Array] $LimitedScopeMembers = Find-LimitedScope -ConfigurationParameters $ConfigurationParameters -CachedUsers $CachedUsersPrepared
            [Array] $UsersWithManagers = foreach ($_ in $UsersNotified) {
                if ($LimitedScopeMembers.UserPrincipalName -contains $_.UserPrincipalName) {
                    if ($null -ne $_.ManagerEmail) {
                        $_
                    }
                }
            }
        } else {
            [Array] $UsersWithManagers = foreach ($_ in $UsersNotified) {
                if ($null -ne $_.ManagerEmail) {
                    $_
                }
            }
            # $UsersWithManagers = $UsersNotified | Where-Object { $null -ne $_.ManagerEmail }
        }
        # Find managers with emails. Make sure only unique is added to the list
        $ManagersEmails = [System.Collections.Generic.List[string]]::new()
        foreach ($u in $UsersWithManagers) {
            if ($ManagersEmails -notcontains $u.ManagerEmail) {
                $ManagersEmails.Add($u.ManagerEmail)
            }
        }
        Write-Color @WriteParameters '[i] Preparing package for managers with emails ', "$($UsersWithManagers.Count) ", 'users to process with', ' manager filled in', ' where unique managers ', "$($ManagersEmails.Count)" -Color  White, Yellow, White, Yellow, White, Yellow
        # processing one manager at time
        $Count = 0
        foreach ($m in $ManagersEmails) {
            $Count++

            # preparing users belonging to manager
            $ColumnNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'

            [Array] $UsersNotifiedManagers = $UsersWithManagers | Where-Object { $_.ManagerEmail -eq $m }
            [string] $ManagerDN = $UsersNotifiedManagers[0].ManagerDN
            $ManagerFull = $CachedManagers[$ManagerDN]
            if ($ConfigurationParameters.RemindersSendToManager.Reports.IncludePasswordNotificationsSent.IncludeNames -ne '') {
                $UsersNotifiedManagers = $UsersNotifiedManagers | Select-Object $ConfigurationParameters.RemindersSendToManager.Reports.IncludePasswordNotificationsSent.IncludeNames
            } else {
                $UsersNotifiedManagers = $UsersNotifiedManagers | Select-Object 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'DaysToExpire', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet', 'EmailSent', 'EmailSentTo'
            }
            if ($ConfigurationParameters.RemindersSendToManager.Reports.IncludePasswordNotificationsSent.Enabled -eq $true) {
                foreach ($u in $UsersNotifiedManagers) {
                    Write-Color @WriteParameters -Text '[-] User ', "$($u.DisplayName) ", " Managers Email (", "$($m)", ')'  -Color White, Yellow, White, Yellow, White
                }
            }
            #$ManagerFull['EmailsSent'] = $UserPrincipalNames -join '; '

            if ($ConfigurationParameters.RemindersSendToManager.RemindersDisplayOnly -eq $true) {
                Write-Color @WriteParameters -Text "[i] Pretending to send email to manager email ", "$($m)", " ...", "Success"  -Color White, Green, White, Green
                $EmailSent = @{ }
                $EmailSent.Status = $false
                $EmailSent.SentTo = 'N/A'
            } else {
                $TemporaryBody = $EmailBody
                $TemporaryBody = Set-EmailBodyReplacementTable -Body $TemporaryBody -TableName 'ManagerUsersTable' -TableData $UsersNotifiedManagers
                $TemporaryBody = Set-EmailReplacements -Replacement $TemporaryBody -User $u -FormattingParameters $FormattingParameters -EmailParameters $EmailParameters #-Day ''

                if ($ConfigurationParameters.Debug.DisplayTemplateHTML -eq $true) {
                    Get-HTML -text $TemporaryBody
                }
                $EmailSplat = @{
                    EmailParameters = $EmailParameters
                    Body            = $TemporaryBody
                    Subject         = $EmailSubject
                }
                if ($FormattingParameters.CompanyBranding.Inline) {
                    $EmailSplat.InlineAttachments = @{ logo = $FormattingParameters.CompanyBranding.Logo }
                }
                if ($ConfigurationParameters.RemindersSendToManager.SendToDefaultEmail -eq $false) {
                    Write-Color @WriteParameters -Text "[i] Sending email to managers email ", "$($m)", " ..."  -Color White, Green -NoNewLine
                    $EmailSplat.To = $m
                } else {
                    Write-Color @WriteParameters -Text "[i] Sending email to managers is disabled. Sending email to default value: ", "$($EmailParameters.EmailTo) ", "..." -Color White, Yellow, White -NoNewLine
                }
                $EmailSent = Send-Email @EmailSplat
                if ($EmailSent.Status -eq $true) {
                    Write-Color -Text "Done" -Color "Green"
                } else {
                    Write-Color -Text "Failed!" -Color "Red"
                }
            }

            $ManagerFull['EmailSent'] = $EmailSent.Status
            $ManagerFull['EmailSentTo'] = $EmailSent.SentTo

            if ($ConfigurationParameters.RemindersSendToManager.Reports.IncludeManagersPasswordNotificationsSent.IncludeNames.Count -gt 0) {
                ([PSCustomObject] $ManagerFull) | Select-Object -Property $ConfigurationParameters.RemindersSendToManager.Reports.IncludeManagersPasswordNotificationsSent.IncludeNames
            } else {
                ([PSCustomObject] $ManagerFull) | Select-Object -Property 'UserPrincipalName', 'Domain', 'DisplayName', 'SamAccountName', 'EmailSent', 'EmailSentTo'
            }


            if ($ConfigurationParameters.RemindersSendToManager.SendCountMaximum -eq $Count) {
                Write-Color @WriteParameters -Text "[i] Sending email to maximum number of managers ", "$($ConfigurationParameters.RemindersSendToManager.SendCountMaximum) ", " has been reached. Skipping..." -Color White, Yellow, White -NoNewLine
                break
            }
        }
        Write-Color @WriteParameters '[i] Ending processing ', 'Managers', ' section' -Color White, Yellow, White
    } else {
        Write-Color @WriteParameters '[i] Skipping processing ', 'Managers', ' section' -Color White, Yellow, White
    }
    #endregion Send Emails to Managers


    if ($ConfigurationParameters.DisableExpiredUsers.Enable -eq $true) {
        Write-Color @WriteParameters '[i] Starting processing ', 'Disable Expired Users', ' section' -Color White, Yellow, White
        foreach ($U in $UsersExpired) {
            if ($ConfigurationParameters.DisableExpiredUsers.DisplayOnly) {
                Write-Color @WriteParameters -Text "[i] User ", "$($u.DisplayName)", " expired on (", "$($u.DateExpiry)", "). Pretending to disable acoount..."  -Color White, Yellow, White, Red, White, Red, White
            } else {
                Write-Color @WriteParameters -Text "[i] User ", "$($u.DisplayName)", " expired on (", "$($u.DateExpiry)", "). Disabling..."  -Color White, Yellow, White, Red, White, Red, White
                Disable-ADAccount -Identity $u.SamAccountName -Confirm:$false
            }
        }
        Write-Color @WriteParameters '[i] Ending processing ', 'Disable Expired Users', ' section' -Color White, Yellow, White
    }

    #region Send Emails to Admins
    if ($ConfigurationParameters.RemindersSendToAdmins.Enable -eq $true) {
        Write-Color @WriteParameters '[i] Starting processing ', 'Administrators', ' section' -Color White, Yellow, White
        if ($ConfigurationParameters.RemindersSendToUsers.Reminders -is [System.Collections.IDictionary]) {
            $DayHighest = Get-HashMaxValue $ConfigurationParameters.RemindersSendToUsers.Reminders
            $DayLowest = Get-HashMaxValue $ConfigurationParameters.RemindersSendToUsers.Reminders -Lowest
        } else {
            [Array] $OrderedDays = $ConfigurationParameters.RemindersSendToUsers.Reminders | Sort-Object -Unique
            $DayHighest = $OrderedDays[-1]
            $DayLowest = $OrderedDays[0]
        }
        $DateCountdownStart = (Get-Date).AddDays($DayHighest).Date
        $DateIminnent = (Get-Date).AddDays($DayLowest).Date
        #Write-Color 'Day Highest ', $DayHighest, ' Day Lowest ', $DayLowest, ' Day Countdown Start ', $DateCountdownStart, ' Day Iminnet ', $DateIminnent -Color White, Yellow, White, Yellow, White, Yellow, White, Yellow

        $ColumnNames = 'UserPrincipalName', 'DisplayName', 'DateExpiry', 'PasswordExpired', 'SamAccountName', 'Manager', 'ManagerEmail', 'PasswordLastSet'

        if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludePasswordNotificationsSent.IncludeNames -gt 0) {
            $UsersNotified = $UsersNotified | Select-Object $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludePasswordNotificationsSent.IncludeNames
        } else {
            $UsersNotified = $UsersNotified | Select-Object $ColumnNames, 'EmailSent', 'EmailSentTo'
        }
        if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringImminent.IncludeNames.Count -gt 0) {
            $ExpiringIminent = $Users | Where-Object { $null -ne $_.DateExpiry -and $_.DateExpiry -lt $DateIminnent -and $_.PasswordExpired -eq $false } | Select-Object $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringImminent.IncludeNames
        } else {
            $ExpiringIminent = $Users | Where-Object { $null -ne $_.DateExpiry -and $_.DateExpiry -lt $DateIminnent -and $_.PasswordExpired -eq $false } | Select-Object $ColumnNames
        }

        if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringCountdownStarted.IncludeNames -gt 0) {
            $ExpiringCountdownStarted = $Users | Where-Object { $null -ne $_.DateExpiry -and $_.DateExpiry -lt $DateCountdownStart -and $_.PasswordExpired -eq $false } | Select-Object $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringCountdownStarted.IncludeNames
        } else {
            $ExpiringCountdownStarted = $Users | Where-Object { $null -ne $_.DateExpiry -and $_.DateExpiry -lt $DateCountdownStart -and $_.PasswordExpired -eq $false } | Select-Object $ColumnNames
        }

        if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpired.IncludeNames -gt 0) {
            $UsersExpired = $UsersExpired | Select-Object $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpired.IncludeNames
        } else {
            $UsersExpired = $UsersExpired | Select-Object $ColumnNames
        }

        $EmailBody = Set-EmailHead -FormattingOptions $FormattingParameters
        $EmailBody += "<body>"
        $EmailBody += Set-EmailReportBranding -FormattingOptions $FormattingParameters
        $EmailBody += Set-EmailReportDetails -FormattingOptions $FormattingParameters `
            -ReportOptions $ReportOptions `
            -TimeToGenerate $Time.Elapsed `
            -CountUsersCountdownStarted $($ExpiringCountdownStarted.Count) `
            -CountUsersImminent $($ExpiringIminent.Count) `
            -CountUsersAlreadyExpired $($UsersExpired.Count) -CountUsersNotified $($UsersNotified.Count)
        $time.Stop()

        if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeSummary.Enabled -eq $true) {
            $SummaryOfUsers = $Users | Group-Object DaysToExpire `
            | Select-Object @{Name = 'Days to Expire'; Expression = { [int] $($_.Name) } }, @{Name = 'Users with Days to Expire'; Expression = { [int] $($_.Count) } }
    $SummaryOfUsers = $SummaryOfUsers | Sort-Object -Property 'Days to Expire'

    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Summary of Expiring Users' -Color White, Yellow
    $EmailBody += Set-EmailBody -TableData $SummaryOfUsers `
        -TableMessageWelcome "Summary of days to expire and it's count" `
        -TableMessageNoData 'There were no users that have days of expiring.'
}
if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludePasswordNotificationsSent.Enabled -eq $true) {
    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Password Notifcations Sent' -Color White, Yellow
    $EmailBody += Set-EmailBody -TableData $UsersNotified `
        -TableMessageWelcome "Following users had their password notifications sent" `
        -TableMessageNoData 'No users required nofifications.'
}
if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeManagersPasswordNotificationsSent.Enabled -eq $true) {
    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Password Notifcations Sent to Managers' -Color White, Yellow
    $EmailBody += Set-EmailBody -TableData $ManagersReceived `
        -TableMessageWelcome "Following managers had their password bundle notifications sent" `
        -TableMessageNoData 'No managers required nofifications.'
}
if ( $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringImminent.Enabled -eq $true) {
    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Users expiring imminent' -Color White, Yellow
    $EmailBody += Set-EmailBody -TableData $ExpiringIminent `
        -TableMessageWelcome "Following users expiring imminent (Less than $DayLowest day(s)" `
        -TableMessageNoData 'No users expiring.'
}
if (  $ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpiringCountdownStarted.Enabled -eq $true) {
    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Expiring Couintdown Started' -Color White, Yellow
    $EmailBody += Set-EmailBody -TableData $ExpiringCountdownStarted `
        -TableMessageWelcome "Following users expiring countdown started (Less than $DayHighest day(s))" `
        -TableMessageNoData 'There were no users that had their coundown started.'
}
if ($ConfigurationParameters.RemindersSendToAdmins.Reports.IncludeExpired.Enabled -eq $true) {
    Write-Color @WriteParameters -Text '[i] Preparing data for report ', 'Users are already expired' -Color White, Yellow
    if ($ConfigurationParameters.DisableExpiredUsers.Enable -eq $true -and -not $ConfigurationParameters.DisableExpiredUsers.DisplayOnly -eq $true) {
        $EmailBody += Set-EmailBody -TableData $UsersExpired -TableMessageWelcome "Following users are already expired (and were disabled...)" -TableMessageNoData "No users that are expired."
    } else {
        $EmailBody += Set-EmailBody -TableData $UsersExpired -TableMessageWelcome "Following users are already expired (and still enabled...)" -TableMessageNoData "No users that are expired and enabled."
    }
}
$EmailBody += "</body>"
if ($ConfigurationParameters.Debug.DisplayTemplateHTML -eq $true) {
    Get-HTML -text $EmailBody
}

if ($ConfigurationParameters.RemindersSendToAdmins.RemindersDisplayOnly -eq $true) {
    Write-Color @WriteParameters -Text "[i] Pretending to send email to admins email ", "$($ConfigurationParameters.RemindersSendToAdmins.AdminsEmail) ", "...", 'Success' -Color White, Yellow, White, Green
} else {
    Write-Color @WriteParameters -Text "[i] Sending email to administrators on email address ", "$($ConfigurationParameters.RemindersSendToAdmins.AdminsEmail) ", "..." -Color White, Yellow, White -NoNewLine
    $EmailSplat = @{
        EmailParameters = $EmailParameters
        Body            = $EmailBody
        Subject         = $ConfigurationParameters.RemindersSendToAdmins.AdminsEmailSubject
        To              = $ConfigurationParameters.RemindersSendToAdmins.AdminsEmail
    }
    if ($FormattingParameters.CompanyBranding.Inline) {
        $EmailSplat.InlineAttachments = @{ logo = $FormattingParameters.CompanyBranding.Logo }
    }
    $EmailSent = Send-Email @EmailSplat
    if ($EmailSent.Status -eq $true) {
        Write-Color -Text "Done" -Color "Green"
    } else {
        Write-Color -Text "Failed! Error: $($EmailSent.Error)" -Color "Red"
    }
}
Write-Color @WriteParameters '[i] Ending processing ', 'Administrators', ' section' -Color White, Yellow, White

} else {
    Write-Color @WriteParameters '[i] Skipping processing ', 'Administrators', ' section' -Color White, Yellow, White

}
#endregion Send Emails to Admins
}




Export-ModuleMember -Function @('Find-PasswordExpiryCheck', 'Start-PasswordExpiryCheck') -Alias @()