Somewhat New Learning Powershell - Azure & JSON Script

Hi everyone, I am trying to learn PowerShell from a course I purchased with Jeff Hicks and also got his practice book.
I not 100% new to programming (took it many many years ago but forgotten) nor am I new to PowerShell, although I haven’t actually written code, I just find scripts and for the most part can follow along on what they do.
I decided to actually sit and write code for myself to master powershell and learn it and it been a challenge learning from the start.
I am stuck at writing a code for azure where it cycles through a bunch of parameter files in a folder in JSON format to input 3 fields. for the most part, from my testing, everything is looking fine except for the last part of the code. i got the idea from a script i seen that installs fonts and cycles through a folder to install them. i need to deploy about 60 azure policies so i figured it would be simplier to write a powershell script to cycle through the parameter files.
the final command is New-AzPolicyAssignment and that statement to assign the policy.
it works fine until i get to the -policyparamter part of the command. i can’t seem to understand how to parse from the JSON file.
i figured out i needed a convertfrom-json but now i am stuck trying to understand this:
the code works if i do things manually without trying to cycle through the folder and run the code 1 by 1 for each policy and json file i am deploying, but it takes time to do that!
anyways, here is my code if someone can assist me on why it gives me this error:

#=========================================================================
$ParamFolder = "C:\Users\Omar Amer\Desktop\src\policies\resource-naming\assignments\"
$ParamItem = Get-Item -Path $ParamFolder
$ParamList = Get-ChildItem -Path $ParamItem -Filter ("*.json")
$mg = Get-azmanagementgroup -GroupName 'Prod'

#Reference the policy to be used for assigning the policy
$definition = Get-AzPolicyDefinition | Where-Object { $_.Properties.DisplayName -eq 'Resource Naming - Long' }
$NonComplianceMessages = @(@{Message="Resource Naming Convention - Long. Please Fix!"})

foreach ($Param in $ParamList) {
        Write-Host 'Assigning Policy -' $Param.BaseName
        $ParamName = "Prod-Name-$($param.basename)"
        write-host $ParamName
        $assignmentParams = Get-Content $Param.FullName | ConvertFrom-Json -Depth 10
        New-AzPolicyAssignment -Name "Prod-Naming-$($Paramname)" -DisplayName "Prod-Naming-$($Paramname)" -Scope $mg.id -PolicyDefinition $definition -NonComplianceMessage $NonComplianceMessages -PolicyParameter $assignmentParams
}

This is my error: 
Assigning Policy - virtualMachine
Prod-Name-virtualMachine
New-AzPolicyAssignment: 
Line |
   6 |          New-AzPolicyAssignment -Name "Prod-Naming-$($Paramname)" -Dis …
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unexpected character encountered while parsing value: @. Path '', line 0, position 0.


If i run the script manually, with no foreach loop and just manually putting in the -name, -displayname, and -policyparameter with no variables and point directly to the json files, it works 100%. 

inside the json file is the following for each policy/resource i am trying to deploy this to:

{

    "resourceType": {

        "value": "Microsoft.Compute/virtualMachines"

    },

    "resourceAbbreviation": {

        "value": "vm"

    },

    "resourceNamingConvention": {

        "value": "long"

    }

}

thanks for the insight and help. this might be too advanced for me since i am still “new” to learning powershell formyself verses always finding code.
Thanks to anyone who reads this, i know its lengthy.

Hi, welcome to the forum :wave:

-PolicyParameter is expecting a hashtable but by default, ConvertFrom-Json returns a PSCustomObject.

If you have PowerShell 7+ you can try the -AsHashtable parameter. If you’re using PowerShell 5.x then you’ll need to do it manually.

$json = Get-Content E:\Temp\Files\params.json | ConvertFrom-Json

$ht = @{}

foreach ($property in $json.PSObject.Properties) {
    $ht[$property.Name] = $property.Value.Value
}
$ht
1 Like

Thank you! glad to be here. really hoping to start mastering powershell and writing some advanced code in the future.

i do have powershell 7. although, i am trying to write this code in VBS as per a colleague who suggested it to start learning powershell. Not sure what the difference is between PS7 and VBS honestly?

nonetheless, does the code you presented need to be inside the foreach loop of my $param variable? or is it a separate function/code outside of the foreach loop and i pass the the $ht variable into -policyparamter in the foreach loop? this is what my brain is trying to understand to make sure it clicks in:
Thought Process:
-when the foreach loop runs, and gets to the new-azpolicy command (without the -policyparamater in the code) it prompts me for a resource type, a resource abbreviation, and a resource convention (as per the policy i have) that i can enter in manually, so how does each of these values inside the json value get entered/passed into -policyparamater based on your feedback?
-im still trying to learn how foreach loops work as when i type in $param after the loops runs and fails, it gives me an output of the file name of the last json file. so during each loop, $param changes to the next file until it gets to the end, or is alllllll of the files stored in $param variable?

sorry for the questions, i do ask alot but its only so that i can fully understand it as it like a pro.
i’m still new to json in powershell so trying to understand it more as i will be using it more and more as i try and code things for Azure vs using the gui.

thank you for all your help and your teachings :slight_smile:

Ok, so i answered my second question about all the values getting into memory and the variable $ht has all the values.

thanks for the code Matt! it has helped and i think i understand how its working. although i have one slight problem with the first line of code $json = get-content… … … In here, i have to still manually type in each parameter file. how can i get it to cycle through the folder? i tried using get-child instead but it threw another error.

the foreach loop now works using -policyparamaterobject and i think i get what you said about json returning it as a object now.

so the issue remains is that if i run the code and point directly to a paramater file, it uses that value over and over again for the forloop which won’t work

i am starring at it trying to figure my how to get the $Json to cycle through all the paramater files in the folder

i thought maybe i could do something like this:
$json = Get-ChildItem -Path $ParamItem -Filter ("*.json") | ConvertFrom-Json

but this returns a unexpected character while parsing error message

so this is my updated code now:

$ParamFolder = “C:\Users\K-pup\Desktop\naming\params”
$ParamItem = Get-Item -Path $ParamFolder
$ParamList = Get-ChildItem -Path $ParamItem -Filter ("*.json")
$mg = Get-azmanagementgroup -GroupName ‘Prod’

#Reference the policy to be used for assigning the policy
$definition = Get-AzPolicyDefinition | Where-Object { $_.Properties.DisplayName -eq ‘Resource Naming - Long’ }
$NonComplianceMessages = @(@{Message=“Resource Naming Convention - Long. Please Fix!”})

$json = Get-Content C:\Users\K-pup\Desktop\naming\params\app.json | ConvertFrom-Json

$ht = @{}

foreach ($property in $json.PSObject.Properties) {
$ht[$property.Name] = $property.Value.Value
}

foreach ($Param in $ParamList) {
Write-Host ‘Assigning Policy -’ $Param.BaseName
$ParamName = “Prod-Name-$($param.basename)”
write-host $ParamName
New-AzPolicyAssignment -Name $ParamName -DisplayName $Paramname -Scope $mg.id -PolicyDefinition $definition -NonComplianceMessage $NonComplianceMessages -PolicyParameterObject $ht
}

Thanks again for the Wisdom!

VBS (Visual Basic Script) is a different scripting language. Do you mean Visual Studio Code? That’s a great editor for PowerShell but it is capable of running both versions of PowerShell so you need to be sure which one you’re using. The version running will be shown in the bottom right-hand corner.

Version 5.1 is Windows PowerShell and ships with the operating system.
Version 7.x is the cross-platform version of PowerShell and has to be installed separately.
Visual Studio Code is an Integrated Development Environment (IDE) for writing PowerShell and other languages.

does the code you presented need to be inside the foreach loop

Only you can answer that :slight_smile:
If the same settings are going to be applied to all machines, you can do it once, before your loop. If you have a different settings file to apply to each machine, then it will need to be inside the loop.

so how does each of these values inside the json value get entered/passed into -policyparamater based on your feedback

If you look at the documentation for the cmdlet, that parameter expects a hashtable (a dictionary of key value pairs). You pass the parameter the variable, which refers to the hashtable, and the cmdlet does the rest.

after the loops runs and fails, it gives me an output of the file name of the last json file. so during each loop, $param changes to the next file until it gets to the end, or is alllllll of the files stored in $param variable

This will depend on the type of error, terminating or non-terminating, and how you handle the errors. If you don’t do any error handling, then there’s a good chance your script will just stop on the error. If you handle the errors, then you can continue processing all the files.

Hey Matt,

Yes I meant VSC. Silly me. The version shows 1.61

I believe in my code since it has to cycle through each parameter file, yes the Json needs to be inside the original forloop when i think about it. this will let it cycle through each paramter file in the original for loop.

and boom! got my code working! this is so awesome and exciting. my first real powershell code that i put together! :star_struck:

Got it working with the following:

$ParamFolder = “C:\Users\K-pup\Desktop\naming\params”
$ParamItem = Get-Item -Path $ParamFolder
$ParamList = Get-ChildItem -Path $ParamItem -Filter ("*.json")
$mg = Get-azmanagementgroup -GroupName ‘Prod’

#Reference the policy to be used for assigning the policy
$definition = Get-AzPolicyDefinition | Where-Object { $_.Properties.DisplayName -eq ‘Resource Naming - Long’ }
$NonComplianceMessages = @(@{Message=“Resource Naming Convention - Long. Please Fix!”})

#cycle through each paramter file, convert it from json,store the values in a loop and push the policy to azure
foreach ($Param in $ParamList) {
Write-Host ‘Assigning Policy -’ $Param.BaseName
$ParamName = “Prod-Name-$($param.basename)”
write-host $ParamName

    #convert the param file into a Object
    $json = $Paramlist | ConvertFrom-Json

    #loop through each json file and pull the values
    $ht = @{}
    foreach ($property in $json.PSObject.Properties) {
        $ht[$property.Name] = $property.Value.Value
    }

    #Deploy the policy based on all the variables collected above
    New-AzPolicyAssignment -Name $ParamName -DisplayName $Paramname -Scope $mg.id -PolicyDefinition $definition -NonComplianceMessage $NonComplianceMessages -PolicyParameterObject $ht

}

Much appreciated Matt!

Well done for getting it working :clap:

What you’ve posted should not be working though.:thinking:

You’re referencing the array of files when you convert from Json, not the individual file and you’re also missing the Get-Content command.

Just as a tip for future posts. When posting code, please use the pre-formatted button </> and make sure all your code (and errors too, if you post them), is pasted inbetween the backticks. That will ensure your post is nicely formatted and it makes it easier to read, and copy and paste for testing.

I will try to remember to use the correct formatting.

hmmm i must have pasted the wrong code then, or my onedrive did not update fast enough as i am working on 2 computers separately. I do have get-content in the code.

let me paste what i have now:


#VARIABLES=========================================================================
#Update the location of where your JSON files exist
$ParamFolder = "C:\Users\K-pup\Desktop\src\policies\resource-naming\LongNames\"

$ParamItem = Get-Item -Path $ParamFolder
$ParamList = Get-ChildItem -Path $ParamItem -Filter ("*.json")

#Get the management group you need the policy to assign to. Update the -GroupName to whatever you need
$mg = Get-azmanagementgroup -GroupName 'Prod'

#Reference the policy to be used for assigning the policy
$definition = Get-AzPolicyDefinition | Where-Object { $_.Properties.DisplayName -eq 'Resource Naming - Long' }

#Default non-compliance message to be used varialble
$NonComplianceMessages = @(@{Message="Resource Naming Convention - Long. Please Fix!"})
#VARIABLES END=========================================================================

#MAIN CODE=========================================================================
#cycle through each paramter file, convert it from json,store the values in a loop and push the policy to azure
foreach ($Param in $ParamList) {
        Write-Host 'Assigning Policy -' $Param.BaseName
        $ParamName = "Prod-Name-$($param.basename)"
        write-host $ParamName


        #convert the param file into a Object
        $json = get-content $Param | ConvertFrom-Json

        #loop through each json file and pull the values
        $ht = @{}
        foreach ($property in $json.PSObject.Properties) {
            $ht[$property.Name] = $property.Value.Value
        }

        #Deploy the policy based on all the variables collected above
        New-AzPolicyAssignment -Name $ParamName -DisplayName $Paramname -Scope $mg.id -PolicyDefinition $definition -NonComplianceMessage $NonComplianceMessages -PolicyParameterObject $ht
}

#MAIN END=========================================================================

Hopefully i learned how to use that </> button correctly.

Let me know what you think :smiley: again big thanks for your help!
This has saved me HOURS of manually deploying 100 naming policies in azure and now into 1 minute!!! huge difference in time savings.

Yes, that looks much better :+1:

One improvement you could make is to remove:

$ParamItem = Get-Item -Path $ParamFolder

This line is redundant, you can just do:

$ParamList = Get-ChildItem -Path $ParamFolder -Filter '*.json'

Thanks! I’ll give that a try an see how it runs.