Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions MailDaemon/MailDaemon.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
RootModule = 'MailDaemon.psm1'

# Version number of this module.
ModuleVersion = '1.1.3'
ModuleVersion = '1.2.14'

# ID used to uniquely identify this module
GUID = 'd5ba333f-5210-4d69-83f0-150dd0909139'
Expand All @@ -21,12 +21,12 @@
Description = 'Mail Daemon as PowerShell Module'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.0'
PowerShellVersion = '5.1'

# Modules that must be imported into the global environment prior to importing
# this module
RequiredModules = @(
@{ ModuleName='PSFramework'; ModuleVersion='1.12.346' }
@{ ModuleName='PSFramework'; ModuleVersion='1.13.416' }
)

# Assemblies that must be loaded prior to importing this module
Expand Down
22 changes: 21 additions & 1 deletion MailDaemon/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## 1.2.14 (2026-02-12)

+ Fix: Update-MDFolderPermission - does not set permissions for the failed folder

## 1.2.13 (2026-02-11)

+ New: Emails that could not be sent will no longer be permanently attempted to resend - after 14 days
+ Upd: Install-MDDaemon - now supports remote-deployment of the daemon task using credentials
+ Upd: Install-MDDaemon - now installs eventlog for MailDaemon
+ Upd: Invoke-MDDaemon - adds logging to the Windows Eventlog by default
+ Fix: Install-MDDaemon - no longer fails when the daemon task already exists
+ Fix: Invoke-MDDaemon - fails to use authentication for SMTP

## 1.1.7 (2026-02-10)

+ Upd: Invoke-MDDaemon - implements `UseSSL` as configured
+ Upd: Set-MDDaemon - supports `-UseSSL`
+ Upd: Install-MDDaemon - supports `-UseSSL`
+ Fix: Install-MDDaemon - now exports credentials without prompting.

## 1.1.3 (2024-11-11)

+ Upd: Added ability to directly embed attachments in the email task, rather than only providing a path to them. (thanks @jebbster88 ; #10)
Expand All @@ -18,7 +38,7 @@
## 0.1.1 (2019-02-09)

+ Fix: Bug in Install-MDDaemon causing errors during installation

## 0.1.0 (2019-02-09)

+ New: Everything
31 changes: 16 additions & 15 deletions MailDaemon/en-us/strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@
# Write-PSFMessage, Stop-PSFFunction or the PSFramework validation scriptblocks
@{
# General
'General.ModuleMissing' = 'The MailDaemon module could not be found in sufficient version on: {0}. Terminating Execution. To install or update to the current version, use Install-MDDaemon or access the PSGallery directly using "Install-Module MailDaemon".'
'General.ModuleMissing' = 'The MailDaemon module could not be found in sufficient version on: {0}. Terminating Execution. To install or update to the current version, use Install-MDDaemon or access the PSGallery directly using "Install-Module MailDaemon".'

# Invoke-MDDaemon
'Invoke-MDDaemon.SendMail.Start' = '{0} - Sending Mail: "{1}" From {2} to {3}'
'Invoke-MDDaemon.SendMail.Failed' = '{0} - Failed to send email!'
'Invoke-MDDaemon.SendMail.Success' = '{0} - Email sent successfully'
'Invoke-MDDaemon.ManageSuccessJob.Failed' = '{0} - Failed to move mail task to the "sent" folder!'
'Invoke-MDDaemon.SendMail.Abandon' = '{0} - Abandoning email after failing to send it within the configured timespan ({1})' # $email.Taskname, $abandonThreshold
'Invoke-MDDaemon.SendMail.Start' = '{0} - Sending Mail: "{1}" From {2} to {3}'
'Invoke-MDDaemon.SendMail.Failed' = '{0} - Failed to send email!'
'Invoke-MDDaemon.SendMail.Success' = '{0} - Email sent successfully'
'Invoke-MDDaemon.ManageSuccessJob.Failed' = '{0} - Failed to move mail task to the "sent" folder!'

# Copy-Module
'Copy-Module.ReceivingModule' = 'Receiving Module from {0}: {1}'
'Copy-Module.ReceivingModule.Failed' = 'Failed to receive Module from {0}: {1}'
'Copy-Module.InstallingModule' = 'Installing module {0} on: {1}'
'Copy-Module.ReceivingModule' = 'Receiving Module from {0}: {1}'
'Copy-Module.ReceivingModule.Failed' = 'Failed to receive Module from {0}: {1}'
'Copy-Module.InstallingModule' = 'Installing module {0} on: {1}'

# Send-MDMail
'Send-MDMail.Folder.CreationFailed' = 'Failed to create outgoing mail folder: {0}'
'Send-MDMail.Email.NotRegisteredYet' = 'No email has been set up yet!'
'Send-MDMail.Email.Sending' = 'Queueing email for sending: {0}'
'Send-MDMail.Email.SendingFailed' = 'Failed to queue email for sending: {0}'
'Send-MDMail.Email.TriggerFailed' = 'Failed to trigger the Mail Daemon task to send {0}'
'Send-MDMail.Folder.CreationFailed' = 'Failed to create outgoing mail folder: {0}'
'Send-MDMail.Email.NotRegisteredYet' = 'No email has been set up yet!'
'Send-MDMail.Email.Sending' = 'Queueing email for sending: {0}'
'Send-MDMail.Email.SendingFailed' = 'Failed to queue email for sending: {0}'
'Send-MDMail.Email.TriggerFailed' = 'Failed to trigger the Mail Daemon task to send {0}'

# Set-MDDaemon
'Set-MDDaemon.UpdatingSettings' = 'Starting Daemon configuration update on {0}'
'Set-MDDaemon.UpdateSetting' = 'Updating the Daemon configuration setting {0} to {1}'
'Set-MDDaemon.UpdatingSettings' = 'Starting Daemon configuration update on {0}'
'Set-MDDaemon.UpdateSetting' = 'Updating the Daemon configuration setting {0} to {1}'

# Update-MDFolderPermission
'Update-MDFolderPermission.Granting.DaemonUser' = 'Assigning write permissions as daemon account to {0} on "{1}" and "{2}"'
Expand Down
106 changes: 69 additions & 37 deletions MailDaemon/functions/Install-MDDaemon.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
function Install-MDDaemon
{
<#
function Install-MDDaemon {
<#
.SYNOPSIS
Configures a computer for using the Mail Daemon

Expand Down Expand Up @@ -31,6 +30,9 @@

.PARAMETER SentPath
The folder in which emails that were successfully sent are stored for a specified time before being deleted.

.PARAMETER FailedPath
The path where mails that could repeatedly not be sent are moved to.

.PARAMETER DaemonUser
The user to grant permissions needed to function as the Daemon account.
Expand All @@ -42,6 +44,12 @@

.PARAMETER MailSentRetention
The time to keep successfully sent emails around.

.PARAMETER MailAbandonThreshold
How long we attempt to send an email before abandoning it and moving it to -FailedPath.

.PARAMETER MailFailedRetention
How long we keep an abandoned email around before removing it entirely.

.PARAMETER SmtpServer
The mailserver to use for sending emails.
Expand All @@ -57,6 +65,13 @@
.PARAMETER RecipientDefault
Default email address to send the email to, if the individual script queuing the email does not specify one.

.PARAMETER UseSSL
Use SSL for sending emails.

.PARAMETER NoLogging
Disables logging.
Unless specified, this setup step will also prepare the windows eventlog by creating a dedicated eventlog for MailDaemon.

.EXAMPLE
PS C:\> Install-MDDaemon -ComputerName DC1, DC2, DC3 -TaskUser $cred -DaemonUser "DOMAIN\MailDaemon" -SmtpServer 'mail.domain.org' -SenderDefault 'daemon@domain.org' -RecipientDefault 'helpdesk-t2@domain.org'

Expand All @@ -83,6 +98,9 @@

[string]
$SentPath,

[string]
$FailedPath,

[string]
$DaemonUser,
Expand All @@ -92,6 +110,12 @@

[Timespan]
$MailSentRetention,

[Timespan]
$MailAbandonThreshold,

[Timespan]
$MailFailedRetention,

[string]
$SmtpServer,
Expand All @@ -103,11 +127,16 @@
$SenderCredential,

[string]
$RecipientDefault
$RecipientDefault,

[switch]
$UseSSL,

[switch]
$NoLogging
)

begin
{
begin {
#region Repetitions (ugly)
# Specifying repetitions directly in the commandline is ugly.
# It ignores explicit settings and requires copying the repetition object from another task.
Expand Down Expand Up @@ -155,9 +184,9 @@
#endregion Repetitions (ugly)

#region Setup Task Configuration
if (-not $NoTask)
{
if (-not $NoTask) {
$action = New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -Command Invoke-MDDaemon"
if ($NoLogging) { $action = New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -Command Invoke-MDDaemon -NoLogging" }
$triggers = @()
$triggers += New-ScheduledTaskTrigger -AtStartup -RandomDelay "00:15:00"
$triggers += New-ScheduledTaskTrigger -At "00:00:00" -Daily
Expand All @@ -173,21 +202,15 @@
TaskName = 'MailDaemon'
InputObject = $taskItem
}
if ($TaskUser)
{
if ($TaskUser) {
$parametersRegister["User"] = $TaskUser.UserName
$parametersRegister["Password"] = $TaskUser.GetNetworkCredential().Password
}
}
#endregion Setup Task Configuration

#region Preparing Parameters
$parameters = @{ }
foreach ($key in $PSBoundParameters.Keys)
{
if ($key -notin 'PickupPath', 'SentPath', 'MailSentRetention', 'SmtpServer', 'SenderDefault', 'RecipientDefault') { continue }
$parameters[$key] = $PSBoundParameters[$key]
}
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include 'PickupPath', 'SentPath', 'FailedPath', 'MailSentRetention', 'MailAbandonThreshold', 'MailFailedRetention', 'SmtpServer', 'SenderDefault', 'RecipientDefault', 'UseSSL'

$paramMainInstallCall = @{
ArgumentList = $parameters
Expand Down Expand Up @@ -217,8 +240,7 @@
#endregion The Main Setup Scriptblock
}

process
{
process {
#region Ensure Modules are installed
$testResults = Test-Module -ComputerName $ComputerName -Credential $Credential -Module @{
MailDaemon = $script:ModuleVersion
Expand All @@ -227,11 +249,9 @@

$failedTests = $testResults | Where-Object Success -EQ $false

if ($failedTests)
{
if ($failedTests) {
$grouped = $failedTests | Group-Object Name
foreach ($groupSet in $grouped)
{
foreach ($groupSet in $grouped) {
Copy-Module -ModuleName (Get-Module $groupSet.Name).ModuleBase -ToComputer $groupSet.Group.ComputerName
}
}
Expand All @@ -242,33 +262,45 @@
Invoke-PSFCommand @paramMainInstallCall

#region Securely store credentials
if ($PSBoundParameters.ContainsKey('SenderCredential'))
{
if ($PSBoundParameters.ContainsKey('SenderCredential')) {
$parametersSave = @{
ComputerName = $ComputerName
Credential = $SenderCredential
Path = 'C:\ProgramData\PowerShell\MailDaemon\senderCredentials.clixml'
ComputerName = $ComputerName
TargetCredential = $SenderCredential
Path = 'C:\ProgramData\PowerShell\MailDaemon\senderCredentials.clixml'
}
if ($Credential) { $parametersSave['Credential'] = $Credential }
if ($TaskUser) { $parametersSave['AccessAccount'] = $TaskUser }
Save-MDCredential @parametersSave

$parametersInvoke = @{ $parametersInvoke['ComputerName'] = $ComputerName }
$parametersInvoke = @{ ComputerName = $ComputerName }
if ($Credential) { $parametersInvoke['Credential'] = $Credential }
Invoke-PSFCommand @parametersInvoke -ScriptBlock {
Set-MDDaemon -SenderCredentialPath "C:\ProgramData\PowerShell\MailDaemon\senderCredentials.clixml"
}
}
#endregion Securely store credentials

#region Setup Logging
if (-not $NoLogging) {
Invoke-PSFCommand @parametersInvoke -ScriptBlock {
if ($PSVersionTable.PSVersion.Major -gt 5 -and -not $IsWindows) { return }
Set-PSFLoggingProvider -Name eventlog -InstanceName MailDaemonInvoke -LogName MailDaemon -Source MailDaemon -Enabled $true -Wait
Write-PSFMessage -Message "Setting up MailDaemon logging"
Disable-PSFLoggingProvider -Name eventlog -InstanceName MailDaemonInvoke
}
}
#endregion Setup Logging

#region Setup Task
if (-not $NoTask)
{
foreach ($computerObject in $ComputerName)
{
if ($ComputerName.Type -like 'CimSession') { $parametersRegister["CimSession"] = $computerObject.InputObject }
elseif (-not $ComputerName.IsLocalhost) { $parametersRegister["CimSession"] = $ComputerName }
$null = Register-ScheduledTask @parametersRegister
}
if (-not $NoTask) {
Invoke-PSFCommand @parametersInvoke -ScriptBlock {
param ($ParametersRegister)

$taskObject = Get-ScheduledTask -TaskName $ParametersRegister.TaskName -ErrorAction Ignore
if ($taskObject) { $taskObject | Unregister-ScheduledTask }

$null = Register-ScheduledTask @ParametersRegister
} -ArgumentList $parametersRegister
}
#endregion Setup Task
}
Expand Down
Loading