From 7830eb9867d9ec5b7fd1dc14bd5e49364210c7d1 Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Tue, 14 Jul 2026 13:56:32 +0200 Subject: [PATCH 1/2] fixing the installation --- MailDaemon/MailDaemon.psd1 | 4 +- MailDaemon/changelog.md | 7 ++ MailDaemon/en-us/strings.psd1 | 4 +- MailDaemon/functions/Install-MDDaemon.ps1 | 17 +++-- MailDaemon/internal/functions/Test-Module.ps1 | 73 +++++++++++-------- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/MailDaemon/MailDaemon.psd1 b/MailDaemon/MailDaemon.psd1 index 5da7264..1de9ee3 100644 --- a/MailDaemon/MailDaemon.psd1 +++ b/MailDaemon/MailDaemon.psd1 @@ -3,7 +3,7 @@ RootModule = 'MailDaemon.psm1' # Version number of this module. - ModuleVersion = '1.3.19' + ModuleVersion = '1.3.23' # ID used to uniquely identify this module GUID = 'd5ba333f-5210-4d69-83f0-150dd0909139' @@ -26,7 +26,7 @@ # Modules that must be imported into the global environment prior to importing # this module RequiredModules = @( - @{ ModuleName='PSFramework'; ModuleVersion='1.13.416' } + @{ ModuleName='PSFramework'; ModuleVersion='1.14.457' } @{ ModuleName='EntraAuth'; ModuleVersion='1.8.52' } ) diff --git a/MailDaemon/changelog.md b/MailDaemon/changelog.md index bc680c3..f261fd6 100644 --- a/MailDaemon/changelog.md +++ b/MailDaemon/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 1.3.23 (2026-07-14) + ++ Upd: Install-MDDaemon - added `-UsePWSH` parameter to allow running mailer with PowerShell 7 ++ Fix: Install-MDDaemon - fails to register scheduled task when not providing sender credentials ++ Fix: Install-MDDaemon - incorrectly detects modules as installed on local setup, when the modules are only installed for the local user ++ Fix: Install-MDDaemon - fails to ensure proper installation of EntraAuth on targeted computer + ## 1.3.19 (2026-05-28) + Major: Added capability to send emails by Graph API, rather than SMTP. diff --git a/MailDaemon/en-us/strings.psd1 b/MailDaemon/en-us/strings.psd1 index 402c61a..5e02fff 100644 --- a/MailDaemon/en-us/strings.psd1 +++ b/MailDaemon/en-us/strings.psd1 @@ -25,8 +25,8 @@ '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}' # ($ComputerName -join ", ") + 'Set-MDDaemon.UpdateSetting' = 'Updating the Daemon configuration setting {0} to {1}' # $key, $Parameters[$key] # Update-MDFolderPermission 'Update-MDFolderPermission.Granting.DaemonUser' = 'Assigning write permissions as daemon account to {0} on "{1}" and "{2}"' diff --git a/MailDaemon/functions/Install-MDDaemon.ps1 b/MailDaemon/functions/Install-MDDaemon.ps1 index 49bf5dc..cbef484 100644 --- a/MailDaemon/functions/Install-MDDaemon.ps1 +++ b/MailDaemon/functions/Install-MDDaemon.ps1 @@ -172,7 +172,10 @@ $CertificateName, [switch] - $NoLogging + $NoLogging, + + [switch] + $UsePWSH ) begin { @@ -224,8 +227,10 @@ #region Setup Task Configuration 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" } + $executable = 'powershell.exe' + if ($UsePWSH) { $executable = 'pwsh.exe' } + $action = New-ScheduledTaskAction -Execute $executable -Argument "-NoProfile -Command Invoke-MDDaemon" + if ($NoLogging) { $action = New-ScheduledTaskAction -Execute $executable -Argument "-NoProfile -Command Invoke-MDDaemon -NoLogging" } $triggers = @() $triggers += New-ScheduledTaskTrigger -AtStartup -RandomDelay "00:15:00" $triggers += New-ScheduledTaskTrigger -At "00:00:00" -Daily @@ -285,6 +290,7 @@ $testResults = Test-Module -ComputerName $ComputerName -Credential $Credential -Module @{ MailDaemon = $script:ModuleVersion PSFramework = (Get-Module -Name PSFramework).Version + EntraAuth = (Get-Module -Name EntraAuth).Version } $failedTests = $testResults | Where-Object Success -EQ $false @@ -300,6 +306,9 @@ $paramMainInstallCall['ComputerName'] = $ComputerName Invoke-PSFCommand @paramMainInstallCall + + $parametersInvoke = @{ ComputerName = $ComputerName } + if ($Credential) { $parametersInvoke['Credential'] = $Credential } #region Securely store credentials if ($PSBoundParameters.ContainsKey('SenderCredential')) { @@ -312,8 +321,6 @@ if ($TaskUser) { $parametersSave['AccessAccount'] = $TaskUser } Save-MDCredential @parametersSave - $parametersInvoke = @{ ComputerName = $ComputerName } - if ($Credential) { $parametersInvoke['Credential'] = $Credential } Invoke-PSFCommand @parametersInvoke -ScriptBlock { Set-MDDaemon -SenderCredentialPath "C:\ProgramData\PowerShell\MailDaemon\senderCredentials.clixml" } diff --git a/MailDaemon/internal/functions/Test-Module.ps1 b/MailDaemon/internal/functions/Test-Module.ps1 index cf437fa..f167093 100644 --- a/MailDaemon/internal/functions/Test-Module.ps1 +++ b/MailDaemon/internal/functions/Test-Module.ps1 @@ -1,6 +1,5 @@ -function Test-Module -{ -<# +function Test-Module { + <# .SYNOPSIS Tests for module existence. @@ -31,6 +30,9 @@ .PARAMETER Quiet Disables output objects and instead returns $true if all modules specified meet the requirements, $false if not so. + + .PARAMETER Scope + What scope the module has been installed to. .PARAMETER ComputerName The computers on which to test. @@ -81,6 +83,10 @@ [switch] $Quiet, + + [ValidateSet('AllUsers','CurrentUser','Any')] + [string] + $Scope = 'Any', [Parameter(ValueFromPipeline = $true)] [PSFComputer[]] @@ -91,16 +97,13 @@ $Credential ) - begin - { + begin { #region Prepare Module parameter $moduleHash = $Module - foreach ($moduleName in $Name) - { + foreach ($moduleName in $Name) { $moduleHash[$moduleName] = $Version } - foreach ($key in ([string[]]$moduleHash.Keys)) - { + foreach ($key in ([string[]]$moduleHash.Keys)) { $moduleHash[$key] = $moduleHash[$key] -as [Version] if (-not $moduleHash[$key]) { $moduleHash[$key] = ([Version]'0.0.0.0') } } @@ -116,12 +119,14 @@ $Test, [bool] - $Quiet + $Quiet, + + [string] + $Scope ) #region Utility Functions - function Write-Result - { + function Write-Result { [CmdletBinding()] param ( [string] @@ -140,32 +145,44 @@ $result = [bool]$Success [PSCustomObject]@{ - Name = $Name - Success = $result + Name = $Name + Success = $result VersionsFound = $VersionsFound ComputerName = $env:COMPUTERNAME - Test = $Test + Test = $Test } } #endregion Utility Functions + $paths = $env:PSModulePath -split ';' + if ($PSVersionTable.PSVersion.Major -gt 5 -and -not $IsWindows) { + $paths = $env:PSModulePath -split ':' + } + $scopedPath = @($paths)[0] + #region Validate each module specified - foreach ($module in $ModuleHash.Keys) - { - $modulesFound = Get-Module -Name $module -ListAvailable + foreach ($module in $ModuleHash.Keys) { + $modulesFound = Get-Module -Name $module -ListAvailable | Where-Object { + $Scope -eq 'Any' -or + ( + $Scope -eq 'CurrentUser' -and + $_.ModuleBase -like "$($scopedPath)*" + ) -or + ( + $Scope -eq 'AllUsers' -and + $_.ModuleBase -notlike "$($scopedPath)*" + ) + } if ($Quiet -and (-not $modulesFound)) { return $false } - if ($ModuleHash[$module] -le '0.0.0.0') - { + if ($ModuleHash[$module] -le '0.0.0.0') { Write-Result -Name $module -Success $modulesFound -VersionsFound $modulesFound.Version -Test $Test continue } #region Quiet Validation [Calls Continue] - if ($Quiet) - { - switch ($Test) - { + if ($Quiet) { + switch ($Test) { 'LesserThan' { if (-not ($modulesFound | Where-Object Version -LT $ModuleHash[$module])) { return $false } } 'LesserEqual' { if (-not ($modulesFound | Where-Object Version -LE $ModuleHash[$module])) { return $false } } 'Equal' { if (-not ($modulesFound | Where-Object Version -EQ $ModuleHash[$module])) { return $false } } @@ -176,8 +193,7 @@ } #endregion Quiet Validation [Calls Continue] - switch ($Test) - { + switch ($Test) { 'LesserThan' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -LT $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test } 'LesserEqual' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -LE $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test } 'Equal' { Write-Result -Name $module -Success ($modulesFound | Where-Object Version -EQ $ModuleHash[$module]) -VersionsFound $modulesFound.Version -Test $Test } @@ -191,8 +207,7 @@ } #endregion Validation Scriptblock } - process - { - Invoke-PSFCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $moduleHash, $Test, $Quiet.ToBool() -HideComputerName + process { + Invoke-PSFCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $moduleHash, $Test, $Quiet.ToBool(), $Scope -HideComputerName } } \ No newline at end of file From 103c98e08381642700d36c28f0a3e13924fd271f Mon Sep 17 00:00:00 2001 From: Friedrich Weinmann Date: Tue, 14 Jul 2026 14:26:56 +0200 Subject: [PATCH 2/2] additional fixes --- MailDaemon/MailDaemon.psd1 | 2 +- MailDaemon/changelog.md | 7 ++++++- MailDaemon/functions/Install-MDDaemon.ps1 | 7 +++++-- MailDaemon/functions/Invoke-MDDaemon.ps1 | 5 +++++ MailDaemon/functions/Set-MDMail.ps1 | 2 +- MailDaemon/functions/Update-MDFolderPermission.ps1 | 2 +- MailDaemon/internal/configurations/daemon.ps1 | 2 +- MailDaemon/internal/functions/Copy-Module.ps1 | 2 +- MailDaemon/internal/functions/Test-Module.ps1 | 10 +++------- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/MailDaemon/MailDaemon.psd1 b/MailDaemon/MailDaemon.psd1 index 1de9ee3..b09c4d1 100644 --- a/MailDaemon/MailDaemon.psd1 +++ b/MailDaemon/MailDaemon.psd1 @@ -3,7 +3,7 @@ RootModule = 'MailDaemon.psm1' # Version number of this module. - ModuleVersion = '1.3.23' + ModuleVersion = '1.3.32' # ID used to uniquely identify this module GUID = 'd5ba333f-5210-4d69-83f0-150dd0909139' diff --git a/MailDaemon/changelog.md b/MailDaemon/changelog.md index f261fd6..cb2efb4 100644 --- a/MailDaemon/changelog.md +++ b/MailDaemon/changelog.md @@ -1,11 +1,16 @@ # Changelog -## 1.3.23 (2026-07-14) +## 1.3.32 (2026-07-14) + Upd: Install-MDDaemon - added `-UsePWSH` parameter to allow running mailer with PowerShell 7 + Fix: Install-MDDaemon - fails to register scheduled task when not providing sender credentials + Fix: Install-MDDaemon - incorrectly detects modules as installed on local setup, when the modules are only installed for the local user + Fix: Install-MDDaemon - fails to ensure proper installation of EntraAuth on targeted computer ++ Fix: Install-MDDaemon - fails to ensure proper directory access for specified daemon & mail senders. ++ Fix: Invoke-MDDaemon - fails to clear temp folder if sending mails with binary attachment-data attached ++ Fix: Update-MDFolderPermission - fails to apply proper mail sender permission. ++ Fix: Set-MDMail - The `-Priority` parameter ignores the "Normal" value. ++ Fix: Config - Failed email tasks were put into the same folder as successfully sent emails. ## 1.3.19 (2026-05-28) diff --git a/MailDaemon/functions/Install-MDDaemon.ps1 b/MailDaemon/functions/Install-MDDaemon.ps1 index cbef484..1526e73 100644 --- a/MailDaemon/functions/Install-MDDaemon.ps1 +++ b/MailDaemon/functions/Install-MDDaemon.ps1 @@ -93,6 +93,9 @@ Disables logging. Unless specified, this setup step will also prepare the windows eventlog by creating a dedicated eventlog for MailDaemon. + .PARAMETER UsePWSH + When setting up the mail daemon task, use PowerShell 7, rather than the default Windows PowerShell + .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' @@ -254,7 +257,7 @@ #endregion Setup Task Configuration #region Preparing Parameters - $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include 'PickupPath', 'SentPath', 'FailedPath', 'MailSentRetention', 'MailAbandonThreshold', 'MailFailedRetention', 'SmtpServer', 'SenderDefault', 'RecipientDefault', 'UseSSL', 'ClientID', 'TenantID', 'Identity', 'Federated', 'CertificateThumbprint', 'CertificateName' + $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include 'PickupPath', 'SentPath', 'FailedPath', 'MailSentRetention', 'MailAbandonThreshold', 'MailFailedRetention', 'SmtpServer', 'SenderDefault', 'RecipientDefault', 'UseSSL', 'ClientID', 'TenantID', 'Identity', 'Federated', 'CertificateThumbprint', 'CertificateName', 'DaemonUser','WriteUser' if ($parameters.Federated -or $parameters.Identity -or $parameters.ClientID) { $parameters.Type = 'Graph' } $paramMainInstallCall = @{ @@ -291,7 +294,7 @@ MailDaemon = $script:ModuleVersion PSFramework = (Get-Module -Name PSFramework).Version EntraAuth = (Get-Module -Name EntraAuth).Version - } + } -Scope AllUsers $failedTests = $testResults | Where-Object Success -EQ $false diff --git a/MailDaemon/functions/Invoke-MDDaemon.ps1 b/MailDaemon/functions/Invoke-MDDaemon.ps1 index 4a8ddf2..98bb32d 100644 --- a/MailDaemon/functions/Invoke-MDDaemon.ps1 +++ b/MailDaemon/functions/Invoke-MDDaemon.ps1 @@ -136,6 +136,11 @@ } } } + # Remove temp deserialized attachments if used + if ($email.AttachmentsBinary) { + $null = Remove-Item -Path $tempAttachmentParentDir -Recurse -Force + } + #endregion Abandon Email if beyond threshold Stop-PSFFunction -String 'Invoke-MDDaemon.SendMail.Failed' -StringValues $email.Taskname -ErrorRecord $_ -Continue -Target $email.Taskname } diff --git a/MailDaemon/functions/Set-MDMail.ps1 b/MailDaemon/functions/Set-MDMail.ps1 index 42da978..6e225cd 100644 --- a/MailDaemon/functions/Set-MDMail.ps1 +++ b/MailDaemon/functions/Set-MDMail.ps1 @@ -111,6 +111,6 @@ function Set-MDMail if ($Attachments) { $script:mail["Attachments"] = $Attachments } if ($RemoveAttachments.IsPresent) { $script:mail["RemoveAttachments"] = ([bool]$RemoveAttachments) } if ($NotBefore) { $script:mail["NotBefore"] = $NotBefore } - if ($Priority) { $script:mail["Priority"] = $Priority } + if ($PSBoundParameters.Keys -contains 'Priority') { $script:mail["Priority"] = $Priority } } } diff --git a/MailDaemon/functions/Update-MDFolderPermission.ps1 b/MailDaemon/functions/Update-MDFolderPermission.ps1 index b89dfe8..9f89155 100644 --- a/MailDaemon/functions/Update-MDFolderPermission.ps1 +++ b/MailDaemon/functions/Update-MDFolderPermission.ps1 @@ -83,7 +83,7 @@ } foreach ($user in $WriteUser) { - if ($user.Trim()) { continue } + if (-not $user.Trim()) { continue } Write-PSFMessage -String 'Update-MDFolderPermission.Granting.WriteUser' -StringValues $user, $pickupPath $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($user, 'Write', 'Allow') diff --git a/MailDaemon/internal/configurations/daemon.ps1 b/MailDaemon/internal/configurations/daemon.ps1 index 8d7747f..5c01b55 100644 --- a/MailDaemon/internal/configurations/daemon.ps1 +++ b/MailDaemon/internal/configurations/daemon.ps1 @@ -1,6 +1,6 @@ Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailPickupPath' -Value "$(Get-PSFPath -Name ProgramData)\PowerShell\MailDaemon\Pickup" -Initialize -Validation 'string' -SimpleExport -Description "The folder from which the daemon will pickup email tasks." Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailSentPath' -Value "$(Get-PSFPath -Name ProgramData)\PowerShell\MailDaemon\Sent" -Initialize -Validation 'string' -SimpleExport -Description "The folder into which completed tasks are moved" -Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailFailedPath' -Value "$(Get-PSFPath -Name ProgramData)\PowerShell\MailDaemon\Sent" -Initialize -Validation 'string' -SimpleExport -Description "The folder into which failed tasks are moved" +Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailFailedPath' -Value "$(Get-PSFPath -Name ProgramData)\PowerShell\MailDaemon\Failed" -Initialize -Validation 'string' -SimpleExport -Description "The folder into which failed tasks are moved" Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailSentRetention' -Value (New-TimeSpan -Days 7) -Initialize -Validation 'timespan' -SimpleExport -Description "How long sent email tasks are retained" Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailAbandonThreshold' -Value (New-TimeSpan -Days 14) -Initialize -Validation 'timespan' -SimpleExport -Description "How long we try to send failing tasks, before abandoning them" Set-PSFConfig -Module 'MailDaemon' -Name 'Daemon.MailFailedRetention' -Value (New-TimeSpan -Days 14) -Initialize -Validation 'timespan' -SimpleExport -Description "How long failed email tasks are retained" diff --git a/MailDaemon/internal/functions/Copy-Module.ps1 b/MailDaemon/internal/functions/Copy-Module.ps1 index e8ae6a4..7467001 100644 --- a/MailDaemon/internal/functions/Copy-Module.ps1 +++ b/MailDaemon/internal/functions/Copy-Module.ps1 @@ -83,7 +83,7 @@ #region Specified a module name else { - $moduleObject = Get-Module $Module | Sort-Object Version -Descending | Select-Object -First 1 + $moduleObject = Get-Module $Module -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1 if (-not $moduleObject) { return [pscustomobject]@{ diff --git a/MailDaemon/internal/functions/Test-Module.ps1 b/MailDaemon/internal/functions/Test-Module.ps1 index f167093..e8684be 100644 --- a/MailDaemon/internal/functions/Test-Module.ps1 +++ b/MailDaemon/internal/functions/Test-Module.ps1 @@ -154,11 +154,7 @@ } #endregion Utility Functions - $paths = $env:PSModulePath -split ';' - if ($PSVersionTable.PSVersion.Major -gt 5 -and -not $IsWindows) { - $paths = $env:PSModulePath -split ':' - } - $scopedPath = @($paths)[0] + $userPath = $HOME #region Validate each module specified foreach ($module in $ModuleHash.Keys) { @@ -166,11 +162,11 @@ $Scope -eq 'Any' -or ( $Scope -eq 'CurrentUser' -and - $_.ModuleBase -like "$($scopedPath)*" + $_.ModuleBase -like "$($userPath)*" ) -or ( $Scope -eq 'AllUsers' -and - $_.ModuleBase -notlike "$($scopedPath)*" + $_.ModuleBase -notlike "$($userPath)*" ) } if ($Quiet -and (-not $modulesFound)) { return $false }