When Environment Variables Go Wrong

One of the things I always try to impart on people leaning PowerShell is to make your scripts portable. While we’re usually writing scripts to solve a specific problem for ourselves in a specific environment, it really sucks when your friend at Acme IT Services is having an identical problem and you say “Oh I’ve got a great script to do that!”, but every path and location is hard coded and doesn’t work when they run it and you both spend a couple hours trying to integrate it to their situation. If that isn’t reason enough, even taking your own scripts from environment to environment, or job to job can be a total nightmare if you haven’t designed them with portability in mind.

This is where environment variables come in real handy, especially in larger, more secure or higher performance server deployments that are making use of system partitions and non-standard locations for various folders, or you’re running against various Windows kernel versions (XP vs Windows 7 for example).

Environment variables are a great tool, and there’s a lot of information you can get from them (just see a little list here https://ss64.com/nt/syntax-variables.html). However…I just ran into a problem with environment variables I obviously didn’t foresee. When a script is being run by a user, you have all kinds of environment variables available to you, but when you’re automating a script run and running the script as NETWORK SERVICE for example…you have a much smaller subset of environment variables (if any at all).

In my specific case, I have a script that needs to copy a file to a share in DFS, and I want the domain to be automatically populated in the path based on where the script is being run.

So here’s my code snip

Looks good to me, when I run it my file goes where I want it to go, everything is fantastic.

Now when I go to automate the script using Windows Task Scheduler. In this environment the security requirements don’t allow me to store a password for a service account when I create the scheduled task. Probably a good thing, since if the service account password changes this task would fail and likely nobody would notice for a good while. Since I’m accessing network resources, I’m electing to run the task as NETWORK SERVICE. However, when I run the script my file doesn’t end up in the location. My logging of course shows we’ve had an issue saving the file, but I don’t get the same error when I run the log in PowerShell ISE. That’s suspicious.

Turns out NETWORK SERVICE doesn’t have a USERDNSDOMAIN since it’s not really a user. So herein lies my problem. I’ve accounted for the portability of a script from one system to another, but I haven’t accounted for the specific account that’s running the script.

Well now how do we solve this problem? I still don’t want to hard code values, or build in a global settings section…since as a cardinal rule of mine I don’t want people to edit my scripts after I give them out in case they don’t know PowerShell. This is where WMI becomes a handy alternative to environment variables, without impacting script performance – and arguably giving me a much wider breadth of information.  A very quick modification to my script has me add a domain variable, and modify my Out-File statement slightly

Problem solved!

Now if I wanted to change my script behavior depending on if the variables are available or not, I could do something like this

Granted, the performance difference between the two is negligible but when I use the Measure-Command cmdlet to determine execution time, Environment Variables are clearly the faster option

As a matter of fact, Environment Variables are so fast they don’t even measure into full milliseconds. If this was a very large script, doing WMI queries could significantly impact our script performance.

That gives me an idea about doing a post on script performance…

I hope you’ve learned something from my little investigation here, it’s certainly given me one more thing to think about when designing my scripts.

Happy PowerShelling!

Leave a Reply

Your email address will not be published. Required fields are marked *