Powershell. Run Command for Every Specific String in Array - powershell

I have a PowerShell command:
Get-AzWebAppAccessRestrictionConfig -ResourceGroupName RG1 -Name CoolTestWebApp1 | Select -ExpandProperty MainSiteAccessRestrictions
That once is ran outputs array:
RuleName : IP-1
Description :
Action : Allow
Priority : 1
IpAddress : 10.0.0.0/24
SubnetId :
RuleName : IP-2
Description :
Action : Allow
Priority : 2
IpAddress : 10.0.0.1/24
SubnetId :
How can I run a command for every entry in RuleName?
For example, something like:
Get-AzWebAppAccessRestrictionConfig -ResourceGroupName RG1 -Name CoolTestWebApp1 | Select -ExpandProperty MainSiteAccessRestrictions | ForEach-Object { Write-Host $RuleNameX }
That would execute:
Write-Host $RuleName1
Write-Host $RuleName2
Which in turn would output:
IP-1
IP-2

You pretty much had it:
Get-AzWebAppAccessRestrictionConfig -ResourceGroupName RG1 -Name CoolTestWebApp1 | Select -ExpandProperty MainSiteAccessRestrictions | ForEach-Object { Write-Host $_.RuleName }
Within a ForEach-Object, you can access the current objects attributes using $_.
i.e
ForEach-Object { $_.attributeName }
If the RuleName attribute contains an array of values, you could then iterate over them too:
$siteRestrictions = (Get-AzWebAppAccessRestrictionConfig -ResourceGroupName RG1 -Name CoolTestWebApp1).MainSiteAccessRestrictions
# Loop through objects
foreach($item in $siteRestrictions) {
# Loop through each RuleName
foreach($ruleName in $item.RuleName) {
# Do some logic here
}
}

Related

How can I use a variable as Application Pool name?

The following code gives me the default app pool, converted to string. When I pass this variable
$appPoolName = Get-IISAppPool | Select-Object Name | Where-Object {$_.Name -like "Default" } | Out-String
(Get-IISAppPool $appPoolName).processmodel.username
I get the following error
Why not just do this:
$appPoolName = (Get-IISAppPool | Where-Object Name -Match 'Default').Name
(Get-IISAppPool $appPoolName).processmodel
# Results
<#
IdentityType : ApplicationPoolIdentity
IdleTimeout : 00:20:00
IdleTimeoutAction : Terminate
LoadUserProfile : True
MaxProcesses : 1
PingingEnabled : True
PingInterval : 00:00:30
PingResponseTime : 00:01:30
Password :
ShutdownTimeLimit : 00:01:30
StartupTimeLimit : 00:01:30
UserName :
...
#>

Wait-Task outputs an error when deploying VMs from a .csv file

I wrote a GUI script that clones VMs from a template and gives them a static IP address that they get from a .csv file.
Everything seems to work fine except for an error I'm getting. The clone completes successfully anyway, but I'm not sure how to fix the error or if I even should.
function StartClone {
$VM_List = Import-Csv $csvTB.Text
$numClones = [int]((Get-Content $csvTB.Text).Length)
$vmh = Get-VMHost
$NewParameters = #{
# Name = ''
Template = $TemplateMenu.Text
Datastore = $DatastoreMenu.Text
DiskStorageFormat = 'Thin'
Location = $FolderCB.Text
OSCustomizationSpec = $CustomizationCB.Text
VMHost = Get-Random -InputObject $vmh
Server = $VCenterTB.Text
RunAsync = $true
}
$SetParameters = #{
NumCpu = $CPU_TB.Text
MemoryGB = $RAM_TB.Text
Notes = $NotesTB.Text
Confirm = $false
}
$taskList = if ($NumClones -gt 0) {
# foreach ($item in (Import-Csv $csvTB.Text))
$VM_List | ForEach-Object {
$NewParameters['Name'] = "$($_.Hostname)"
Get-OSCustomizationSpec -name $CustomizationCB.Text | Get-OSCustomizationNICMapping | Set-OSCustomizationNICMapping -IPMode UseStaticIP -IPAddress "$($_.IP)" -SubNetMask "$($_.Subnet)" -DefaultGateway "$($_.Gateway)" -Dns ""
New-VM #NewParameters
}
}
$newVM = $taskList | Wait-Task -ErrorAction SilentlyContinue
$newVM | Set-VM #SetParameters
$newVM | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $VLAN_CB.Text -Confirm:$false
if ($StartVM_CB.Checked -eq $true) {$newVM | Start-VM }
}
The error I get is related to the Wait-Task command which I have to use to wait for the VMs to actually be done cloning.
Wait-Task : The input object cannot be bound to any parameters for the command either because
the command does not take pipeline input or the input and its properties do not match any of
the parameters that take pipeline input.
At line:465 char:26
+ $newVM = $taskList | Wait-Task
+ ~~~~~~~~~
+ CategoryInfo : InvalidArgument: (UseStaticIP:192.168.1.1:PSObject) [Wait-Tas k], ParameterBindingException + FullyQualifiedErrorId : InputObjectNotBound,VMware.VimAutomation.Common.Commands.Cmdlets. WaitTask
The error repeats for each VM in my .csv file, so if I have 4 VMs that I want to deploy, it will pop up 4 times. As the error suggests, it's related to the fact I'm looping over the .csv file and creating a new VM with with iteration, but like I said, everything completes without issues and the VMs are working.
Any input on this would be great. Thanks.
New-VM returns a VirtualMachineImpl object, and Wait-Task has nothing to do it...
If you add the -RunAsync parameter to New-VM it will return a TaskImpl object, then you can pipe the results to the Wait-Task cmdlet.

2 inputs for Set-Annotation

I'm looking to use this command to set annotations on VMware virtual machines.
Set-Annotation -entity $vm -CustomAttribute "Owner" -Value "$owner"
I need the script to read 2 input files during the same loop. One input for the entity name and one for the value.
If we make 2 text files,
file 1 =
vm1
vm2
vm3
file 2 =
john
bob
ken
I need the script to do:
Set-Annotation -entity vm1 -CustomAttribute "Owner" -Value "john"
then
Set-Annotation -entity vm2 -CustomAttribute "Owner" -Value "bob"
I've been able to get different loops to run, but nothing correctly.
Try the following:
# Read VM names and owners into parallel arrays.
$vmNames = Get-Content 'file 1'
$owners = Get-Content 'file 2'
# Loop over the VM names with a pipeline and assign the corresponding owner
# by array index, maintained in variable $i.
$vmNames | % { $i = 0 } `
{ Set-Annotation -entity $_ -CustomAttribute "Owner" -Value $owners[$i++] }
You could streamline this by using Get-Content 'file 1' directly as the start of the pipeline, without the need to collect all lines in array variable $vmNames first.

Powershell output formatting table

I have a line of script
`Connect-hpoa *servername* |get-HPOAServerName |format-list -property Hostname,ServerName`
that gives the following output:
Hostname : gblonblade1 ServerName : {#{Bay=1; ServerName=Absent;
SerialNumber=; Status=; Power=; UID=; Partner=}, #{Bay=2;
ServerName=GBLON1234.ops.test.net; SerialNumber=123456789
Status=OK; Power=On; UID=Off; Partner=}, #{Bay=3;
ServerName=GBLON5678; SerialNumber=987654321; Status=OK; Power=On;
UID=Off; Partner=}, #{Bay=4;ServerName=Absent; SerialNumber=; Status=;
Power=; UID=; Partner=}...}
how can i change the output to view just the Hostname and server name and bay number?
Hostname : gblonblade1
Servername: Absent
Bay=1
Hostname : gblonblade1
Servername: gblon1234.ops.test.net
Bay=2
etc....
Assuming that Get-HPOAServerName produces a property ServerName that contains a list of hashtables (which is what your output looks like), you could transform the output like this:
... | Get-HPOAServerName | % {
$hostname = $_.hostname
$_.Servername | % {
New-Object -Type PSCustomObject -Property $_ |
select -Property *,#{n='HostName';e={$hostname}}
}
}

Cannot execute same powershell function twice, gives error

I'm fairly new to powershell and I'm basically writing a script which performs a join on several .csv files based on a primary column. I am using the Join-Collections script from here: http://poshcode.org/1461
As I need to combine 5 .csv files, I need to run this function 4 times.
On the first run, it works fine, but then trying to run the function again gives 'No object specified to the cmd-let' errors.
In trying to debug, I've literally copy-and-pasted the line and only changed the variable name to make a new variable.
I must be doing something fundamentally wrong...
$SumFile = "VMSummary.csv"
$MemFile = "VMMemory.csv"
$ProcFile = "VMProcessor.csv"
$OSFile = "VMOS.csv"
$NicFile = "VMNics.csv"
$SumFileCSV = Import-Csv $SumFile | Select VMElementName,GuestOS,Heartbeat,MemoryUsage,IpAddress
$MemFileCSV = Import-Csv $MemFile | Select VMElementName,Reservation
$ProcFileCSV = Import-Csv $ProcFile
$OSFileCSV = Import-Csv $OSFile
$NicFileCSV = Import-Csv $NicFile
$JoinColumn = "VMElementName"
function Join-Collections {
PARAM(
$FirstCollection
, [string]$FirstJoinColumn
, $SecondCollection
, [string]$SecondJoinColumn=$FirstJoinColumn
)
PROCESS {
$ErrorActionPreference = "Inquire"
foreach($first in $FirstCollection) {
$SecondCollection | Where{ $_."$SecondJoinColumn" -eq $first."$FirstJoinColumn" } | Join-Object $first
}
}
BEGIN {
function Join-Object {
Param(
[Parameter(Position=0)]
$First
,
[Parameter(ValueFromPipeline=$true)]
$Second
)
BEGIN {
[string[]] $p1 = $First | gm -type Properties | select -expand Name
}
Process {
$Output = $First | Select $p1
foreach($p in $Second | gm -type Properties | Where { $p1 -notcontains $_.Name } | select -expand Name) {
Add-Member -in $Output -type NoteProperty -name $p -value $Second."$p"
}
$Output
}
}
}
}
$Temp = Join-Collections $SumFileCSV $JoinColumn $MemFileCSV $JoinColumn
$Temp
##BREAKS HERE
$Temp2 = Join-Collections $SumFileCSV $JoinColumn $MemFileCSV $JoinColumn
UPDATE
It gives the following error:
No object has been specified to the get-member cmdlet
+ foreach($p) in $Second | gm <<<< -type Properties | Where { $p1 -notcontains $_.Name } | select -expand Name)
The csv data is pretty straight forward. When I print out $Temp just before it breaks, it spits out:
GuestOS : Windows Server (R) 2008 Standard
Heartbeat : OK
IpAddress : 192.168.48.92
MemoryUsage : 1024
VMElementName : VM015
Reservation : 1024
GuestOS : Windows Server (R) 2008 Standard
Heartbeat : OK
IpAddress : 192.168.48.151
MemoryUsage : 1028
VMElementName : VM053
Reservation : 1028
GuestOS : Windows Server (R) 2008 Standard
Heartbeat : OK
IpAddress : 192.168.48.214
MemoryUsage : 3084
VMElementName : VM065
Reservation : 3084
GuestOS :
Heartbeat :
IpAddress :
MemoryUsage :
VMElementName : VM074
Reservation : 1024
GuestOS : Windows Server 2008 R2 Standard
Heartbeat : OK
IpAddress : 192.168.48.32
MemoryUsage : 3072
VMElementName : VM088
Reservation : 3072
GuestOS : Windows Server (R) 2008 Enterprise
Heartbeat : OK
IpAddress : 192.168.48.81
MemoryUsage : 3084
VMElementName : VM090
Reservation : 3084
GuestOS : Windows Server 2008 R2 Enterprise
Heartbeat : OK
IpAddress : 192.168.48.82
MemoryUsage : 5120
VMElementName : VM106
Reservation : 5120
The rest of the .csv data is the same sort of stuff - just stats on different servers.
Ideally what I want to do is this :
$Temp = Join-Collections $SumFileCSV $JoinColumn $MemFileCSV $JoinColumn
$Temp = Join-Collections $Temp $JoinColumn $ProcFileCSV $JoinColumn
$Temp = Join-Collections $Temp $JoinColumn $OSFileCSV $JoinColumn
$Temp = Join-Collections $Temp $JoinColumn $NicFileCSV $JoinColumn | Export-Csv "VMJoined.csv" -NoTypeInformation -UseCulture
This code works fine on Powershell v3 CTP 2 (which is probably what #manojlds is using). In Powershell V2 however the parameter $second of the Join-Object function is not bound when invoking Join-Collections the second time. This can be easily verified by adding the following line to the process block inside the Join-Object function:
$psboundparameters | out-host
You will notice that when invoking Join-Collections for the first time both parameters (of Join-Object are bound, however the second time $second is no longer bound.
It is unclear what is causing this behaviour, but since it seems to be working in Powershell V3 I'm guessing it's a bug.
To make the function work in Powershell V2 one could explicitly bind the parameters by replacing this line:
$SecondCollection | Where{ $_."$SecondJoinColumn" -eq $first."$FirstJoinColumn" } | Join-Object $first
by this line:
$SecondCollection | Where{ $_."$SecondJoinColumn" -eq $first."$FirstJoinColumn" } | %{Join-Object -first $first -second $_}