Json value retrive from powershell - json

I am trying to fetch some values from json array if condition meet
My Json File:
{
"ImpactMitigationTime": 638159040000000000,
"Status": "Active",
"ExternalIncidentId": null,
"RecommendedActions": null,
"PlatformInitiated": true,
"ImpactStartTime": 638114093314870000,
"SubscriptionId": "xxxx",
"LastUpdateTime": 638120832531660300,
"EventSource": "ServiceHealth",
"TrackingId": "VNY4-RC8",
"EventLevel": "Informational",
"impactType": null,
"EventType": "HealthAdvisory",
"Priority": 22,
"duration": null,
"Summary": "<p><em>You’re receiving this notification because you use Azure Active Directory (Azure AD).</em></p>",
"Header": "Your service might have been impacted by an Azure service issue",
"Impact": [
{
"ImpactedService": "Azure Active Directory",
"ImpactedRegions": [
{
"ImpactedRegion": "West US 2",
"Status": "Active"
},
{
"ImpactedRegion": "East US",
"Status": "Resolved"
}
]
},
{
"ImpactedService": "Multi-Factor Authentication",
"ImpactedRegions": [
{
"ImpactedRegion": "South Central US",
"Status": "Active"
},
{
"ImpactedRegion": "Central US",
"Status": "Resolved"
}
]
}
],
"Title": "Action required: Add your IPv6 ranges to Conditional Access policies by 31 March 2023",
"Level": "Warning",
"IsHIR": false
}
Need PS script to pull value only of Active status Impacted regions
My Current PS Script:
$varNotifications = gc "C:\Users\pocadmin\Desktop\1.json" | ConvertFrom-Json
if($varnotifications.Impact.ImpactedRegions | Where {$_.Status -eq 'Active'})
{
$varsubscriptionId = $varnotifications.subscriptionid
$varimpactedServices = $varnotifications.Impact.ImpactedService
$varimpactedRegions = $varnotifications.Impact.ImpactedRegions.ImpactedRegion
$varstatus = $varnotifications.Impact.ImpactedRegions.status
}
$varsubscriptionId
$varimpactedServices
$varimpactedRegions
$varstatus
My Output:
xxxx
Azure Active Directory
Multi-Factor Authentication
West US 2
East US
South Central US
Central US
Active
Resolved
Active
Resolved

You shouldn't try to fetch the info in various separate variables, but have the code return an array of objects with values that belong to each other.
Something like this:
$data = Get-Content -Path 'C:\Users\pocadmin\Desktop\1.json' | ConvertFrom-Json
# loop through the data and return only objects where the Status for the ImpactedRegion equals 'Active'
$result = $data.impact | ForEach-Object {
$service = $_.ImpactedService
foreach ($region in ($_.ImpactedRegions | Where-Object {$_.Status -eq 'Active'})) {
[PsCustomObject]#{
Service = $service
Region = $region.ImpactedRegion
Status = $region.Status
}
}
}
# output on screen
$result
# output to CSV file if you like
$result | Export-Csv -Path 'C:\Users\pocadmin\Desktop\1.csv' -NoTypeInformation -UseCulture
On console screen this outputs
Service Region Status
------- ------ ------
Azure Active Directory West US 2 Active
Multi-Factor Authentication South Central US Active
The -UseCulture switch lets PowerShell create a CSV file where the delimiter character is whatever is defined in your systems ListSeparator. By doing this, you can simply double-click the file to open it in Excel on machines that have the same regional settings

Your method only gets an array of the form #(), you need to get the value from it.
$varnotifications.Impact.ImpactedRegions |Where-Object {$_.status -eq "active"} |Select-Object -Expand ImpactedRegion

Related

Edit Windows Terminal profiles settings JSON from PowerShell

I am working on a PowerShell script that creates a Fedora WSL using docker, it all works, but I cannot get to work the code part which sets the icon in the settings.json file.
Relevant part of the JSON:
"profiles":
{
"defaults": {},
"list":
[
{
"commandline": "PATH\\TO\\WSL",
"guid": "{your-guid}",
"hidden": false,
"name": "fedora",
"icon": "PATH\\TO\\ICON"
},
{
"commandline": "cmd.exe",
"guid": "{your-guid}}",
"hidden": false,
"name": "Command Prompt"
},
{
"guid": "{your-guid}}",
"hidden": false,
"name": "Azure Cloud Shell",
"source": "Windows.Terminal.Azure"
},
Here is what I've tryied:
$settings = Get-Content $env:localappdata'\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json' -raw | ConvertFrom-Json
$settings.profiles.list | % {if($_.name -eq $WSLname){$_.icon=$InstallPath\fedora.ico}}
$settings | ConvertTo-Json -depth 32| set-content $env:localappdata'\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json'
Variables are taken from params in first part of the script.
My goal is to chech if the profile name with given input by the user exists, if so, changes or adds the "icon" property to the fedora.ico path.
Edit: This part of the script needs to run after windows terminal has been relaunched.
Here is how you can approach the logic for your code to check if the icon property exists and assign a new value to it and if it doesn't exist, add a new property to the object with the new value. Hope the inline comments helps you understand the logic.
$settings = Get-Content 'path\to\settings.json' -Raw | ConvertFrom-Json
# enumerate all objects in `.profiles.list`
foreach($item in $settings.profiles.list) {
# if the `Name` property is not equal to `$WSLName`
if($item.name -ne $WSLname) {
# we can skip this object, just go next
continue
}
# if we're here we know `$item.name` is equal to `$WSLname`
# so we need to check if the `icon` property exists, if it does
if($item.PSObject.Properties.Item('icon')) {
# assign a new value to it
$item.icon = "$InstallPath\fedora.ico"
# and go to the next element
continue
}
# if we're here we know `$item.name` is equal to `$WSLname`
# but the `icon` property does not exist, so we need to add it
$item.PSObject.Properties.Add([psnoteproperty]::new('icon', "$InstallPath\fedora.ico"))
}
$settings | ConvertTo-Json -Depth 32 | Set-Content 'path\to\newsetting.json'

How to use PowerShell to access array object within JSON file without an index

High-Level Concept
This PowerShell script will be shared between numerous SQL servers. If the server's name is in the JSON file, it will then run something similar to Set-Service -Name SQLEngine_InstanceA -StartType Manual using SQL service names from the JSON file and change their starttype to either "disabled", "on-demand", "manual", or "automatic". It will then start those services listed in the JSON file.
Goal
The JSON file in a specific format, and the script needs to act upon parameters specified in the JSON file. The script will check if the executing server's name exists in the JSON file then update the services starttype based on the JSON file parameters. It will also check against each parameter in the JSON file to know if it should or shouldn't act on it.
Basics of what I need to accomplish:
Server runs this PowerShell script regularly (every 15 mins) to know if the SQL services will be brought down for maintenance within that window.
Script runs against a JSON file StartServices.json which contains:
serverName
startAt
Services
serviceName
Order
startMode
If the ServerName matches $env:COMPUTERNAME and the startAt >= $Current_Time it continues to next step.
It should iterate through each service matching the computer name, specifically in the "Order" specified, and run Start-Service -Name $ServiceName -StartType $StartMode. Order is important because we require starting/stopping certain services before others.
I'm stuck on step #4. Here's a simplified example of the script I'm using to access the parameters. I'm unable to cleanly reference the Services section because PowerShell creates an array at that level when its ingested via ConvertFrom-JSON. The problem with an array is I want to avoid hardcoding indexes since there might be only 3 services to act on or more than 5.
I would like to access this element ideally by something like $content.Server_Name["ServerABC"].Services or similar Object based approach.
Example PowerShell Script
# Declare Variables
$InputFile = 'C:\temp\StartServices.json'
$ParsedRaw = Get-Content -Raw -Path $InputFile | ConvertFrom-Json
$vPSObject = $ParsedRaw
$serverName = $vPSObject.serverName
$services = $vPSObject.services #this just lists ALL service names, order, and startMode
# Check if JSON file exists
if (Test-Path -Path $InputFile -PathType Leaf) {
Write-Host "JSON File Exists"
# Check if Server name is in list
if ($serverName -contains $env:COMPUTERNAME) {
$currentServerIndex = $serverName.IndexOf($env:COMPUTERNAME)
Write-Host "The current index of $env:COMPUTERNAME is $currentServerIndex"
# Check if StartAt time in JSON is after the current time
$DateTimeNow = Get-Date
$DateTimeEvent = [DateTime]::ParseExact($vPSObject.startAt[$currentServerIndex], 'yyy-MM-dd HH:mm:ss', $null) # this format needed to match JSON time formatting
if ($DateTimeEvent -gt $DateTimeNow.DateTime) {
Write-Host "This will run since startAt is in the future"
# Area I'm stuck - Getting Service Start Mode & Status without using Indexes
$StartTypeEngine = Get-Service -Name $vPSObject.serverName[$currentServerIndex].services.serviceName[0] | Select -Property starttype -ExpandProperty starttype
$StartTypeBrowser = Get-Service -Name $vPSObject.serverName[$currentServerIndex].services.serviceName[1] | Select -Property starttype -ExpandProperty starttype
$StartTypeAgent = Get-Service -Name $vPSObject.serverName[$currentServerIndex].services.serviceName[2] | Select -Property starttype -ExpandProperty starttype
# If Variables are more dynamic the rest of the code would be as simple as:
ForEach ($service in $services){
Set-Service -Name $service.serviceName -StartType $service.StartupMode
Start-Service -Name $service.serviceName
Write-Host "The service $service.serviceName has started and it's startup mode is set to $service.StartMode"
}
}
}
}
Example JSON
[
{
"serverName": "Main_SQL_Server",
"startAt" : "2021-10-14 10:00:00",
"services": [
{
"serviceName": "MSSQL$Cluster",
"order": 1,
"startupMode": "manual"
},
{
"serviceName": "MsDtsServer",
"order": 2,
"startupMode": "manual"
},
{
"serviceName": "SQLBrowser$Cluster",
"order": 3,
"startupMode": "manual"
},
{
"serviceName": "SQLAgent$Cluster",
"order": 4,
"startupMode": "automatic"
}
]
},
{
"serverName": "Other_SQL_Server",
"startAt" : "2021-10-14 11:00:00",
"services": [
{
"serviceName": "MSSQL$Backup",
"order": 1,
"startupMode": "manual"
},
{
"serviceName": "MsDtsServer",
"order": 2,
"startupMode": "auto"
},
{
"serviceName": "SQLBrowser$Backup",
"order": 3,
"startupMode": "auto"
},
{
"serviceName": "SQLAgent$Backup",
"order": 4,
"startupMode": "manual"
}
]
}
]
This isn't hard! So you have an array of services for each server, right?
{
"serverName": "Main_SQL_Server",
"startAt" : "2021-10-14 10:00:00",
"services": [
{
"serviceName": "MSSQL$Cluster",
"order": 1,
"startupMode": "manual"
},
{
"serviceName": "MsDtsServer",
"order": 2,
"startupMode": "manual"
},
{
"serviceName": "SQLBrowser$Cluster",
"order": 3,
"startupMode": "manual"
},
{
"serviceName": "SQLAgent$Cluster",
"order": 4,
"startupMode": "automatic"
}
]
}
We can load it like so:
$js = get-content c:\temp\stack.json
We can then pick just a specific server like this:
$server = $js | where serverName -eq Main_SQL_Server
You can then just iterate through the servers using a foreach loop.
forEach ($service in ($server.services | sort order)){
Set-Service -Name $service.ServiceName -StartupType $service.StartupMode
}

Convert Azure DevOps rest api test plans for all projects to csv file using powershell

I am trying to generate report for all the test plans available in azure DevOps organization. here is my script which generates list of projects and the iterate through every project to find test plans available in it. I want all this data to be saved in csv file. I am able to get json file. Is there any way I can get this data saved in csv file with every project iterate for test plan ?
$connectionToken = ""
$BaseUrl = "https://dev.azure.com/{organization_name}/_apis/projects?
api-versions=5.0"
$base64AuthInfo=
[System.Convert]::ToBase64String([System.Text.Encoding]
'::ASCII.GetBytes(":$($connectionToken)"))
$ProjectInfo = Invoke-RestMethod -Uri $BaseUrl -Headers
#{authorization = "Basic $base64AuthInfo"} -
Method Get
$ProjectDetails = $ProjectInfo | ConvertTo-Json -Depth 100
$Projectname = $ProjectInfo.value.name
ForEach ($project in $Projectname){
$TestPlanApi = "https://dev.azure.com/{org}/$project/_apis/test/plans?
api-version=5.0"
$TestplanInfo = Invoke-RestMethod -Uri $TestPlanApi -Headers
#{authorization = "Basic $base64AuthInfo"} -Method Get
if (-NOT ($TestplanInfo.count -eq 0)){
$info = $TestplanInfo | ConvertTo-Json -Depth 100
$info
}
}
This gives me following json file , I want to convert in to csv. Due to every project test plans value starts the Json result with value I am not able to expand it and save to csv
{
"value": [
{
"id": 134,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url":
"https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-
Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl": "mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
}
],
"count": 1
}
{
"value": [
{
"id": 567,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url": "https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl":"mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
},
{
"id": 678,
"name": "sprint1",
"url": "https://dev.azure.com/fabrikam/fabrikam-fiber-
tfvc/_apis/test/Plans/1",
"project": {
"id": "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
"name": "Fabrikam-Fiber-TFVC",
"url": "https://dev.azure.com/fabrikam/_apis/projects/Fabrikam-Fiber-
TFVC"
},
"area": {
"id": "343",
"name": "Fabrikam-Fiber-TFVC"
},
"iteration": "Fabrikam-Fiber-TFVC\\Release 1\\Sprint 1",
"state": "Active",
"rootSuite": {
"id": "1"
},
"clientUrl":
"mtms://fabrikam.visualstudio.com:443/DefaultCollection/p:Fabrikam-
Fiber-TFVC/Testing/testplan/connect?id=1"
}
],
"count": 2
}
These are the values for different projects test runs, some projects have count = 1 then it shows one id , some projects has count =3 then it shows all the 3 ids and so on
I want this json file in csv file with columns -
id , name , url , project.name , project.id , project.url ,area.id , area.name , iteration , owner , revision , state , rootsuite.id , clienturl
How can I expand all the values in csv file ? I tried
Select-object Expand-property value but its fails to to expand all the values in json data
As a workaround, we can save the response body and then convert the json file to csv file.
We cannot save all info in one csv file, the latest info will overwrite the old data, so we need to save the test plan info in different csv files.
Sample:
$connectionToken = "{PAT}"
$BaseUrl = "https://dev.azure.com/{Org}/_apis/projects?api-version=6.1-preview.4"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$ProjectInfo = Invoke-RestMethod -Uri $BaseUrl -Headers #{authorization = "Basic $base64AuthInfo"} -Method Get
$ProjectDetails = $ProjectInfo | ConvertTo-Json -Depth 100
$Projectname = $ProjectInfo.value.name
ForEach ($project in $Projectname){
$TestPlanApi = "https://dev.azure.com/{Org}/$project/_apis/test/plans?api-version=5.0"
$TestplanInfo = Invoke-RestMethod -Uri $TestPlanApi -Headers #{authorization = "Basic $base64AuthInfo"} -Method Get
if (-NOT ($TestplanInfo.count -eq 0)){
#Save the test plan info to json file
$info = $TestplanInfo | ConvertTo-Json -Depth 100 | out-file E:\test\$project.json
#convert the json file to csv file
Get-Content -Path E:\test\$project.json | ConvertFrom-Json | Select-Object -expand value | ConvertTo-Csv -NoTypeInformation | Set-Content E:\test\$project.csv
#delete json file
Remove-Item E:\test\$project.json
}
}
Result:

Only remove/Exclude an attribute from json if it exists

I have following JSON and I would like to remove streets from the JSON object if only it exists under Address which is an array. I am trying to do this in powershell. I can get my script working and remove the streets but I only want to run the exclude line of command if the address has the streets property. Is that possible?
{
"Customer": [{
"id": "123"
}],
"Nationality": [{
"name": "US",
"id": "456"
}],
"address": [{
"$type": "Home",
"name": "Houston",
"streets": [{
"name": "Union",
"postalCode": "10"
}]
},
{
"$type": "Home5",
"name": "Houston5"
},
{
"$type": "Office",
"name": "Hawai",
"streets": [{
"name": "Rock",
"postalCode": "11"
}]
}
]
}
Powershell script
$FileContent = Get-Content -Path "Test.json" -Raw | ConvertFrom-Json
#Only want to run for address objects that contains streets
$FileContent.address = $FileContent.address | Select-Object * -ExcludeProperty streets #Only would like to run if object address has streets property
$FileContent | ConvertTo-Json
Note:
This answer performs the same operation as in the question, only more succinctly, in a single pipeline.
It is benign to run Select-Object * -ExcludeProperty streets against all objects in array address, because the call is an effective no-op for those objects that already lack a streets property (though a copy of such objects is created too).
You need an assignment to modify your objects in-place before outputting them, which requires a ForEach-Object call:
Get-Content -Raw Test.json | ConvertFrom-Json |
ForEach-Object {
[array] $_.address = $_.address | select * -exclude streets; $_
}
Note how each object parsed from the JSON input is first modified via the assignment ($_.address = ...), and then passed out ($_).
A more efficient, but a little more obscure variant:
Get-Content -Raw Test.json | ConvertFrom-Json |
ForEach-Object {
$_.address.ForEach({ $_.psobject.Properties.Remove('streets') }); $_
}
With your sample JSON input, both commands output the following:
Customer Nationality address
-------- ----------- -------
{#{id=123}} {#{name=US; id=456}} {#{$type=Home; name=Houston}, #{$type=Home5; name=Houston5}, #{$type=Office; name=Hawai}}
Note how the objects in the address column no longer have a streets property.
Caveat: Note that ConvertTo-Json limits the serialization depth to 2 by default, which is sufficient in this case, but in other cases you may have to pass a -Depth argument to prevent data loss - see this post.

Parsing JSON array with PowerShell

I'm having issues returning specific data from an API query with PowerShell. I'm making a simple API call to grab a user which works great.
$Response = Invoke-RestMethod -uri $uri -Method GET -Headers $Headers
$Response
The call returns the following.
Total Sum Users Groups
----- ----- ----- ------
1 1 #{123456789=} #{Test Users=System.Object[]; Global Users=System.Object[]...
$Response.Users returns the following
123456789
---------
#{name= Test Case; fullname=Test Tester Case; office=Area 51;....
The issue is the number "123456789" is a unique identifier that changes with every user. In order to pull the needed data from each user I need to be able to parse and grab just that number.
Here is the JSON output from $Response.Users
{
"123456789": {
"name": "Test Case",
"fullname": "Tester Test Case",
"office": "Area 51",
"country": "United States",
"groups": [
"Test Users",
"Global Users",
"Test OU"
]
}
}
Any help is greatly appreciated!
Thanks
You can access the name of the note property using the default powershell property PsObject and retrieve the name of it:
$json =
#'
{
"123456789": {
"name": "Test Case",
"fullname": "Tester Test Case",
"office": "Area 51",
"country": "United States",
"groups": [
"Test Users",
"Global Users",
"Test OU"
]
}
}
'#
$json | ConvertFrom-Json | ForEach-Object {$_.PsObject.Properties.Name}
Or in your case:
$Response.Users | ConvertFrom-Json | ForEach-Object {$_.PsObject.Properties.Name}
Should do the trick ;-)