Display all sites and bindings in PowerShell - iis

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>

Related

Add exceptions for file paths from azure defender to adaptive application security controls

I have a bunch of machines being monitored by adaptive application security controls that are giving warnings because the training process was not ran long enough to recognize benign executables. What's an easy way to add exceptions for the executables in active alerts to the adaptive security groups?
There's already an existing recommendation that might provide what you are trying to do:
https://learn.microsoft.com/en-us/azure/defender-for-cloud/adaptive-application-controls#respond-to-the-allowlist-rules-in-your-adaptive-application-control-policy-should-be-updated-recommendation
This script grabs the active alerts from defender, and updates the groups.
The alerts must still be dismissed manually.
function Get-ExistingRules {
Param(
$subscriptionId,
$groupName
)
$url = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Security/locations/centralus/applicationWhitelistings/${groupName}?api-version=2015-06-01-preview";
return az rest `
--method get `
--url $url `
| ConvertFrom-Json;
}
function Add-NewRules {
Param(
$subscriptionId,
$groupName,
$files
)
$url = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.Security/locations/centralus/applicationWhitelistings/${groupName}?api-version=2015-06-01-preview";
$existing = Get-ExistingRules $subscriptionId $groupName;
$existing | ConvertTo-Json -Depth 100 > asd.json;
$myList = $existing.properties.pathRecommendations;
foreach ($file in $files) {
$myList += [pscustomobject]#{
path = $file.path
type = "File"
common = $true
action = "Add"
usernames = #(
[pscustomobject]#{
username = "Everyone"
recommendationAction = "Recommended"
}
)
userSids = #(
"S-1-1-0"
)
fileType = $file.type
configurationStatus = "NotConfigured"
};
}
$existing.properties.pathRecommendations = $myList;
$existing.properties = [pscustomobject]#{
protectionMode = $existing.properties.protectionMode
vmRecommendations = $existing.properties.vmRecommendations
pathRecommendations = $existing.properties.pathRecommendations
}
$existing.PSObject.properties.remove("location");
# return $existing;
$body = $existing | ConvertTo-Json -Depth 20 -Compress;
$body > temp.json;
# $body = $body -replace "`"", "\`"";
# return az rest `
# --method PUT `
# --url $url `
# --body $body `
# | ConvertFrom-Json;
# avoid max command length limit by storing body in a file
try {
return az rest `
--method PUT `
--url $url `
--body `#temp.json `
| ConvertFrom-Json;
}
catch {
Write-Warning "Encountered error adding rule";
Write-Warning "$_";
}
return $null;
}
function Format-Body {
param(
$obj
)
$body = $obj | ConvertTo-Json -Depth 100 -Compress;
$body = $body -replace "`"", "\`"";
$body = $body -replace "`r", "";
return $body;
}
Write-Host "Listing subscriptions";
# you can filter to just one subscription if you want
# $subscriptions = az account list --query "[?name=='NPRD'].id" --output tsv;
$subscriptions = az account list --query "[].id" --output tsv;
$allAlerts = New-Object System.Collections.ArrayList;
$i = 0;
foreach ($sub in $subscriptions) {
Write-Progress -Id 0 -Activity "Fetching alerts" -Status $sub -PercentComplete ($i / $subscriptions.Count * 100);
$i = $i++;
$alerts = az security alert list `
--subscription $sub `
| ConvertFrom-Json `
| Where-Object { #("VM_AdaptiveApplicationControlLinuxViolationAudited", "VM_AdaptiveApplicationControlWindowsViolationAudited") -contains $_.alertType } `
| Where-Object { $_.status -eq "Active" };
foreach ($x in $alerts) {
$allAlerts.Add($x) > $null;
}
}
Write-Progress -Id 0 "Done" -Completed;
function Get-Files {
Param(
$alert
)
if ($alert.alertType -eq "VM_AdaptiveApplicationControlLinuxViolationAudited") {
$fileType = "executable";
}
else {
$fileType = "exe";
}
$pattern = "Path: (.*?);";
$str = $alert.extendedProperties.file;
return $str `
| Select-String -Pattern $pattern -AllMatches `
| ForEach-Object { $_.Matches } `
| ForEach-Object { $_.Value } `
| ForEach-Object { [pscustomobject]#{
path = $_
type = $fileType
}};
}
$alertGroups = $allAlerts | Select-Object *, #{Name = "groupName"; Expression = { $_.extendedProperties.groupName } } | Group-Object groupName;
foreach ($group in $alertGroups) {
$groupName = $group.Name;
$group.Group[0].id -match "/subscriptions/([^/]+)/" > $null;
$subscriptionId = $matches[1];
$files = $group.Group | ForEach-Object { Get-Files $_ };
Write-Host "Adding file path rule sub=$subscriptionId group=$groupName count=$($files.Count)";
Add-NewRules $subscriptionId $groupName $files;
}

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

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.

Can I get the Automatic Daylight Saving Time info from a VM through powershell?

I need to get a CSV of VMs that have the "Adjust for daylight saving time automatically?" set as off.
I think I can't create a script that search all the VMs in my subscription at once, since the information about the DST is not in the Azure portal. So maybe can I run something on each VM to get the expected result?
Is there any way to get this information using powershell at all?
Thanks!
Here's a handy script for Windows VMs: https://devblogs.microsoft.com/scripting/check-for-daylight-savings-time-by-using-powershell/
I cannot test this myself, but I think you only have to see if the DaylightBias property returned by Get-CimInstance -ClassName Win32_TimeZone is anything other than 0
Get-VM | ForEach-Object {
if ($_.State -eq 'Running') {
$tz = Get-CimInstance -ClassName Win32_TimeZone -ComputerName $_.Name
$dst = if (!$tz.DaylightBias) { 'Off' } else { 'On' }
}
else {
$dst = 'Unknown'
}
[PsCustomObject]#{
'Computer' = $_.Name
'Status' = $_.State
'DaylightSavingTime' = $dst
}
} | Export-Csv -Path 'D:\VM-DstInfo.csv' -NoTypeInformation
Or perhaps execute cmdlet Get-TimeZone on the VM will get you the info you need:
Get-VM | ForEach-Object {
if ($_.State -eq 'Running') {
$tz = Invoke-Command -ComputerName $_.Name -ScriptBlock {Get-TimeZone}
$dst = if (!$tz.SupportsDaylightSavingTime) { 'Off' } else { 'On' }
}
else {
$dst = 'Unknown'
}
[PsCustomObject]#{
'Computer' = $_.Name
'Status' = $_.State
'DaylightSavingTime' = $dst
}
} | Export-Csv -Path 'D:\VM-DstInfo.csv' -NoTypeInformation

Splitting output of a string into separate strings

I've been working on a powershell script and it's been really boggling my mind. There are 2 parts to the script.
First part is a function that gets all servers in a domain. We have 4 different domains, so I check each one individually and output the result.
Second part is a function that outputs the software on a specific remote machine. In my case, the output from the function above will be seeded into this function to see if a server has a particular piece of software installed.
The function that searches the software works properly. The function that I am getting an output of all the servers is what I am having trouble with.
The issue is, that when I output the list of servers (the output is correct), it outputs everything into a single large multiline string...
For example lets say I have 5 servers: (ServerA, ServerB, ServerC, ServerD, ServerE).
When I run the code I will get an output of all the servers for each domain like so:
TestA.com
ServerA
ServerB
ServerC
ServerD
ServerE
TestB.com
ServerA
ServerB
ServerC
ServerD
ServerE
TestC.com
ServerA
ServerB
ServerC
ServerD
ServerE
TestD.com
ServerA
ServerB
ServerC
ServerD
ServerE
However each domain output is all 1 string, so I can't seed it into the function to check software because it's trying to find it in "ServerA,ServerB,ServerC,ServerD,ServerE", instead of each server individually.
I hope this makes sense. Here is my code to get the list of servers.
#Clear Screen
CLS
function Get-Servers
{
#Variables
[array]$MyDomains="TestA.com","TestB.com","TestC.com","TestD.com"
[array]$MySearchBase="dc=TestA,dc=com","dc=TestB,dc=com","dc=TestC,dc=com","dc=TestD,dc=com"
for($i=0; $i -lt $MyDomains.Count; $i++)
{
Write-Output $($MyDomains[$i])
$MyServers = Get-ADComputer -Filter 'OperatingSystem -like "Windows*Server*"' -Properties Name -SearchBase $($MySearchBase[$i]) -Server $($MyDomains[$i]) | Format-Table Name -HideTableHeaders | out-string
foreach ($MyServer in $MyServers)
{
$MyServer
pause
}
}
}
#Get list of servers
Get-Servers
How can I get the output for each server individually to be stored in the "$MyServer" variable?
EDIT:
Here is my function to find remote software
function Get-RemoteRegistryProgram
{
<#
.Synopsis
Uses remote registry to read installed programs
.DESCRIPTION
Use dot net and the registry key class to query installed programs from a
remote machine
.EXAMPLE
Get-RemoteRegistryProgram -ComputerName Server1
#>
[CmdletBinding()]
Param
(
[Parameter(
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]
$ComputerName = $env:COMPUTERNAME
)
begin
{
$hives = #(
[Microsoft.Win32.RegistryHive]::LocalMachine,
[Microsoft.Win32.RegistryHive]::CurrentUser
)
$nodes = #(
"Software\Microsoft\Windows\CurrentVersion\Uninstall",
"Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)
}
process
{
$ComputerName
forEach ($computer in $ComputerName)
{
forEach($hive in $hives)
{
try
{
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hive,$computer)
}
catch
{
throw $PsItem
}
forEach($node in $nodes)
{
try
{
$keys = $registry.OpenSubKey($node).GetSubKeyNames()
forEach($key in $keys)
{
$displayname = $registry.OpenSubKey($node).OpenSubKey($key).GetValue('DisplayName')
if($displayname)
{
$installedProgram = #{
# ComputerName = $computer
DisplayName = $displayname
# Version = $registry.OpenSubKey($node).OpenSubKey($key).GetValue('DisplayVersion')
}
New-Object -TypeName PSObject -Property $installedProgram
}
}
}
catch
{
$orginalError = $PsItem
Switch($orginalError.FullyQualifiedErrorId)
{
'InvokeMethodOnNull'
{
#key maynot exists
}
default
{
throw $orginalError
}
}
}
}
}
}
}
end
{
}
}
EDIT 2:
If I modify my server function like so:
for($i=0; $i -lt $MyDomains.Count; $i++)
{
Write-Output $($MyDomains[$i])
$MyServers = Get-ADComputer -Filter 'OperatingSystem -like "Windows*Server*"' -Properties Name -SearchBase $($MySearchBase[$i]) -Server $($MyDomains[$i]) | Format-Table Name -HideTableHeaders
foreach ($MyServer in $MyServers)
{
Get-RemoteRegistryProgram -ComputerName $MyServer
}
}
I get the following error:
Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found.
"
At line:47 char:21
+ $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IOException
Thank you in advance for any help!
Your code is converting the server names to a string
$MyServers = Get-ADComputer -Filter 'OperatingSystem -like "Windows*Server*"' -Properties Name -SearchBase $($MySearchBase[$i]) -Server $($MyDomains[$i]) | Format-Table Name -HideTableHeaders | out-string
The last part of that is out-string. Instead of piping to a format table and pushing it out as a string, keep the objects and use the properties in each object to get the names of each server.
I ended up rewriting some things and fixing my issue. To avoid the string issue, I export the results to a text file and then using get-content I read line by line from the text file and seeded each server to let me know which servers have the software I need. Here is the end result.
#Clear Screen
CLS
function Get-RemoteRegistryProgram
{
<#
.Synopsis
Uses remote registry to read installed programs
.DESCRIPTION
Use dot net and the registry key class to query installed programs from a
remote machine
.EXAMPLE
Get-RemoteRegistryProgram -ComputerName Server1
#>
[CmdletBinding()]
Param
(
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0)][string]$ComputerName = $env:COMPUTERNAME,
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=1)][string]$SoftwareName
)
begin
{
$hives = #(
[Microsoft.Win32.RegistryHive]::LocalMachine,
[Microsoft.Win32.RegistryHive]::CurrentUser
)
$nodes = #(
"Software\Microsoft\Windows\CurrentVersion\Uninstall",
"Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
)
}
process
{
$ComputerName
$skip = $false
forEach ($computer in $ComputerName)
{
forEach($hive in $hives)
{
try
{
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hive,$computer)
}
catch
{
$skip = $true
}
if($skip -eq $false)
{
forEach($node in $nodes)
{
try
{
$keys = $registry.OpenSubKey($node).GetSubKeyNames()
forEach($key in $keys)
{
$displayname = $registry.OpenSubKey($node).OpenSubKey($key).GetValue('DisplayName')
#Modified by James
if(($displayname) -like "*$SoftwareName*")
{
$displayname + "`t" + $computer >> c:\scripts\sysaidServers.txt
}
<# Modified by James
if($displayname)
{
$installedProgram = #{
# ComputerName = $computer
DisplayName = $displayname
# Version = $registry.OpenSubKey($node).OpenSubKey($key).GetValue('DisplayVersion')
}
New-Object -TypeName PSObject -Property $installedProgram
}
#>
}
}
catch
{
<#
$orginalError = $PsItem
Switch($orginalError.FullyQualifiedErrorId)
{
'InvokeMethodOnNull'
{
#key maynot exists
}
default
{
throw $orginalError
}
}
#>
}
}
}
}
}
}
end
{
}
}
#Output the servers to a txt file
function Get-Servers
{
param ([Parameter( Mandatory=$true)][string]$SaveFile)
#Variables
[array]$MyDomains="DomainA.com","DomainB.com","DomainC.com","DomainD.com"
[array]$MySearchBase="dc=DomainA,dc=com","dc=DomainB,dc=com","dc=DomainC,dc=com","dc=DomainD,dc=com"
for($i=0; $i -lt $MyDomains.Count; $i++)
{
#I only want servers running Windows Server OS
$MyServers = Get-ADComputer -Filter 'OperatingSystem -like "Windows*Server*"' -Properties Name -SearchBase $($MySearchBase[$i]) -Server $($MyDomains[$i]) | Format-Table Name -HideTableHeaders | out-string
#Remove all whitespace and export to txt file
$MyServers.Trim() -replace (' ', '') >> $SaveFile
}
}
function CheckServerSoftware
{
param ([Parameter( Mandatory=$true)][string]$SaveFile)
Get-Content $SaveFile | ForEach-Object {
if($_ -match $regex)
{
$computer = $_.ToString()
Get-RemoteRegistryProgram -ComputerName $computer.Trim() $SoftwareName
Write-Output ""
}
}
}
#Path to where our exported server list is
$SaveFile = "c:\scripts\servers.txt"
$SoftwareName = "SysAid"
#If the file already exists, remove it
Remove-Item $SaveFile
#Create the text file with servers
Get-Servers $SaveFile
#Import our server list and check software on each server
CheckServerSoftware $SaveFile

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

Resources