powershell unable to add ips into a security group via array - arrays

Having issues getting this script running. Err. Cannot index into a null array
any ideas would be a great help. I've looked at verbose logging but I'm not sure how to output compute methods to find the contents. Obviously it appears to be empty but for investigation purposes at least it would be a start.
$rgname = "xxxxxx"
$subscriptionname = "xxxxxx"
$vmname = "xxxxxx"
# Get the VM we need to configure
$vm = Get-AzureRmVM -ResourceGroupName $rgname -Name $vmname
Write-host "$vm"
# Get the name of the first NIC in the VM
$nic = Get-AzureRmNetworkInterface -ResourceGroupName $rgname -Name (Get-AzureRmResource -ResourceId $vm.NetworkInterfaceIDs[0]).ResourceName
$nsg = Get-AzureRmNetworkSecurityGroup -ResourceGroupName $rgname -Name (Get-AzureRmResource -ResourceId $nic.NetworkSecurityGroup.Id).Name
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4",ipname5"),
("ip1,"ip2","ip3","ip4","ip5"))
#LOOP THE ARRAY AND SET DESCRIPTION AND IP VARIABLE FOR COMMAND
$priority = 1010
for ($i=0;$i -lt $nameAndIPArray[0].length; $i++) {
$nameAndIPArray[0][$i] + " " + $nameAndIPArray[1][$i]
$nsg | Add-AzureRmNetworkSecurityRuleConfig -Name $nameAndIPArray[0][$i] -Description $nameAndIPArray[0][$i] -Access Allow -Protocol Tcp -Direction Inbound -Priority $priority -SourceAddressPrefix $nameAndIPArray[1][$i] -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443
Set-AzureRmNetworkSecurityGroup -NetworkSecurityGroup $nsg
$priority = $priority + 10
}
Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine
Cannot index into a null array.
At line:14 char:1
Get-AzureRmResource : Cannot validate argument on parameter 'ResourceId'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
Add-AzureRmNetworkSecurityRuleConfig : Cannot bind argument to parameter 'NetworkSecurityGroup' because it is null.
At line:28 char:12
Set-AzureRmNetworkSecurityGroup : Cannot bind argument to parameter 'NetworkSecurityGroup' because it is null.
At line:29 char:59

I test in my lab, there are some mistakes in your script. Use your script, I could not get $nic and $nsg value.$vm does not have the attribute NetworkInterfaceIDs[0], so you could not use like this. The line $nameAndIPArray loses ". The correct usage should be like below:
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4","ipname5"),
("ip1","ip2","ip3","ip4","ip5"))
I modify your script, I get $nsg by using resource group name and nsg name. You could find them on Portal, it works for me.
$nsg= Get-AzureRmNetworkSecurityGroup -ResourceGroupName <resource group name> -Name "<NSG name>"
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4","ipname5"),
("10.0.0.4","10.0.0.5","10.0.0.6","10.0.0.7","10.0.0.8"))
$priority = 1010
for ($i=0;$i -lt $nameAndIPArray[0].length; $i++) {
$nameAndIPArray[0][$i] + " " + $nameAndIPArray[1][$i]
$nsg | Add-AzureRmNetworkSecurityRuleConfig -Name $nameAndIPArray[0][$i] -Description $nameAndIPArray[0][$i] -Access Allow -Protocol Tcp -Direction Inbound -Priority $priority -SourceAddressPrefix $nameAndIPArray[1][$i] -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443
Set-AzureRmNetworkSecurityGroup -NetworkSecurityGroup $nsg
$priority = $priority + 10
}
Replace correct value to your script.

I assume this is the line that is empty, so you are not getting any vms back:
$vm = Get-AzureRmVM -ResourceGroupName $rgname -Name $vmname
so, check the $vm variable and if some vm's exist with those parameters.

Related

Powershell-Azure WebApp IpRestrictions - WebApps Array

I have been struggling to come up with a working solution for days on this
What am I trying to achieve?
Foreach ($item in $webApps){
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $item -ResourceGroupName $resourceGroup -ApiVersion $APIVersion)
}
The issue is that "-resourceName" will not accept objects, but rather only a string
I am looking for a way to take the output of the following command, convert it to a string, so that it can satisfy –ResourceName, and loop through each item in the string
$webApps = (Get-AzureRmResourceGroup -Name $resourceGroup | Get-AzureRmWebApp).name
This returns a nice list of Azure WebApps that exist in a specified ResourceGroup, however they are in object form, which –ResourceName will not take
I have tried several ways to convert the output of $webApps to a string, add a comma to the end, then do a –split ',' but nothing seems to work for properly, where –ResourceName will accept it
Method 1:
[string]$webAppsArrays =#()
Foreach ($webApp in $webApps){
$webAp+',' -split ','
}
Method 2:
$
webApps | ForEach-Object {
$webApp = $_ + ","
Write-Host $webApp
}
Method 3:
$csvPath2 = 'C:\Users\Giann\Documents\_Git Repositorys\QueriedAppList2.csv'
$webApps = (Get-AzureRmResourceGroup -Name $resourceGroup | Get-AzureRmWebApp).name | out-file -FilePath $csvPath1 -Append
$csvFile2 = import-csv -Path $csvPath1 -Header Name
This ouputs a list in a CSV, however these are still objects, so I cannot pass each item into –ResourceName
I am going in circles trying to make the below a repeatable, looping script
The desired end result would be to use the below script, with an array of webApps, being queried from the provided resource group variable:
Any help would be greatly appreciated for how to use this script, but pull a dynamic list of WebApps from a specified Resource Group, keeping in mind the -ResourceName "String" restrictions in the $WebAppConfig variable
Here is the original script to create IP Restrictions for 1 Web App and 1 Resource Group, using properties from a CSV file:
#Create a Function to create IP Restrictions for 1 Web App and 1 Resource Group, using properties from the CSV file:
#Variables
$WebApp = ""
$resourceGroup =""
$subscription_Id = ''
#Login to Azure
Remove-AzureRmAccount -ErrorAction SilentlyContinue | Out-Null
Login-AzureRmAccount -EnvironmentName AzureUSGovernment -Subscription $subscription_Id
Function CreateIpRestriction {
Param (
[string] $name,
[string] $ipAddress,
[string] $subnetMask,
[string] $action,
[string] $priority
)
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp -ResourceGroupName $ResourceGroup -ApiVersion $APIVersion)
$ipRestriction = $WebAppConfig.Properties.ipSecurityRestrictions
$ipRestriction.name = $name
$ipRestriction.ipAddress = $ipAddress
$ipRestriction.subnetMask = $subnetMask
$ipRestriction.action = $action
$ipRestriction.priority = $priority
return $ipRestriction
}
#Set csv file path:
$csvPath5 = 'C:\Users\Giann\Documents\_Git Repositorys\ipRestrictions5.csv'
#import CSV Contents
$ipRestrictionArray = Import-Csv -Path $csvPath5
$ipRestrictions = #()
foreach($item in $ipRestrictionArray){
Write-Host "Adding ipRestriction properties for" $item.name
$newIpRestriction = CreateIpRestriction -name $item.name -ipAddress $item.ipAddress -subnetMask $item.subnetMask -action $item.action -priority $item.priority
$ipRestrictions += $newIpRestriction
}
#Set the new ipRestriction on the WebApp
Set-AzureRmResource -ResourceGroupName $resourceGroup -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp/web -ApiVersion $APIVersion -PropertyObject $ipRestrictions
As continuation on the comments, I really need multiline, so here as an answer.
Note that I cannot test this myself
This page here shows that the Set-AzureRmResource -Properties parameter should be of type PSObject.
(instead of -Properties you may also use the alias -PropertyObject)
In your code, I don't think the function CreateIpRestriction returns a PSObject but tries to do too much.
Anyway, try like this:
Function CreateIpRestriction {
Param (
[string] $name,
[string] $ipAddress,
[string] $subnetMask,
[string] $action,
[string] $priority
)
# There are many ways to create a PSObject (or PSCustomObject if you like).
# Have a look at https://social.technet.microsoft.com/wiki/contents/articles/7804.powershell-creating-custom-objects.aspx for instance.
return New-Object -TypeName PSObject -Property #{
name = $name
ipAddress = $ipAddress
subnetMask = $subnetMask
action = $action
priority = $priority
}
}
#Set csv file path:
$csvPath5 = 'C:\Users\Giann\Documents\_Git Repositorys\ipRestrictions5.csv'
#import CSV Contents
$ipRestrictionArray = Import-Csv -Path $csvPath5
# create an new array of IP restrictions (PSObjects)
$newIpRestrictions = #()
foreach($item in $ipRestrictionArray){
Write-Host "Adding ipRestriction properties for" $item.name
$newIpRestrictions += (CreateIpRestriction -name $item.name -ipAddress $item.ipAddress -subnetMask $item.subnetMask -action $item.action -priority $item.priority )
}
# here we set the restrictions we collected in $newIpRestrictions in the $WebAppConfig.Properties.ipSecurityRestrictions array
$APIVersion = ((Get-AzureRmResourceProvider -ProviderNamespace Microsoft.Web).ResourceTypes | Where-Object ResourceTypeName -eq sites).ApiVersions[0]
$WebAppConfig = (Get-AzureRmResource -ResourceType Microsoft.Web/sites/config -ResourceName $WebApp -ResourceGroupName $ResourceGroup -ApiVersion $APIVersion)
$WebAppConfig.Properties.ipSecurityRestrictions = $newIpRestrictions
$WebAppConfig | Set-AzureRmResource -ApiVersion $APIVersion -Force | Out-Null
The code above will replace the ipSecurityRestrictions by a new set. You may want to consider first getting them and adding to the already existing list.
I found examples for Getting, Adding and Removing ipSecurityRestrictions here, but I can imagine there are more examples to be found.
Hope that helps.

PS Script: Match First 3 octets of IP address with IPArray

Need help on PS Script.
Requirement: I would like to copy large files to multiple VM using FTP. I have FTP with multiple IP Ranges with Class C Subnet.
Ex."10.10.10.10","10.11.10.10""10.10.12.10","10.10.10.13" (All are Class C with subnet 255.255.255.
The script will be executed locally on each VM. so I want the script to match first 3 octets of assigned IP(few VM have multiple IP's with different ranges too) and then pick the FTP IP from the list of IP address and set that as variable.
As per my knowledge, I created the IP array which contains all the FTP IP Address. Then I use Win32_NetworkAdapterConfiguration to get the list of IP address, Subnet, and Gateway. I put the Win32_NetworkAdapterConfiguration to the hash table so that I can use that for comparison.
Need help to compare hash table data and fetch the matched IP address from IP array and set that FTP IP as Variable.
Ex. If VM A have IP 10.10.12.25 then it should use 10.10.12.10 FTP IP.
I need to use Win32_NetworkAdapterConfiguration to make the script compatible with old operating systems running PS v2
$FTPIPs = #("10.10.10.10","10.11.10.10""10.10.12.10","10.10.10.13")
$AllIPs = #()
foreach ($adapter in (Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName $env:COMPUTERNAME ))
{
$Prop = #{
'IPAddress' = $adapter.IpAddress
'SubnetMask' = $adapter.IPSubnet
'DefaultGateway' = $adapter.DefaultIPGateway
}
$obj = New-Object -TypeName PSobject -Property $Prop
$AllIPs += $obj
}
Created Script as below.
$FTPIPs = #("10.10.10.10","10.11.10.10","10.10.12.10","10.10.10.13")
$Prop = #{
'IPAddress' = '10.11.10.54'
'SubnetMask' = '255.255.255.128'
'DefaultGateway' = '10.11.10.1'
}
$LocalIP = New-Object -TypeName PSobject -Property $Prop
$FTPIPs |
ForEach-Object {
If ((([IPAddress]$LocalIP.IPAddress).Address -band ([IPAddress]$LocalIP.SubnetMask).Address) -eq (([IPAddress]$_).Address -band ([IPAddress]$LocalIP.SubnetMask).Address)) {
"$_ is in the same subnet as $($LocalIP.IPAddress)"
}
}

Powershell Select-Object -expandproperty strange behavior when input is array and contains array properties

I am trying to build a report file collecting data from various sources.
I have built a reporting structure like this:
$Data = import-csv "some CSV FILE"
<#
csv file must look like this
hostname,IP
server1,192.168.1.20
#>
Then I am building an array object, prepopulated with "initial values", and I attach it to my $data variable
$Ids = ('7.1.1.1','7.1.1.2')
$CheckObj= #()
foreach ($id in $IDs) {
$row = "" | Select-Object CheckID,CheckData,CheckDataRaw
$row.CheckID = $id
$row.CheckData = "NotChecked"
$CheckObj+= $row
}
$Data = $Data | Select *,CheckData
$data | % {$_.CheckData = $CheckObj}
The resulting object is:
hostname : server1
ip : 192.168.1.20
CheckData : {#{CheckID=7.1.1.1; CheckData=NotChecked; CheckDataRaw=},
#{CheckID=7.1.1.2; CheckData=NotChecked; CheckDataRaw=}}
All is well until I want to do this:
$FinalReport = $data | Select-Object -Property * -ExpandProperty Checkdata
I get all these errors, which let's say I can ignore...
Select-Object : The property cannot be processed because the property
"CheckData" already exists.
At line:1 char:24
+ ... lReport = $data | Select-Object -Property * -ExpandProperty Checkdata
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (#{hostname=serv...ystem.Objec
t[]}:PSObject) [Select-Object], PSArgumentException
+ FullyQualifiedErrorId : AlreadyExistingUserSpecifiedPropertyExpand,Micro
soft.PowerShell.Commands.SelectObjectCommand
BUT, an entire set of other variable gets altered, like:
$data | fl
hostname : server1
ip : 192.168.1.1
CheckData : {#{CheckID=7.1.1.1; CheckData=NotChecked; CheckDataRaw=;
hostname=server1; ip=192.168.1.1}, #{CheckID=7.1.1.2;
CheckData=NotChecked; CheckDataRaw=; hostname=server1;
ip=192.168.1.1}}
aswell as the $CheckObj variable
$CheckObj
CheckID : 7.1.1.1
CheckData : NotChecked
CheckDataRaw :
hostname : server1
ip : 192.168.1.1
CheckID : 7.1.1.2
CheckData : NotChecked
CheckDataRaw :
hostname : server1
ip : 192.168.1.1
This is totally unintended on my side...
Can someone clarify what I am doing wrong?
I am using powershell 5.0 on Windows 7.
All testing was done using powershell_ise, and I didn't change any of the powershell defaults
My expected result would be for the $Final Report variable to contain the expanded content, not all the variables I used in the process...
It seems, after a bit more digging I understood, to some extent why this is occurring.
I am using simple $b = $a assignments, which appear to be a form of shallow copy. So any change in $b also impacts object $a and vice-versa.
For my purpose I need distinct copies of the data, it seems the solution is to do a deep copy, similar to the solution of this post:
PowerShell copy an array completely
So the working code, which gives me the desired result would be:
Function Copy-Object ($Source,[switch]$DeepCopy) {
# Serialize and Deserialize data using BinaryFormatter
if ($DeepCopy) {
$ms = New-Object System.IO.MemoryStream
$bf = New-Object System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
$bf.Serialize($ms, $Source)
$ms.Position = 0
#Deep copied data
$Target = $bf.Deserialize($ms)
$ms.Close()
Write-Output $Target
}
Else {
Write-Output $Source
}
}
$Data = "" | select hostname,IP
$data.Hostname = "server1"
$data.IP = "192.168.1.10"
$Ids = ('7.1.1.1','7.1.1.2')
$CheckObj= #()
foreach ($id in $IDs) {
$row = "" | Select-Object CheckID,CheckData,CheckDataRaw
$row.CheckID = $id
$row.CheckData = "NotChecked"
$CheckObj += $row
}
$Data = Copy-Object -source $Data -DeepCopy | Select *,CheckData2
$Data | % {$_.CheckData2 = Copy-Object -source $CheckObj -DeepCopy}
$FinalReport = Copy-Object -source $Data -DeepCopy | Select-Object -Property hostname,IP -ExpandProperty Checkdata2
$FinalReport | ft
output being:
CheckID CheckData CheckDataRaw hostname IP
------- --------- ------------ -------- --
7.1.1.1 NotChecked server1 192.168.1.10
7.1.1.2 NotChecked server1 192.168.1.10

Format array and include in e-mail using powershell

I am trying to build a powershell script which collects system up-time information from multiple system and then e-mails it. I want to format the e-mail so I can structure it in a sensible way and later add more information about the systems.
Today I have this script below which collect the uptime information about a list of systems and then passes these information in a array to the SendMail function which includes the array in an e-mail. However the current output is like this.
Uptime list: Restarted 02/19/2013 04:04:52 MyServer01 Computer Uptime: 23 days and 9 hours Restarted 02/15/2013 04:17:40 MyServer02 Computer Uptime: 27 days and 9 hours
I would like to have the output or rather the e-mail text more like:
System | Restarted date/time | Uptime
MyServer01 02/17/2013 04:04:52 25 days and 9 hours
MyServer02 02/17/2013 04:04:52 25 days and 9 hours
How can I format the text in the e-mail or do I have to do that before I pass the array to the e-mail function.
The script itself:
`
function SendMail([string]$arg1){
#SMTP server name
$smtpServer = "mysmtp"
#Creating a Mail object
$msg = new-object Net.Mail.MailMessage
#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
#Email structure
$msg.From = "my#overtherainbow.ddd"
$msg.ReplyTo = "noreply#overtherainbow.ddd"
$msg.To.Add("my#overtherainbow.ddd")
$msg.subject = "System uptime"
$msg.body = ("List og servers and uptime:" + $arg1 )
#Sending email
$smtp.Send($msg)}
function CheckUptime($listofservers){
$k =#()
foreach($s in $listofservers){
$Booted = Get-WmiObject -Class Win32_OperatingSystem -Computer $s
$Calc = [DateTime]::Now - $Booted.ConvertToDateTime($Booted.LastBootUpTime)
$k += " Restarted " +
$Booted.ConvertToDateTime($Booted.LastBootUpTime) +
" " + $s + " " +
" Computer Uptime: " +
$Calc.days + " " +
"days and", $Calc.hours + " hours"
}
return $k
}
#Defining list og servers
$listofservers = "MyServer01", "MyServer02"
#Calling function
$message = CheckUptime($serverlist)
SendMail($message)
`
Use Format-Table to format the data as desired, and pipe that into Out-String to convert into a single string containing the formatted data.
Then append that to your email content.
This relies on the data gathering replacing building a single string with building a custom object with separate fields for each different column.
Eg (incomplete, but should give you the idea)
$serverData = $serverList | Foreach-Object { GetServerData $_ } |
Format-Table Name,
#(l='Boot Time'; e={$_.BootTime.ToLocalTime()}},
#(l='Uptime'; e={FormatUptime([DateTime]::UtcNow - $_.BootTime}} |
Out-String
$emailBody += $serverData
where the referenced functions are already defined, along the lines of:
function GetServerData {
params([string]$name)
$os = Get-WmiObject -Class Win32_OperatingSystem -Computer $name
$bootTimeLocal = $os.ConvertToDateTime($os.LastBootUpTime)
new-object PSObject -property #{
Name = $name;
# Keep everything in UTC to avoid confusion in calcs,
# TODO FIX: Use the remote machine's timezone for this....
BootTime = $bootTimeLocal.ToUniversalTime()
}
}
and
function FormatUptime {
params([TimeSpan]$time)
...
}
Summary:
Use Format-* cmdlets to format, and if not immediately passing to output (and the default Out-Default appended to the end of every pipeline without an Out-*) then convert to a string with Out-String.
Keep data types in their native type as long as possible. Do not convert to human readable forms until you have to. Thus information is not lost.
Make use of custom objects and properties (especially NoteProperties) to create your own object "types" to keep related information together.
First, it shows little effort when your sample is not even a working sample. Variable typos etc.
As a solution, why don't you create custom objects containing the data for each server and output it as an array?. Something like:
PS > function CheckUptime($listofservers){
$res = #()
foreach($s in $listofservers){
$boot = Get-WmiObject win32_operatingsystem -ComputerName $s | % { $_.ConvertToDateTime($_.LastBootUpTime) }
$uptime = [DateTime]::Now - $boot
$res += New-Object psobject -Property #{
Server = (Get-Culture).TextInfo.ToTitleCase($s)
BootTime = $boot
Uptime = "{0} days and {1} hours" -f $uptime.Days, $uptime.Hours
}
}
#Output
$res
}
$myservers = "localhost", "localhost"
$str = "List of servers and uptime:`r`n"
$str += CheckUptime $myservers | Out-String
PS > $str
List of servers and uptime:
Server Uptime BootTime
------ ------ --------
Localhost 8 days and 6 hours 06.03.2013 09:33:37
Localhost 8 days and 6 hours 06.03.2013 09:33:37

Populate existing array/table with new data

I'm still at the beginning of learning powershell. My goal here is to have a script that pulls SCOM alerts that are one day old, compares the alerts' netbios computer name to a value from a CSV that I will import.
If the ServerName from the CSV Matches the NetbiosName it then adds the Administrator Name to the original Array/Table I created. Currently it does everything I ask of it, but when I have it output the final data it only has the last administrator that was used added to the Admin column instead of the appropriate admin.
NetbiosComputerName : Server
MonitoringObjectDisplayName : Server.Domain
Name : Blah Blah Blah
Severity : Warning
ResolutionState : 0
RepeatCount : 0
Server Adminstrator : Admin
NetbiosComputerName : Server
MonitoringObjectDisplayName : Server.Domain
Name : Blah Blah Blah
Severity : Warning
ResolutionState : 0
RepeatCount : 0
Server Adminstrator : Admin
On the Second output the Admin should instead reflect the admin for that particular server and not very same one.
Here is my code.
# Load SCOM snap-in
add-pssnapin "Microsoft.EnterpriseManagement.OperationsManager.Client";
$server = "RMSSERVER"
# Connect to OpsMgr SDK - change management server to your RMS
new-managementGroupConnection -ConnectionString:RMSSERVER.domain;
set-location "OperationsManagerMonitoring::";
$Date = (Get-Date).adddays(-1)
$ScomAlert = get-alert | where {($_.ResolutionState -eq 0) -and ($_.TimeRaised -gt $Date) } | Select NetbiosComputerName,MonitoringObjectDisplayName, Name,Severity, ResolutionState, RepeatCount
$Administrators = Import-CSV "C:\Script\SCOM\admin.csv"
$TableSC = $ScomAlert
ForEach ($Alert in $ScomAlert)
{
$NetBios = "$($Alert.NetBiosComputerName)"
$MonObjectName = "$($Alert.MonitoringObjectDisplayName)"
$AlertName = "$($Alert.Name)"
$Severity = "$($Alert.Severity)"
$ResState = "$($Alert.ResolutionState)"
$RepCount = "$($Alert.RepeatCount)"
ForEach ( $Admin in $Administrators )
{
$ServerName = "$($Admin.ServerName)"
$ServerAdmin = "$($Admin.AdminName)"
if($ServerName -eq $NetBios)
{
$ServerAdministrator = $ServerAdmin
}
}
$TableSC | Add-Member -MemberType NoteProperty -Name "Server Adminstrator" -Value $ServerAdministrator -Force
}
$TableSC | Add-Member -MemberType NoteProperty -Name "Server Adminstrator" ...
The above line replaces the property Server Administrator in all items of the collection $TableSC with the given value.
If you want each item updated with the respective administrator you need something like this (untested):
$Administrators = #{}
Import-Csv "C:\Script\SCOM\admin.csv" | % {
$Administrators[$_.ServerName] = $_.AdminName
}
$TableSC = $ScomAlert | select NetBiosComputerName,
MonitoringObjectDisplayName, Name, Severity, ResolutionState, RepeatCount,
#{n="Server Administrator";e={$Administrators[$_.NetBiosComputerName]}}

Resources