Creating variables dynamically in Powershell

pedrostc

Pedro Osternack Corrêa

Posted on January 4, 2022

Creating variables dynamically in Powershell

This is the first part of my "Being lazy with PowerShell" series were I'm documenting my exploration of the language by solving some simple problems. If want more context on what we're doing here please check the introduction post here.

TL; DR;

Here's the final snippet for this post.

$reposFolder = "C:\repos\"
$repos = @{
    client = "client-portal";
    dash = "service-dashboard";
    users = "users-api-v4";
    devops = "devops-toolkit";
    main = "backend-monster";
    data = "data-bridge";
}

$repos.Keys | ForEach-Object { New-Variable -Name $_ -Value ($reposFolder + $repos[$_])}
Enter fullscreen mode Exit fullscreen mode

This will generate one variable on the local scope of your PowerShell session for each entry on the $repos HashMap, and you can use them to easily navigate to the repo folders like this: cd $client.
If you want a more in-depth explanation of what's going on just keep scrolling :D.


Initial Setup

My first attempt is kind of simple, I'll create a new variable for each repo that I have, but I want to do that programmatically.
The first step was to create a HashMap with all the repos. On one hand I could list my repos folder and go from there, but the HashMap approach gives me the option of defining a key for each folder and use that as the variable name. Also I'm getting started so want to keep the logic very as light as possible.

So, this is what my repos folder looks like:

PS C:\repos> ls


    Directory: C:\repos


Mode                 LastWriteTime         Length Name                                                                                                                                                                               
----                 -------------         ------ ----                                                                                                                                                                               
d-----        2021-12-14   8:52 PM                backend-monster                                                                                                                                                       
d-----        2021-12-14   8:52 PM                client-portal                                                                                                                                                           
d-----        2021-12-14   8:52 PM                data-bridge                                                                                                                                                               
d-----        2021-12-14   8:52 PM                devops-toolkit                                                                                                                                                              
d-----        2021-12-14   8:52 PM                service-dashboard                                                                                    
d-----        2021-12-14   8:52 PM                users-api-v4
Enter fullscreen mode Exit fullscreen mode

Based on that I've created the following HashMap:

$repos = @{
    client = "client-portal";
    dash = "service-dashboard";
    users = "users-api-v4";
    devops = "devops-toolkit";
    main = "backend-monster";
    data = "data-bridge";
}
Enter fullscreen mode Exit fullscreen mode

Here I've mapped each one of my repos to a key that identifies them and that I'll use to identify my variables, such that the client-portal should be the value of the variable $client portal, and so on.

Building the script

Now that we have a map we need to iterate over my $repos map, for that end we can use either the HashMap ForEach method or the ForEach-Object cmdlet, I'll use the latter here, but they'll give us the same results. (there are more ways of solving this problem and I'll list the ones that I know at the end just as a curiosity)
Now, you cannot iterate over a HashMap in PowerShell the same way you can over an Array. By that I mean that if we just use the following command we will only see "Hello" printed once on our terminal.

$repos | ForEach-Object { "Hello" }
Enter fullscreen mode Exit fullscreen mode

To be able to iterate over each item we can use either $repos.Keys to get each key of the map or $repos.Values to get each value of the map. So if we update the snippet above to use the Keys property, we should see Hello being printed once for each item on the map (6 times in this case).

$repos.Keys | ForEach-Object { "Hello" }
Enter fullscreen mode Exit fullscreen mode

Ok, let's step back for a bit. What's happening on the snippet above is: we're evaluation $repos.Keys which will return each key on our map to the console, and passing that result to the ForEach-Object cmdlet using the pipe operaton |. That ForEach-Object cmdlet will print Hello for each item returned by the previous command ($repos.Keys in this case).
What is the difference between both snippets then? On the first one, evaluating $repo will return only one result, which will be the full map, so the ForEach-Object will only have 1 entry to work with. On the other hand, $repos.Keys will not return the list of keys, but each key inside of that list, which means that the ForEach-Object will receive 6 entries. Interesting, right?

Moving on. We can iterate over the keys of our map, and now we want to use them. to do so we can use the $_ variable. That is a special variable that powershell gives us so we can get current item being iterated over inside of the foreach loop. So, if we update our snippet again we will see all keys being printed on our console.

PS C:\repos> $repos.Keys | ForEach-Object { $_ }
dash
data
devops
client
users
main
Enter fullscreen mode Exit fullscreen mode

Ok, good, from here we need to access the value for those keys. To do so we can use them as indexes on the map like this: $repo[key]. Let's update the snippet once again.

PS C:\repos> $repos.Keys | ForEach-Object { $_ + " -> " + $repos[$_] }
dash -> service-dashboard
data -> data-bridge
devops -> devops-toolkit
client -> client-portal
users -> users-api-v4
main -> backend-monster
Enter fullscreen mode Exit fullscreen mode

Now all we need to do if define our new variables. The easiest way I could find to do that is by using the New-Variable cmdlet. We can than pass our key as the -Name argument and the path to the repo as the -Value argument to that cmdlet and we should be good to go. The path for the repos is a combination of the path to my repos folder (c:\repos) and the folder name in the map. The script should look something like this:

$repos.Keys | ForEach-Object { New-Variable -Name $_ -Value ("C:\repos\" + $repos[$_])}
Enter fullscreen mode Exit fullscreen mode

After running the above script now we should have available on our terminal one variable for each entry on our map. We can call them by prepending $ to the key. They should look something like this

$dash -> C:\repos\service-dashboard
$data -> C:\repos\data-bridge
$devops -> C:\repos\devops-toolkit
$client -> C:\repos\client-portal
$users -> C:\repos\users-api-v4
$main -> C:\repos\backend-monster
Enter fullscreen mode Exit fullscreen mode

And we can use them in combination with cd (or Set-Location if you want to get fancy) to navigate to our repos, ex.: cd $dash should send us to the C:\repos\service-dashboard directory and so on and so forth.

About the solution

This certainly works. I can have aliases for my folders and need to type less to get to my repos, but it's not there yet.
One issue is adding a new folder requires to create a new entry on the map on my posh profile. Admittedly this is not a huge issue, mainly if you consider the likeliness of adding new projects to the specific solution or how often that would happen.
Also we need to be very conscious of our variable names to not create unwanted conflicts, which may be a bigger problem if you rely on posh for your day-to-day work.
And there's also the small issue of needing to type cd $, the main problem being the $ I really want to avoid that. (Please note that this is not a problem at all, I just want an excuse to try some new stuff, so bear with me.)
So for my next attempt I'll probably try to tackle some of those points.

Well this is it for now, hope that you've liked this first post and maybe found it somewhat useful. See you on the next one.

Cover Photo by Dean Pugh on Unsplash

💖 💪 🙅 🚩
pedrostc
Pedro Osternack Corrêa

Posted on January 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related