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.

powershell-xml-settings-01

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.

powershell-xml-settings-02

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.

About the Author

Paul Cunningham

Paul is a former Microsoft MVP for Office Apps and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul no longer writes for Practical365.com.

Comments

  1. Adam

    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

  2. himanshuk

    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

  3. Wil C

    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.

  4. Gonz

    Nices, stills works even on a macOS with Powershell Core

  5. Michael

    Brilliantly clear post and EXACTLY what I was looking for!

  6. Van Fanel

    Wonderful post! Thanks Paul!

  7. Wouter

    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.

    1. James L

      Use $PSScriptRootsettings.xml

      This will prefix the the settings.xml path to the same as the script that executed it.

  8. Richard A

    @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?

      1. Mike

        @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

  9. Richard Voogt

    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.

  10. Me

    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

  11. Michel de Rooij

    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.

Leave a Reply