One nice feature of a DSC configuration is that all resources support specifying a DependsOn
property that ensures that the resources that it depends on are ran first. Every once in a while, I find myself wanting to use that feature in other scripts. I created a module called DependsOn to do that for me.
Index
- Index
- Basic Scenario
- Resolve-DependencyOrder
- Parameters
- DependsOn alias
- Value maps
- Installing DependsOn
- About DependsOn
Basic Scenario
Let’s say I am creating some user groups from a datafile. Each item specifys the group name and the members of the group. Here is basic sample of what that json may look like.
[
{
"Name":"Group1"
},
{
"Name":"Group2",
"Members":"Group1"
}
]
If I process this list in order, I would create Group1
first and then create Group2
with Group1
as a member. You can see how processing this in order is important.
What if if we flipped the order?
[
{
"Name":"Group2",
"Members":"Group1"
},
{
"Name":"Group1"
}
]
When we go to create Group2
with Group1
as a member before creating Group1
then I would expect our script to have an error.
We could manually keep our list in order. But this list could grow to be quite large or even data driven where we don’t have a way to contol it.
Resolve-DependencyOrder
This is where DependsOn offers a solution. It will take a list of items, allow you to define how they depend on each other, and then sort the items in order of that dependency.
I’m going to add a few more records and save them into $groups
.
$groups = @'
[
{
"Name":"Group4",
"Members":["Group3","Group2"]
},
{
"Name":"Group3",
"Members":"Group1"
},
{
"Name":"Group2",
"Members":"Group1"
},
{
"Name":"Group1"
}
]
'@ | ConvertFrom-Json
Now we get to sort it with Resolve-DependencyOrder
.
PS\> $groups | Resolve-DependencyOrder -Key {$_.Name} -DependsOn {$_.Members} |
Select-Object Name, Members
Name Members
---- -------
Group1
Group3 Group1
Group2 Group1
Group4 {Group3, Group2}
It processed the groups from top to bottom. When it identified that Group4
depended on Group3
and Group2
, it made sure they were in the list ahead of Group4
. It then checked Group3
to see that it depended on Group1
. So it made sure that Group1
was in the list ahead of Group3
. By the time it got to Group2
, Group1
was already in the list so it was not added a 2nd time.
You may have noticed that Group3
and Group2
could have been processed in any order as long as Group1
was processed first. The secondary sort is based on order of discovery. Either by walking dependencies or processing the list. I just happened to discover Group3
before Group2
.
Parameters
Taking a closer look at the Resolve-DependencyOrder
, we will see 2 important parameters. The Key
defines how to to identify the object. The DependsOn
then defines how to to identify what your object depends on. So it needs to be able to match the DependsOn
values to the Key
values. Both of these parameters are scriptblocks for maximum flexibility.
Here is a second example that use and array of hashtables instead of objects:
$familyTree = [ordered]@(
@{Name='Girl'; DependsOn='Dad','Mom'}
@{Name='Mom'; DependsOn='Dad'}
@{Name='Dad'; DependsOn='Grandpa','Grandma'}
@{Name='Grandpa'; DependsOn=$null}
@{Name='Grandma'; DependsOn='Grandpa'}
@{Name='Boy'; DependsOn='Dad','Mom'}
)
$familyTree |
Resolve-DependencyOrder -Key {$_.name} -DependsOn {$_.DependsOn} |
ForEach-Object {$_.name}
This will produce this list:
Grandpa
Grandma
Dad
Mom
Girl
Boy
DependsOn alias
The alias for Resolve-DependencyOrder
is DependsOn
. I thing the name is a little long so I personally prefer the alias for this one.
$familyTree |
DependsOn -Key {$_.name} -DependsOn {$_.DependsOn}
Value maps
My first 2 examples expect that the data has enough information to deterine what it’s own dependencies are. We are also able to reference other sets of data or call other commands as part of that mapping process.
Here is an example where I have a list of strings that I want to order by using an external dependency map in a hashtable.
$list = @(
'Girl'
'Mom'
'Grandpa'
'Dad'
'Grandma'
'Boy'
)
$map = [ordered]@{
'Girl'='Dad','Mom'
'Mom'='Dad'
'Grandpa' = $null
'Dad'='Grandpa','Grandma'
'Grandma'='Grandpa'
'Boy'='Dad','Mom'
}
$list | Resolve-DependencyOrder -DependsOn {$map[$_]}
If you don’t specify a key then it takes each object at it’s value. When you have a list of strings then that is the value that gets used.
I used a hashtable here, but it easily could have been a call to a database or a reset endpoint for dependency information.
Installing DependsOn
This module is published to the PSGallery.
Install-Module DependsOn -Scope CurrentUser
About DependsOn
This is a hybrid module that support PowerShell Core and was built using the PowerShell Standard Library. This is also the first open source module from loandDepot. I hope to see more of our modules and DSC resources like this opened up soon.
Let me know if you find a good use for this module.