How to output hash table query result into Out-GridView? - azure

I wish to export a hashtable result into Out-GridView using the Powershell.
The purpose of the below script is to export the Azure VM tags to Out-GridView, it throws error like the below blank result:
Error on the console:
Out-GridView : Syntax error in PropertyPath 'Syntax error in Binding.Path '[ Product] ' ... '(Tag)'.'.
At line:46 char:19
+ $Output | Out-GridView #Export-Csv -Path c:\temp\1a.csv -appe ...
+ ~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Out-GridView], InvalidOperationException
+ FullyQualifiedErrorId : ManagementListInvocationException,Microsoft.PowerShell.Commands.OutGridViewCommand
This is the actual script which was executed under the Global Administrator role:
<#
.AUTHOR: https://stackoverflow.com/users/13390556/lukasz-g
#>
$Subscription = Get-AzSubscription | Out-GridView -Title 'Select subscription' -OutputMode 'Multiple'
# Initialise output array
$Output = #()
if ($Subscription) {
foreach ($item in $Subscription) {
$item | Select-AzSubscription
# Collect all the resources or resource groups (comment one of below)
$Resource = Get-AzResource
#$Resource = Get-AzResourceGroup
# Obtain a unique list of tags for these groups collectively
$UniqueTags = $Resource.Tags.GetEnumerator().Keys | Get-Unique -AsString | Sort-Object | Select-Object -Unique | Where-Object { $_ -notlike "hidden-*" }
# Loop through the resource groups
foreach ($ResourceGroup in $Resource) {
# Create a new ordered hashtable and add the normal properties first.
$RGHashtable = New-Object System.Collections.Specialized.OrderedDictionary
$RGHashtable.Add("Name", $ResourceGroup.ResourceGroupName)
$RGHashtable.Add("Location", $ResourceGroup.Location)
$RGHashtable.Add("Id", $ResourceGroup.ResourceId)
$RGHashtable.Add("ResourceType", $ResourceGroup.ResourceType)
# Loop through possible tags adding the property if there is one, adding it with a hyphen as it's value if it doesn't.
if ($ResourceGroup.Tags.Count -ne 0) {
$UniqueTags | Foreach-Object {
if ($ResourceGroup.Tags[$_]) {
$RGHashtable.Add("[$_] (Tag)", $ResourceGroup.Tags[$_])
}
else {
$RGHashtable.Add("[$_] (Tag)", "-")
}
}
}
else {
$UniqueTags | Foreach-Object { $RGHashtable.Add("[$_] (Tag)", "-") }
}
# Update the output array, adding the ordered hashtable we have created for the ResourceGroup details.
$Output += New-Object psobject -Property $RGHashtable
}
# Sent the final output to CSV
$Output | Out-GridView #Export-Csv -Path c:\temp\1a.csv -append -NoClobber -NoTypeInformation -Encoding UTF8 -Force
}
}

$RGHashtable.Add("[$_] (Tag)"
In above code, You are trying to add something like below :
In the output
Removed everthing and I tested with simple statements
$Output = #()
$RGHashtable = New-Object System.Collections.Specialized.OrderedDictionary
$RGHashtable.Add("[Testing] (Name)", "Temporary")
$Output += New-Object psobject -Property $RGHashtable
$Output | Out-GridView
I was provided with the same error.
After couple of testing, understood the error only occurs when there is a combination "[SomeString](SomeString)" --- [...](....) in the string.
The Out-GridView is trying to parse the "[<SomeString>](<SomeString>)" and hence the error.
You could try any 1 of the below combination in your code :
$RGHashtable.Add("[$_] [Tag]", $ResourceGroup.Tags[$_])
OR
$RGHashtable.Add("{$_} (Tag)", $ResourceGroup.Tags[$_])
OR
$RGHashtable.Add("[$_] [Tag]", $ResourceGroup.Tags[$_])
This should resolve your issue.
you will have change in 3 instances in your code if I am not wrong.

Related

PowerShell: Output provider results from multiple Azure subscriptions into one .txt file - via arrayList

I am trying to get the registered providers for multiple subscriptions and output everything into one file.
For that, i am getting the subscriptions from a folder full of *.yaml files that contain information about the subscriptions, including their name.
What i cannot achieve is get the result for each subscriptions into an array and output that array into a text file. The script also allows the use of only one item from the folder in case that is the case.
Here is a sample code of a subscription and the code for it:
subscription1.yaml
name: subscription1
emailContact: email.address#domain.com
tags:
- key: "key1"
value: "Value1"
subscription2.yaml
name: subscription2
emailContact: email.address#domain.com
tags:
- key: "key1"
value: "Value1"
Folder structure where the yaml files is: ./landingZones/landingzone1/settings/dev/*.yaml
script:
param (
[Parameter(Mandatory = $false)]
[string]$Environment = 'dev',
[Parameter(Mandatory = $false)]
[string]$LandingZoneType = 'landingzone1',
[Parameter(Mandatory = $false)]
[string]$SingleSubscription
)
$scriptPath = Split-Path -parent $PSCommandPath
$subscriptionsEnvironmentDirectory = Get-ChildItem -Directory $scriptPath -Recurse -Filter "*$Environment*" | Where-Object { $_.parent.parent.Name -eq $LandingZoneType }
$subscriptions = Get-ChildItem -Path $($subscriptionsEnvironmentDirectory.FullName)
foreach ($subscription in ($subscriptions | Where-Object { ([System.String]::IsNullOrEmpty($SingleSubscription)) -or ($_.Name -replace "\.[^\.]+$", '') -eq $SingleSubscription })) {
$landingZone = Get-Content -Path $subscription.FullName | ConvertFrom-Yaml
# Set subscriptionName variable
$subscriptionName = $landingZone.name
$providers = az provider list --subscription $subscriptionName | ConvertFrom-Json
$defaultRegisteredProviders = 'Microsoft.ADHybridHealthService|Microsoft.Authorization|Microsoft.Billing|Microsoft.ClassicSubscription|Microsoft.Commerce|Microsoft.Consumption|Microsoft.CostManagement|Microsoft.Features|Microsoft.MarketplaceOrdering'
$registeredProviders = $providers | Where-Object { ($_.registrationState -eq 'Registered') -and ($_.namespace -notmatch $defaultRegisteredProviders) }
# Outputting result into txt file in the same directory where the command was executed
Write-Host ('{1}# Registered providers for subscription [{0}]' -f $subscriptionName, "`n")
$list = New-Object -TypeName 'System.Collections.ArrayList'
$sortedObjects = $registeredProviders | Sort-Object namespace | `
Format-Table `
#{l = 'Namespace'; e = { $_.namespace } }, `
#{l = "Subscription Id [$subscriptionName]"; e = { $_.id } }, `
#{l = 'Registration State'; e = { $_.registrationState } }, `
#{l = 'Registration Policy'; e = { $_.registrationPolicy } }
foreach ($i in $sortedObjects) {
$list.Add($i) | Out-Null
}
# Alternative to add into array:
# #($sortedObjects).foreach({$list.Add($_)}) | Out-Null
}
$list.Count
$list | Out-File .\registered_providers.txt -Force
The result is a file called 'registered_providers.txt' that contains only the registered providers for the first subscription in the foreach loop. I cannot get the contents of the second, third and so on in the same file, just a replaced text from the $sortedObjects
How do i create the array to contain all the info from all the subscriptions called?
Thanks
$list | Out-File Should be inside the for loop so that all the data you are fetching will be stored in the specified file.
Out-File cmdlet has -append parameter that appends the output to the existing file for every for loop happens. Otherwise, it will clean up the information/value stored in that output file.
Thanks to #jdweng for pointing the user to the right solution.
Refer to this MS Doc on Out-File -Append Parameter usage.

Powershell 7 - Get-AzureADUser, AssignedLicenses, Convert Class to Object

When I retieve an AzureADUser while using the AzureAD module (imported with -UseWindowsPowershell), and trying to retrieve the property for AssignedLicenses, they return as a Class instead of an obect (e.g. just a string?)
class AssignedLicense {
DisabledPlans: System.Collections.Generic.List`1[System.String]
SkuId: zzzzzzzz-2c81-4ef7-yyyy-5b5392b571df}
class AssignedLicense {
DisabledPlans: System.Collections.Generic.List`1[System.String]
SkuId: zzzzzzz-fae0-4ca2-xxxx-7a907fd6c235
}
How would I convert such a thing to something I can actually use (e.g. an object?)
At first I thought it was JSON, but the ConvertFrom-JSON cmdlet gave errors on the notation, as far as I am aware there is no ConvertFrom-Class included in normal powershell :)
Did also try to just remove all the text and keep anything after SkuID, but that proves a challenge when the DisabledPlans is bigger/smaller than I expect.
Output received by running these commands:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned = ($u | Select-Object AssignedLicenses).AssignedLicenses
}
I tried to reproduce the scenario in my environment:
I tried the same script as yours:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned =($u | Select-Object AssignedLicenses ).AssignedLicenses
$Assigned
}
Getting expected result :
Alternately I tried with below commands:
$users = Get-AzureADUser -filter "AccountEnabled eq true"
$users | ForEach-Object {
foreach($user in $_.AssignedLicenses){
New-object -typename PSobject -property #{
ID = $_.Id
DisplayName = $_.DisplayName
DisabledPlans = $user.DisabledPlans
SkuId = $user.SkuId
}
}} | Sort-Object ID, DisplayName, DisabledPlans, SkuId | Export-Csv -Path C:\Assignedlicense2.csv -NoTypeInformation
I am getting the same result type for disabled plans System.Collections.Generic.List'1[System.String] as below image:
You can try the second way and check from your end or adding -join parameter to each object .
Reference: powershell - How to convert "System.Collections.Generic.List`1[System.String]" into readable format Using Get-AzureADMSConditionalAccessPolicy cmdlet - Stack Overflow
Or directly use:
$userList = Get-AzureADUser -filter "AccountEnabled eq true"
foreach ($u in $userList) {
$Assigned =$u.AssignedLicenses
$Assigned
}

In Azure, To get Load balancer details in to csv file using powershell script

I wrote a powershell script in Azure DevOps pipeline to get Load Balancer details like FrontendIPConfigurationsName,FrontendIPAddress in to csv file. AM getting those details but FrontendIPConfigurationsNames which starts with same name like "ers-A1,ers-B1,ers-C1,ers-D1" are coming in same row. But I want to get them in different rows.Please suggest
$excel = #()
LBlist = Get-AZLoadBalancer | Where-Oject {$_.ResourceGroupName -clike '$(grp-wildcard)'} | Select-Object
foreach ($LB in LBlist)
$Array =""| Select-Object ResourceGroupName, FrontendIPConfigurationsName,FrontendIPAddress
$Array.ResourceGroupName =$LB.ResourcegroupName
$Array.FrontendIPConfigurationsName = ($LB.FrontendIpConfigurationsName.name -join ',')
$Array.FrontendIPAddress =($LB.FrontendIpConfigurations.PrivateIpAddress -join ',')
}
$excel +=$Array
$excel |Format-Table ResourceGroupName, FrontendIPConfigurationsName,FrontendIPAddress
$excel | Export-Csv -NTI -Path "($Build.ArtifactStagingDirectory)/LBlist.csv
You can do something similar to this to get the objects on separate rows.
$LBlist = Get-AZLoadBalancer | Where-Object { $_.ResourceGroupName -clike '$(grp-wildcard)' }
$LBlist | Export-Csv -NTI -Path "$(Build.ArtifactStagingDirectory)/LBlist.csv"
Mock for Get-AZLoadBalancer (not sure if it's entirely accurate as I've never used Az Powershell)
function Get-AZLoadBalancer() {
$obj = #(
[PSCustomObject] #{
ResourceGroupName = '$(grp-wildcard)'
FrontendIPConfigurationsName = 'ers-A1'
FrontEndIPAddress = '1.2.3.4'
},
[PSCustomObject] #{
ResourceGroupName = '$(grp-wildcard)'
FrontendIPConfigurationsName = 'ers-B1'
FrontEndIPAddress = '1.2.3.5'
}
)
return $obj
}

Excel report Formatting in PowerShell

Need help to create a script to get a HPOA server blade Health report
The problem is that when I get query Health it outputs in a PSO object with fields (IP,Health,Blades(#{Blade1 Health}{Blade2 Health}{3} . . .) )
I want a report like below
IP Bay Power Health
-- --- ----- -----
10.3.131.2 1 On OK
2 On OK
3 On OK
4 On OK
5 On Degraded
The variables are derived as below .
$sstaInfo = {} | Select IP, Bay, Power, Health, DeviceFailure
$sstaInfo.IP=$ssta.IP (Gives a single IP output)
$sstaInfo.Bay=$sstaBlades.Bay $sstaInfo.Power=$sstaBlades.Power
$sstaInfo.Health=$sstaBlades.Health
How can I get this working ?
$ssta variable has the below output :
#{Power=On; CurrentWattageUsed=480; Health=OK; UnitIdentificationLED=Off; VirtualFan=33%; DiagnosticStatus=; Bay=1} #{Power=On; CurrentWattageUsed=576; Health=OK; UnitIdentificationLED=Off; VirtualFan=47%; DiagnosticStatus=; Bay=2}
#------------------------------------------------------------ Input Variable Definations
$HPOAServers =#(
[pscustomobject]#{Name='10.11.12.13'},
[pscustomobject]#{Name='10.11.12.14'}
)
$Username ="admin"
$Password ="admin"
#------------------------------------------------------------ Main Script Starts Here
# Function for connecting to OA and returning connection object on success
foreach ($HPOAServer in $HPOAServers) {
$con = Connect-HPOA $HPOAServer.Name -username $Username -password $Password
$report = #()
$ssta = Get-HPOAServerStatus -Bay All $con
$sstaBlade=$ssta.Blade
Write-Host $sstaBlade
Foreach ($sstaBlades in $sstaBlade) {
$i++
$sstaInfo = {} | Select IP, Bay, Power, Health, DeviceFailure
$sstaInfo.IP=$ssta.IP
$sstaInfo.Bay=$sstaBlades.Bay
$sstaInfo.Power=$sstaBlades.Power
$sstaInfo.Health=$sstaBlades.Health
$sstaInfo.DeviceFailure=$ssta.Blade.DiagnosticStatus.DeviceFailure
}
$report += $ssta | Select-Object -Property IP
$report += $ssta.Blade | Select-Object -Property Bay, Power, Health | Format-Table *
$report | out-file "HPOA_Health_Report.txt" -Append
}
Disconnect-HPOA $con
I suggest you use Export-CSV instead, so below line
$report | Out-File "HPOA_Health_Report.txt" -Append
will be replaced by:
$report | Export-Csv "HPOA_Health_Report.csv" -Append -NoTypeInformation
Function HPOA () {
try
{
Remove-Item -Path $outputfile -Force
foreach ($HPOAServer in $HPOAServers)
{
$con = Connect-HPOA $HPOAServer.Name -username $Username -password $Password -ErrorAction 'Stop'
$ssta = Get-HPOAServerStatus -Bay All $con
$ssta.Blade | Foreach-Object {
$sstaInfo = $_
$sstaInfo | Select-Object -Property #{Name="Chassis_IP_Address";Expression={$ssta.IP}},
#{Name="Blade_Power_Status";Expression={$_.Power}},
#{Name="Blade_Bay_Number";Expression={$_.Bay}},
#{Name="Blade_Health_Status";Expression={$_.Health}},
#{Name="Blade_Diagnostic_DeviceFailure_Status";Expression={$ssta.Blade.DiagnosticStatus.DeviceFailure}}
} | ConvertTo-Html -Title " $HPOARegionName HPOA Health Report " -Head $Header -Body "<H2> $HPOARegionName HPOA Health Report </H2>" -As Table | Out-File -Append $outputfile
Disconnect-HPOA $con
}
}
catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Write-Host $ErrorMessage
Write-Host $FailedItem
}
}
HPOA

Display all sites and bindings in PowerShell

I am documenting all the sites and binding related to the site from the IIS. Is there an easy way to get this list through a PowerShell script rather than manually typing looking at IIS?
I want the output to be something like this:
Site Bindings
TestSite www.hello.com
www.test.com
JonDoeSite www.johndoe.site
Try this:
Import-Module Webadministration
Get-ChildItem -Path IIS:\Sites
It should return something that looks like this:
Name ID State Physical Path Bindings
---- -- ----- ------------- --------
ChristophersWeb 22 Started C:\temp http *:8080:ChristophersWebsite.ChDom.com
From here you can refine results, but be careful. A pipe to the select statement will not give you what you need. Based on your requirements I would build a custom object or hashtable.
Try something like this to get the format you wanted:
Get-WebBinding | % {
$name = $_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1'
New-Object psobject -Property #{
Name = $name
Binding = $_.bindinginformation.Split(":")[-1]
}
} | Group-Object -Property Name |
Format-Table Name, #{n="Bindings";e={$_.Group.Binding -join "`n"}} -Wrap
If you just want to list all the sites (ie. to find a binding)
Change the working directory to "C:\Windows\system32\inetsrv"
cd c:\Windows\system32\inetsrv
Next run "appcmd list sites" (plural) and output to a file. e.g c:\IISSiteBindings.txt
appcmd list sites > c:\IISSiteBindings.txt
Now open with notepad from your command prompt.
notepad c:\IISSiteBindings.txt
The most easy way as I saw:
Foreach ($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]#{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation}}}
Try this
function DisplayLocalSites
{
try{
Set-ExecutionPolicy unrestricted
$list = #()
foreach ($webapp in get-childitem IIS:\Sites\)
{
$name = "IIS:\Sites\" + $webapp.name
$item = #{}
$item.WebAppName = $webapp.name
foreach($Bind in $webapp.Bindings.collection)
{
$item.SiteUrl = $Bind.Protocol +'://'+ $Bind.BindingInformation.Split(":")[-1]
}
$obj = New-Object PSObject -Property $item
$list += $obj
}
$list | Format-Table -a -Property "WebAppName","SiteUrl"
$list | Out-File -filepath C:\websites.txt
Set-ExecutionPolicy restricted
}
catch
{
$ExceptionMessage = "Error in Line: " + $_.Exception.Line + ". " + $_.Exception.GetType().FullName + ": " + $_.Exception.Message + " Stacktrace: " + $_.Exception.StackTrace
$ExceptionMessage
}
}
function Get-ADDWebBindings {
param([string]$Name="*",[switch]$http,[switch]$https)
try {
if (-not (Get-Module WebAdministration)) { Import-Module WebAdministration }
Get-WebBinding | ForEach-Object { $_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1' } | Sort | Get-Unique | Where-Object {$_ -like $Name} | ForEach-Object {
$n=$_
Get-WebBinding | Where-Object { ($_.ItemXPath -replace '(?:.*?)name=''([^'']*)(?:.*)', '$1') -like $n } | ForEach-Object {
if ($http -or $https) {
if ( ($http -and ($_.protocol -like "http")) -or ($https -and ($_.protocol -like "https")) ) {
New-Object psobject -Property #{Name = $n;Protocol=$_.protocol;Binding = $_.bindinginformation}
}
} else {
New-Object psobject -Property #{Name = $n;Protocol=$_.protocol;Binding = $_.bindinginformation}
}
}
}
}
catch {
$false
}
}
I found this page because I needed to migrate a site with many many bindings to a new server. I used some of the code here to generate the powershell script below to add the bindings to the new server. Sharing in case it is useful to someone else:
Import-Module WebAdministration
$Websites = Get-ChildItem IIS:\Sites
$site = $Websites | Where-object { $_.Name -eq 'site-name-in-iis-here' }
$Binding = $Site.bindings
[string]$BindingInfo = $Binding.Collection
[string[]]$Bindings = $BindingInfo.Split(" ")
$i = 0
$header = ""
Do{
[string[]]$Bindings2 = $Bindings[($i+1)].Split(":")
Write-Output ("New-WebBinding -Name `"site-name-in-iis-here`" -IPAddress " + $Bindings2[0] + " -Port " + $Bindings2[1] + " -HostHeader `"" + $Bindings2[2] + "`"")
$i=$i+2
} while ($i -lt ($bindings.count))
It generates records that look like this:
New-WebBinding -Name "site-name-in-iis-here" -IPAddress "*" -Port 80 -HostHeader www.aaa.com
I found this question because I wanted to generate a web page with links to all the websites running on my IIS instance. I used Alexander Shapkin's answer to come up with the following to generate a bunch of links.
$hostname = "localhost"
Foreach ($Site in get-website) {
Foreach ($Bind in $Site.bindings.collection) {
$data = [PSCustomObject]#{
name=$Site.name;
Protocol=$Bind.Protocol;
Bindings=$Bind.BindingInformation
}
$data.Bindings = $data.Bindings -replace '(:$)', ''
$html = "" + $data.name + ""
$html.Replace("*", $hostname);
}
}
Then I paste the results into this hastily written HTML:
<html>
<style>
a { display: block; }
</style>
{paste PowerShell results here}
</body>
</html>

Resources