I created this function but I don't know how to make it work. here is the code:
function ProgramRegistry {
param (
[Parameter(Mandatory=$false)][HashTable]$HashTable,
[Parameter(Mandatory=$false)][String]$AlertPath,
[Parameter(Mandatory=$false)][String]$AlertName,
[Parameter(Mandatory=$false)][String]$AlertValue
)
foreach ($AlertPath in $HashTable.Values){
foreach($AlertName in $HashTable.Values){
foreach($AlertValue in $HashTable.Values){
New-Item -Path $AlertPath -Force | Out-Null
New-ItemProperty -Path $AlertPath -Name $AlertName -Value $AlertValue -PropertyType DWORD -Force
}
}
}
}
$keys = [ordered]@{
key1 = @{
AlertPath = 'Path'
AlertName = 'Name'
AlertValue = 'Value'
}
key2 = @{
AlertPath = 'Path'
AlertName = 'Name'
AlertValue = 'Value'
}
# and so on...
}
ModifyRegistry @keys
ModifyRegistry -AlertPath "path" -AlertName "name" -AlertValue "value"
I want to be able to call the function in 2 different ways (as shown in the script)
- either by defining its 3 parameters explicitly in one line.
- or by passing a nested hash table consisting of multiple objects each having the function's 3 parameters.
how can I achieve that?
I want to only modify the function and not the way I call it. I need to call it a bunch of times and want to keep the code for doing it as minimal as possible, like this ModifyRegistry @keys
. it's okay if the function itself is complicated and long but I want calls to function to take very little code like that. instead of nested hash table, I could just call the function repeatedly but it'd be too much repeated code and that's what I want to avoid.
CodePudding user response:
You may modify your function to accept a hashtable of hashtables. You just need to provide some logic to check if the hashtable received is a hashtable containing other hashtables that have the values you need or if it is instead a single hashtable containing the values you need. Also needed is to handle the other parameters still when not providing a hashtable. The example below shows how I would do this in an advanced function utilizing a begin, process, and end block. In the begin block we only need to create the collection object that we will use to sift out the inputs. The process block is repeated for each input object received when using the pipeline. If supplying the arguments to the function directly this process block will only run once. We will use this process block to determine and add our input objects to the $inputs arraylist we created. In the end block we will perform the actual processing on each of the objects that we've collected.
function ProgramRegistry {
[cmdletbinding()]
param (
# ValueFromPipeline attribute will allow piping hashtables to the function
[Parameter(Mandatory = $false, ValueFromPipeline)][HashTable]$HashTable,
[Parameter(Mandatory = $false)][String]$AlertPath,
[Parameter(Mandatory = $false)][String]$AlertName,
[Parameter(Mandatory = $false)][String]$AlertValue
)
begin {
# Create an arraylist to collect hashtables for later processing in end block
$inputs = [System.Collections.ArrayList]::new()
}
process {
if ($HashTable) {
if ($HashTable.ContainsKey('AlertPath')) {
# if single hashtable is received with 'AlertPath' key add to inputs for processing in end block
$inputs.Add($HashTable) | Out-Null
}
else {
foreach ($value in $HashTable.Values) {
# check if value of key is a hashtable
if ($value -is [hashtable]) {
# check if hashtable contains key "AlertPath" and if so add to $inputs for processing in end block
if ($value.ContainsKey('AlertPath')) {
$inputs.Add($value) | Out-Null
}
else {
Write-Warning "Invalid hashtable format - missing 'AlertPath' key"
}
} else {
Write-Warning "Object is not a hashtable"
}
}
}
}
else {
# process when not a hashtable by creating a hashtable and adding to $inputs
$inputs.Add(@{
AlertPath = $AlertPath
AlertName = $AlertName
AlertValue = $AlertValue
}) | Out-Null
}
}
end {
# Process hashtables collected in $inputs
foreach ($hash in $inputs) {
# your code here
[pscustomobject]@{
Path = $hash.AlertPath
Name = $hash.AlertName
Value = $hash.AlertValue
}
}
}
}
$keys = [ordered]@{
key1 = @{
AlertPath = 'Path1'
AlertName = 'Name1'
AlertValue = 'Value1'
}
key2 = @{
AlertPath = 'Path2'
AlertName = 'Name2'
AlertValue = 'Value2'
}
# and so on...
}
ProgramRegistry -HashTable $keys
# or
$keys | ProgramRegistry
# or even
ProgramRegistry -HashTable $keys.key1 #or $keys.key1 | ProgramRegistry
If pipeline and advanced function is not wanted you can still do something similar without begin, process, and end blocks. I use nested function 'processit' so that I don't have to repeat the processing logic multiple times
function ProgramRegistry {
param (
[Parameter(Mandatory = $false)][HashTable]$HashTable,
[Parameter(Mandatory = $false)][String]$AlertPath,
[Parameter(Mandatory = $false)][String]$AlertName,
[Parameter(Mandatory = $false)][String]$AlertValue
)
# create nested function to process each hash table.
function processit {
param([hashtable]$hash)
# add processing logic here
[pscustomobject]@{
Path = $hash.AlertPath
Name = $hash.AlertName
Value = $hash.AlertValue
}
}
if ($HashTable) {
if ($HashTable.ContainsKey('AlertPath')) {
# if single hashtable is received with 'AlertPath' key process it
processit -hash $HashTable
}
else {
foreach ($value in $HashTable.Values) {
# check if value of key is a hashtable
if ($value -is [hashtable]) {
# check if hashtable contains key "AlertPath" and if so process it
if ($value.ContainsKey('AlertPath')) {
processit -hash $value
}
else {
Write-Warning "Invalid hashtable format - missing 'AlertPath' key"
}
}
else {
Write-Warning 'Object is not a hashtable'
}
}
}
}
else {
processit @{AlertPath = $AlertPath; AlertName = $AlertName; AlertValue = $AlertValue }
}
}
Update in response to your question regarding using the key name as AlertName
function ProgramRegistry {
param (
[Parameter(Mandatory = $false)][HashTable]$HashTable,
[Parameter(Mandatory = $false)][String]$AlertPath,
[Parameter(Mandatory = $false)][String]$AlertName,
[Parameter(Mandatory = $false)][String]$AlertValue
)
# create nested function to process each hash table.
function processit {
param([hashtable]$hash)
# add processing logic here
[pscustomobject]@{
Path = $hash.AlertPath
Name = $hash.AlertName
Value = $hash.AlertValue
}
}
if ($HashTable) {
if ($HashTable.ContainsKey('AlertPath')) {
# if single hashtable is received with 'AlertPath' key process it
processit -hash $HashTable
}
else {
foreach ($item in $HashTable.GetEnumerator()) {
if ($item.Value -is [hashtable]) {
# check if the hashtable has AlertPath and AlertValue keys
if ($item.Value.ContainsKey('AlertPath') -and $item.Value.ContainsKey('AlertValue')) {
$hash = $item.Value
# check if hashtable contains an AlertName key.
if (-not $hash.ContainsKey('AlertName')){
# If not use parent key name
$hash.AlertName = $item.Key
}
processit -hash $hash
}
else {
Write-Warning "Invalid hashtable format - missing AlertPath and/or AlertValue key"
}
}
else {
Write-Warning "Item does not contain a hashtable"
}
}
}
}
else {
processit @{AlertPath = $AlertPath; AlertName = $AlertName; AlertValue = $AlertValue }
}
}
Calling the function
$items = @{
Alert1 = @{
AlertPath = 'Path1'
AlertValue = 'Value1'
}
Alert2 = @{
AlertPath = 'Path2'
AlertValue = 'Value2'
}
Alert3 = @{
AlertName = 'Overridden AlertName'
AlertPath = 'Path3'
AlertValue = 'Value3'
}
Alert4 = @{
AlertValue = 'Value2'
}
Alert5 = "just a string"
}
ProgramRegistry -HashTable $items
Output
WARNING: Item does not contain a hashtable
WARNING: Invalid hashtable format - missing AlertPath and/or AlertValue key
Path Name Value
---- ---- -----
Path3 Overridden AlertName Value3
Path2 Alert2 Value2
Path1 Alert1 Value1