Public/Set-MailAccount.ps1
|
<# .SYNOPSIS Creates or changes an account that Send-Mail can send from. .DESCRIPTION An account is a named set of SMTP settings, like the accounts of msmtp: each one with its own server, user, sender and default recipients, so the same machine and user can send from several mailboxes. `Send-Mail -Account <name>` sends from one of them; the default account is used when Send-Mail is called without -Account. The settings are kept in accounts.json, in the folder ElektoMailPosh of the user's configuration folder (%APPDATA% on Windows, ~/.config elsewhere), or in the folder named by SMTP_CONFIG_DIR. Passwords are never written there: - On Windows they go to the Credential Manager, as the generic credential "ElektoMailPosh:<name>", encrypted with the user's logon (DPAPI). - On Linux and macOS they go to the file secrets/<name> in that folder, readable only by its owner. - On any platform, the environment variable SMTP_PASS_<NAME> (the name in upper case, with any character other than letters and digits as "_") takes precedence over the stored password. Only the settings given are changed. An empty value removes a setting. .PARAMETER Name The name of the account: letters, digits, '.', '-' and '_', starting with a letter or digit. .PARAMETER Server The SMTP server. Required for a new account (unless it comes from -FromEnvironment). .PARAMETER Port The SMTP server port. Default is 587. The connection is always encrypted with STARTTLS. .PARAMETER User The SMTP authentication username. Required for a new account (unless it comes from -FromEnvironment). .PARAMETER From The sender address. Default is the User. Servers such as Gmail only accept the user's own address or an alias configured in the mailbox. .PARAMETER FromName The sender display name. Default is the machine name. .PARAMETER To Default recipients, when Send-Mail is called without -To. .PARAMETER Cc Default carbon copy recipients, when Send-Mail is called without -Cc. .PARAMETER Bcc Default blind carbon copy recipients, when Send-Mail is called without -Bcc. .PARAMETER ReplyTo Default addresses for replies, when Send-Mail is called without -ReplyTo. .PARAMETER Password The SMTP password (for Gmail, an app password), stored as described above. .PARAMETER Default Makes this the default account. -Default:$false stops it from being the default. .PARAMETER FromEnvironment Takes the settings not given as parameters, and the password, from the SMTP_* environment variables (SMTP_SERVER, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_FROM, SMTP_FROM_NAME, SMTP_TO, SMTP_CC, SMTP_BCC and SMTP_REPLY_TO). This moves an installation configured by environment variables to an account. .EXAMPLE Set-MailAccount -Name automation -FromEnvironment -Default Moves the configuration in the SMTP_* environment variables to the account "automation" and makes it the default. Send-Mail then ignores those variables, which can be removed. .EXAMPLE Set-MailAccount -Name work -Server smtp.office365.com -User me@example.com -FromName "My Name" -Password (Read-Host -AsSecureString 'Password') Creates the account "work", asking for its password without showing it. .EXAMPLE Set-MailAccount -Name work -ReplyTo support@example.com Makes replies to the email sent from "work" go to support@example.com. #> Function Set-MailAccount { [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true, Position = 0)] [ValidatePattern('^[A-Za-z0-9][A-Za-z0-9_.-]*$')] [string]$Name, [string]$Server, [ValidateRange(1, 65535)] [int]$Port, [string]$User, [string]$From, [string]$FromName, [string[]]$To, [string[]]$Cc, [string[]]$Bcc, [string[]]$ReplyTo, [securestring]$Password, [switch]$Default, [switch]$FromEnvironment ) $file = Read-AccountFile $accounts = if ($file) { $file.Accounts } else { [ordered]@{} } $defaultName = if ($file) { $file.Default } else { $null } # An existing account keeps the spelling of its name $existingName = Find-AccountName $file $Name if ($existingName) { $Name = $existingName } $settings = [ordered]@{} if ($existingName) { foreach ($key in $accounts[$Name].psbase.Keys) { $settings[$key] = $accounts[$Name][$key] } } $secret = $null if ($FromEnvironment) { $environment = Get-EnvironmentConfiguration foreach ($key in $script:AccountSettingNames) { if (-not $PSBoundParameters.ContainsKey($key) -and $environment[$key]) { $settings[$key] = $environment[$key] } } $secret = $environment.Password } foreach ($key in $script:AccountSettingNames) { if (-not $PSBoundParameters.ContainsKey($key)) { continue } $value = $PSBoundParameters[$key] if ($key -in $script:AddressSettingNames) { $value = ConvertTo-AddressList $value } if (@($value).Count -eq 0 -or "$value" -eq '') { $settings.Remove($key) } else { $settings[$key] = $value } } if ($Password) { $secret = [Net.NetworkCredential]::new('', $Password).Password } if (-not $settings.Server -or -not $settings.User) { throw "Account '$Name' needs a server and a user: give -Server and -User." } if ($PSBoundParameters.ContainsKey('Default')) { if ($Default) { $defaultName = $Name } elseif ($defaultName -eq $Name) { $defaultName = $null } } if (-not $PSCmdlet.ShouldProcess("account '$Name' in '$(Get-AccountFilePath)'", 'Save')) { return } $accounts[$Name] = $settings Write-AccountFile $defaultName $accounts Write-Verbose "Account '$Name' saved in '$(Get-AccountFilePath)'." if ($secret) { Save-StoredPassword $Name $settings.User $secret Write-Verbose "Password of account '$Name' stored." } elseif (-not (Get-AccountPassword $Name)) { Write-Warning (Get-MissingPasswordMessage $Name) } # While an account is the default, the SMTP_* variables are of no use, and SMTP_PASS should not linger if ($defaultName -eq $Name) { $variables = @('SMTP_SERVER', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASS', 'SMTP_FROM', 'SMTP_FROM_NAME', 'SMTP_TO', 'SMTP_CC', 'SMTP_BCC', 'SMTP_REPLY_TO') | Where-Object { [Environment]::GetEnvironmentVariable($_) } if ($variables) { Write-Warning "'$Name' is the default account, so Send-Mail no longer reads $($variables -join ', '). Remove them from the environment, above all SMTP_PASS." } } } |