Creating variables dynamically in Powershell
Tags: poweshell, beginners, blog
Published: 2022-01-04 22:24:09

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](https://dev.to/pedrostc/being-lazy-with-powershell-2dd9).

## TL; DR;
Here's the final snippet for this post.

```powershell
$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[$_])}
```
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](https://docs.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.2) 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:
```powershell
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
```
Based on that I've created the following HashMap:

```powershell
$repos = @{
    client = "client-portal";
    dash = "service-dashboard";
    users = "users-api-v4";
    devops = "devops-toolkit";
    main = "backend-monster";
    data = "data-bridge";
}
```
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](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.2#inputs) 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](https://docs.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-hashtable?view=powershell-7.2#iterating-hashtables) 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.

```powershell
$repos | ForEach-Object { "Hello" }
```
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).

```powershell
$repos.Keys | ForEach-Object { "Hello" }
```
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.

```powershell
PS C:\repos> $repos.Keys | ForEach-Object { $_ }
dash
data
devops
client
users
main
```
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.

```powershell
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
``` 
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](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-variable?view=powershell-7.2). 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:

```powershell
$repos.Keys | ForEach-Object { New-Variable -Name $_ -Value ("C:\repos\" + $repos[$_])}
```
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
```
And we can use them in combination with `cd` (or [Set-Location](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/set-location?view=powershell-7.2) 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 <a href="https://unsplash.com/@wezlar11?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Dean Pugh</a> on <a href="https://unsplash.com/collections/4510513/coding?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>