functions/Send-MForgeMassMail.ps1

function Send-MForgeMassMail {
    <#
    .SYNOPSIS
    Sends mass emails based on an Excel file and a mail template.
 
    .DESCRIPTION
    Send-MForgeMassMail reads the specified Excel file and sends one email for each row.
    Recipients and subject can be provided either from a specified column in the Excel file or as a fixed parameter.
    Data rows can be filtered using a filter scriptblock, and the number of emails sent can be limited (e.g., for testing).
    All columns of the Excel row are available as placeholders in the template and can be used with the Unicode character þ (ALT+0254), e.g. þNameþ.
    The template can be specified by name (registered beforehand) or as a file. Additional settings like CC/BCC, subject, etc. are optional.
 
    .PARAMETER Credential
    Optional credential for SMTP authentication. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER SMTPServer
    SMTP server address. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER Port
    SMTP port. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER From
    Sender address. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER RecipientList
    List of recipient addresses. Default values can be set with Initialize-MForgeMailDefault. Overrides MailToColumn.
 
    .PARAMETER CCList
    List of CC addresses. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER BCCList
    List of BCC addresses. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER UseSecureConnectionIfAvailable
    Uses a secure connection if available. Default values can be set with Initialize-MForgeMailDefault.
 
    .PARAMETER Subject
    Subject of the email. Can be provided from a column in the Excel file (SubjectColumn) or as a fixed value.
 
    .PARAMETER TemplateName
    Name of the template to use. Must be registered beforehand with Register-MForgeTemplate.
 
    .PARAMETER TemplateFile
    Path to the template file (.html or .md).
 
    .PARAMETER DataFile
    Excel file containing recipient data.
 
    .PARAMETER Filter
    Scriptblock for filtering data rows. Default: all rows.
 
    .PARAMETER WorksheetName
    Name of the Excel worksheet.
 
    .PARAMETER MailToColumn
    Column name for the recipient address. Used if RecipientList is not set.
 
    .PARAMETER SubjectColumn
    Column name for the subject. Used if Subject is not set.
 
    .PARAMETER Limit
    Maximum number of emails to send (e.g., for testing).
 
    .PARAMETER MailToOverride
    Overrides the recipient address for all emails.
 
    .EXAMPLE
    Send-MForgeMassMail -TemplateName "Newsletter" -DataFile "data.xlsx" -WorksheetName "Recipients" -MailToColumn "Email"
 
    Sends emails based on the "Newsletter" template to all recipients in the "Email" column.
 
    .EXAMPLE
    Send-MForgeMassMail -TemplateFile "template.html" -DataFile "data.xlsx" -WorksheetName "Sheet1" -MailToColumn "Email" -SubjectColumn "Subject" -Limit 10 -Filter { $_.Status -eq 'Active' }
 
    Sends up to 10 emails to all active recipients, with subject and recipient taken from the respective columns in the Excel file. All columns are available as placeholders in the template.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param (
        # Optional parameters from Initialize-MForgeMail
        [pscredential]$Credential,
        [string]$SMTPServer,
        [int]$Port,
        $From, # MailboxAddress
        $RecipientList, # InternetAddressList
        $CCList, # InternetAddressList
        $BCCList, # InternetAddressList
        [boolean]$UseSecureConnectionIfAvailable,
        [string]$Subject,

        # Mandatory parameters
        # ParameterSet ByName
        [Parameter(Mandatory = $true, ParameterSetName = 'ByName')]
        [string]$TemplateName,

        # ParameterSet ByFile
        [Parameter(Mandatory = $true, ParameterSetName = 'ByFile')]
        [string]$TemplateFile,

        [Parameter(Mandatory = $true)]
        [PsfFile]$DataFile,
        [scriptblock]$Filter = { $true },
        [Parameter(Mandatory = $true)]
        [string]$WorksheetName,
        [string]$MailToColumn,
        [string]$SubjectColumn,
        [int]$Limit = 0,
        [string]$MailToOverride
    )
    $singleMailParams = $PSBoundParameters | ConvertTo-PSFHashtable -ReferenceCommand "Send-MForgeMail" -Exclude TemplateFile
    Write-PSFMessage "Single Mail Params: $($singleMailParams|ConvertTo-Json -Compress)"
    if ($PSCmdlet.ParameterSetName -eq 'ByFile') {
        $templateName = Register-MForgeTemplate -TemplateFile $TemplateFile -Temporary
        $singleMailParams.TemplateName = $templateName
    }
    $rawData = Import-Excel -Path $DataFile -WorksheetName $WorksheetName
    $SelectParam = @{}
    if ($Limit -gt 0) {
        $SelectParam.First = $Limit
    }
    $TemplateData = $rawData | Select-Object @SelectParam | where-object $Filter
    Write-PSFMessage "File $DataFile imported, $($TemplateData.Count) entries after filtering original $($rawData.Count)"
    $uniqueRecipients = ($TemplateData | Select-Object -ExpandProperty $MailToColumn -ErrorAction SilentlyContinue | Measure-Object).count
    if ($RecipientList) {
        Write-PSFMessage -Level Host -Message "RecipientList parameter is set, ignoring MailToColumn and sending $($TemplateData.Count) mails to $($RecipientList.Count) recipients"
        $ConfirmMessage = "Sending $($TemplateData.Count) mails to $($RecipientList.Count) recipients"
        $MailToOverride = $RecipientList
    }
    else {
        $ConfirmMessage = "Sending $($TemplateData.Count) mails to $uniqueRecipients unique recipients from column $MailToColumn"
    }
    Invoke-PSFProtectedCommand -Action $ConfirmMessage -ScriptBlock {
        if ([string]::IsNullOrEmpty($TemplateData)) {
            Stop-PSFFunction -Level Warning -Message "No data found in Excel" -EnableException $true
        }
        if ($SubjectColumn -and -not $TemplateData[0].$SubjectColumn) {
            Stop-PSFFunction -Level Warning -Message "SubjectColumn '$SubjectColumn' not found in data, please check your input." -EnableException $true
        }
        if ($MailToColumn -and -not $TemplateData[0].$MailToColumn) {
            Stop-PSFFunction -Level Warning -Message "MailToColumn '$MailToColumn' not found in data, please check your input." -EnableException $true
        }
        if ($MailToOverride) {
            $singleMailParams.RecipientList = $MailToOverride
        }
        $TemplateData = $TemplateData | ConvertTo-PSFHashtable
        foreach ($entry in $TemplateData) {
            if (-not $MailToOverride) {
                $singleMailParams.RecipientList = $entry.$MailToColumn
            }
            if ($SubjectColumn -and $entry.PSObject.Properties.Name -contains $SubjectColumn) {
                $singleMailParams.Subject = $entry.$SubjectColumn
            }
            Write-PSFMessage "Sending mail to $($singleMailParams.RecipientList) with subject '$($singleMailParams.Subject)'"
            Write-PSFMessage "Mail Parameters: $($singleMailParams | ConvertTo-Json -Compress)"
            Send-MForgeMail @singleMailParams -templateParameters $entry
        }
    }
    if ($PSCmdlet.ParameterSetName -eq 'ByFile') {
        Write-PSFMessage "Removing temporary template $templateName"
        Remove-PSMDTemplate -TemplateName $TemplateName -Confirm:$false -ErrorAction SilentlyContinue
    }
}