One of the most common feedback items for PowerShell scripts that I’ve published, such as Test-ExchangeServerHealth.ps1, is that having to edit the script to insert the correct email settings is a pain. The other is that the scripts are unsigned, requiring the execution policy of the machine running the script to be relaxed.
These two things go hand in hand. I don’t sign the scripts because they always need editing to work in your environment. And I include the settings that need editing in the script, instead of making them parameters, because I want the script to be easy to run (a lot of parameters seems to increase the number of “How do I…?” questions I get).
To solve these issues, at least partially (people may still choose to edit scripts to customize their operations in other ways), I am looking at breaking these settings out into a separate file instead.
Here is a demonstration of how this would work. I’ve created a simple demo script and Settings.xml file to go with it.
The PowerShell script is quite simple – it sends an email message.
<# .SYNOPSIS Script Settings File Demo #> [CmdletBinding()] param () #------------------------------------------------- # Variables #------------------------------------------------- $myDir = Split-Path -Parent $MyInvocation.MyCommand.Path # Import email settings from config file [xml]$ConfigFile = Get-Content "$MyDirSettings.xml" $smtpsettings = @{ To = $ConfigFile.Settings.EmailSettings.MailTo From = $ConfigFile.Settings.EmailSettings.MailFrom Subject = "Email Subject Line Goes here" SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer } $body = "Email body goes here" #------------------------------------------------- # Script #------------------------------------------------- try { Send-MailMessage @smtpsettings -Body $body -ErrorAction STOP } catch { Write-Warning $_.Exception.Message } #------------------------------------------------- # The End #-------------------------------------------------
The script imports the Settings.xml file to determine the various email settings it should use.
# Import email settings from config file [xml]$ConfigFile = Get-Content "$MyDirSettings.xml"
The XML file itself is also quite simple.
The script uses the data in the XML file to set parameters for the email message. Values can still be set directly in the script (like the email subject below), but I think it will make more sense to put them in the XML file instead.
$smtpsettings = @{ To = $ConfigFile.Settings.EmailSettings.MailTo From = $ConfigFile.Settings.EmailSettings.MailFrom Subject = "Email Subject Line Goes here" SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer }
When the script is executed an email message is sent. Simple as that.
This concept extents beyond just the email settings for scripts. Several of my scripts also include other variables such as alert thresholds, text strings for different languages, and so on. The more of these that are moved into a Settings.xml file like the above demonstration uses, the fewer reasons there will be to edit a script file directly.
If you have any feedback or comments on this I’d love to hear them.
Hi Paul
Thanks for sharing
Can you please advise if there is a way to define the settings file outside the script so i can reuse the script with multiple settings files e.g:
powershell.exe c:\scripts\myscript.ps1 -switchforsettings c:\script\settings1.xml
powershell.exe c:\scripts\myscript.ps1 -switchforsettings c:\script\settings2.xml
powershell.exe c:\scripts\myscript.ps1 -switchforsettings c:\script\settings3.xml
I know I could create separate folders for each instance which contains the script and settings file for each program (and the script would be identical) but then I would have multiple copies of the code to maintain.
Any ideas would be very well received
Many thanks
Adam
Paul, for all scripts in one bunch of module how to prepare single json or psxml files and write all the cmdletbindings defined all parameters in it so that from the single psxml file we can call all the parameters anywere in individual scripts in module
Anythoughts on best way to encrypt strings in the XML config? I am using something similar to above, but have a password paramater in config id like secure.
The Real Person!
The Real Person!
If you take a look at my stored creds functions here…
https://github.com/cunninghamp/PowerShell-Stored-Credentials/blob/master/Functions-PSStoredCredentials.ps1
…you’ll see how passwords can be securely stored in plain text files (which includes XML).
Nices, stills works even on a macOS with Powershell Core
Brilliantly clear post and EXACTLY what I was looking for!
Wonderful post! Thanks Paul!
How to access the path of the script? I want to be able to just put the script and it’s config anywhere and let the script read the config file from the same folder. Regardless of where the script is called from.
Use $PSScriptRootsettings.xml
This will prefix the the settings.xml path to the same as the script that executed it.
@Paul Cunningham – On the previous version of Get-DailyBackupAlerts it was posssible to have multiple recipients in the To field. This was achieved by seperating each in inverted commas, with a comma between (e.g. From = “1@xyz.com”,”2@abc.com”,”3@aaa.com). This no longer seems possible with the Settings.xml file. Please would you advise me how to do this?
The Real Person!
The Real Person!
I recommend using a distribution group.
@Richard and Paul
A bit late but I had the same problem as you Richard.
When setting the $smtpsettings hashtable, I changed the ‘To’ key to be:
To= @($ConfigFile.Settings.EmailSettings.MailTo.Split(“,”).Trim())
The XML line is:
MailTo>user1@domain.com,user2@domain1.com etc.
This seems to have done the trick.
Mike
Nice way to use the script in many different enviroments, like i do.
Thanks for the hint, i’m going to use this in my scripts.
No complains here:)
I don’t think its a lot of work to edit a script somebody talented like you wrote.
Thanks for the effort you put into it
Depends on the purpose. To maximize flexibility and general purpose usage, I parametrize stuff. Yes, that will increase erroneous usage but I see that as part of the learning curve of the one running the script. For testing or repeated usage, I use splatting for all or specific parameters like you do yourself (‘@’), you only read them from an XML in your script. Downside is that this creates a dependency (on that file) and requires proper syntax and XML (where some characters aren’t allowed). On the plus side, they can send you that XML to try to reproduce issues etc. or you can use it to run scheduled jobs (without zillions of parameters). I use an XML file for my Install-Exchange2013 script to save parameters and state information in between reboots.