How to combine and rename properties of arrays in powershell? - arrays

I am pulling data from an API that ConnectWise offers. I am trying to pull a list of usernames and password and put them in to a format which I can either reference in another script (or combiner with this one), or that I can export to a CSV. I'd prefer the first if possible.
The code:
Function Get-CWConfiguration
{
[string]$BaseUri = "$CWServerRoot"
[string]$Accept = "application/vnd.connectwise.com+json; version=v2015_3"
[string]$ContentType = "application/json"
[string]$Authstring = $CWInfo + '+' + $CWCredentials1 + ':' + $CWCredentials2
$encodedAuth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(($Authstring)));
$Headers=#{"Authorization"="Basic $encodedAuth"}
Invoke-RestMethod -URI $BaseURI -Headers $Headers -ContentType $ContentType -Method Get
}
$username = (Get-CWConfiguration).questions | Where-Object {($_.questionid -eq 419)} | Sort-Object -Property questionid | select answer
$password = (Get-CWConfiguration).questions | Where-Object {($_.questionid -eq 420)} | Sort-Object -Property questionid | select answer
Everything above the $username variable is the stuff connecting to the API and I did not modify that. The code below is the best way I found to pull the info that I need. This is returning each variable as an array, which is fine but if that happens I need to have an array with 2 columns. 1 named Username and the other named Password. if I do a $username | Get-Member it returns:
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
answer NoteProperty string answer=admin#admin.com
My end goal is to have a variable like $credentials output something that displays like:
Username Password
-------- ----------
admin1#admin.com Password1
admin2#admin.com Password2
admin3#admin.com Password3
Most methods I have tried have resulted in everything put output to 1 line or 1 column. I'm not sure if I should be combining the arrays and renaming the $username.answer and $password.answer fields or creating a PSObject? My main difficulty has been that both variables have that same answer NoteProperty

You'd want to create new objects with the expanded properties of the current objects. This could all look something like this (note that I'm manually creating analogs for what you're all dealing with and ignoring the string manipulation you'll likely have to do)
$Username = New-Object -TypeName pscustomobject -Property #{Answer = "Answer:Hithisispatrick"}
$Password = New-Object -TypeName pscustomobject -Property #{Answer = "Answer:HelloThisisSquidward"}
$UsernameAnswer = $Username | Select -ExpandProperty Answer
$PasswordAnswer = $Password | Select -ExpandProperty Answer
$Final = New-Object -TypeName pscustomobject -Property #{UserName= $UsernameAnswer ; Password = $PAsswordAnswer}
This should give you output something like this:
UserName Password
-------- --------
Answer:Hithisispatrick Answer:HelloThisisSquidward
Then if you want to parameterize it/Expand it, you should be able then to toss it in to a foreach loop and have it output the Custom Object, setting that equal to an array that then builds out of those objects, like so(I'll use while to make it simpler to show):
$Count = 0
$Username = New-Object -TypeName pscustomobject -Property #{Answer = "Answer:Hithisispatrick"}
$Password = New-Object -TypeName pscustomobject -Property #{Answer = "Answer:HelloThisisSquidward"}
$Array = While ($Count -lt 5){
$UsernameAnswer = $Username | Select -ExpandProperty Answer
$PAsswordAnswer = $Password | Select -ExpandProperty Answer
$Final = New-Object -TypeName pscustomobject -Property #{UserName= $UsernameAnswer ; Password = $PAsswordAnswer}
$Final
$Count += 1
}
$Array
This produces something like this:
Password UserName
-------- --------
Answer:HelloThisisSquidward Answer:Hithisispatrick
Answer:HelloThisisSquidward Answer:Hithisispatrick
Answer:HelloThisisSquidward Answer:Hithisispatrick
Answer:HelloThisisSquidward Answer:Hithisispatrick
Answer:HelloThisisSquidward Answer:Hithisispatrick

Related

Issue creating an array of objects from a csv file

I'm fairly new with Powershell, so this is likely a rookie mistake, but I am trying to take a CSV document containing only user display names, query AD for the required information, and populate that information into object properties using a hash table.
Here is what i have right now
$Path = "C:\Scripts\Generate-CSRSpreadsheets\Roster-Jpay.csv"
$Table = Import-csv -Path $Path -Header EmployeeDisplayName
$Array = #()
$ADUser = Get-ADUser -Properties DisplayName,Manager -Filter {DisplayName -eq $_.EmployeeDisplayname}
ForEach($User in $Table){
$Object = New-Object PSObject -Property #{
DisplayName = $ADUser.DisplayName
GivenName = $ADUser.GivenName
Surname = $ADUser.Surname
Email = $ADUser.Mail
}
$Array += $Object
}
This seems to me like it should work fine, but when I check my output it looks something like this:
Example Output Image
Let's say in this example I have 9 total users, but it's only outputting the information from the last user in the csv. I've been pouring over this code, but I can't see what's wrong with it. Any help would be appreciated.
Thank you
You need to move your Get-ADUser inside the loop:
$Path = "C:\Scripts\Generate-CSRSpreadsheets\Roster-Jpay.csv"
$Table = Import-csv -Path $Path -Header EmployeeDisplayName
$Array = #()
ForEach($User in $Table){
$ADUser = Get-ADUser -Filter "DisplayName -eq '$($User.EmployeeDisplayName)'" -Properties DisplayName,Manager
$Object = New-Object PSObject -Property #{
DisplayName = $ADUser.DisplayName
GivenName = $ADUser.GivenName
Surname = $ADUser.Surname
Email = $ADUser.Mail
}
$Array += $Object
}

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.

Update object array over multiple iterations

I have an array of custom objects:
$report = #()
foreach ($person in $mylist)
{
$objPerson = New-Object System.Object
$objPerson | Add-Member -MemberType NoteProperty -Name Name -Value $person.Name
$objPerson | Add-Member -MemberType NoteProperty -Name EmployeeID
$objPerson | Add-Member -MemberType NoteProperty -Name PhoneNumber
$report += $objPerson
}
Note that I haven't set values for the last two properties. The reason I've done this is because I'm trying to produce a matrix where I'll easily be able to see where these are blanks (although I could just set these to = "" if I have to).
Then, I want to iterate through a second dataset and update these values within this array, before exporting the final report. E.g. (this bit is pretty much pseudo code as I have no idea how to do it:
$phonelist = Import-Csv .\phonelist.csv
foreach ($entry in $phonelist)
{
$name = $entry.Name
if ($report.Contains(Name))
{
# update the PhoneNumber property of that specific object in the array with
# another value pulled out of this second CSV
}
else
{
# Create a new object and add it to the report - don't worry I've already got
# a function for this
}
}
I'm guessing for this last bit I probably need my if statement to return an index, and then use that index to update the object. But I'm pretty lost at this stage.
For clarity this is a simplified example. After that I need to go through a second file containing the employee IDs, and in reality I have about 10 properties that need updating all from different data sources, and the data sources contain different lists of people, but with some overlaps. So there will be multiple iterations.
How do I do this?
I would read phonelist.csv into a hashtable, e.g. like this:
$phonelist = #{}
Import-Csv .\phonelist.csv | ForEach-Object { $phonelist[$_.name] = $_.number }
and use that hashtable for filling in the phone numbers in $report as you create it:
$report = foreach ($person in $mylist) {
New-Object -Type PSObject -Property #{
Name = $person.Name
EmployeeID = $null
PhoneNumber = $phonelist[$person.Name]
}
}
You can still check the phone list for entries that are not in the report like this:
Compare-Object $report.Name ([array]$phonelist.Keys) |
Where-Object { $_.SideIndicator -eq '=>' } |
Select-Object -Expand InputObject
I would iterate over the $phonelist two times. The first time, you could filter all phone entities where the name is in your $myList and create the desired object:
$phonelist = import-cse .\phonelist.csv
$report = $phonelist | Where Name -in ($mylist | select Name) | Foreach-Object {
[PSCustomObject]#{
Name = $_.Name
PhoneNumber = $_.PhoneNumber
EmployeeID = ''
}
}
The second time you filter all phone entities where the name is not in $myList and create the new object:
$report += $phonelist | Where Name -NotIn ($mylist | select Name) | Foreach-Object {
#Create a new object and add it to the report - don't worry I've already got a function for this
}

Call a Group Object by Name in ForEach Loop

I've imported a CSV file into an array and grouped it by email address. I'd like to be able to call the group by name in another ForEach loop so that I can send an email to each email address with a table of the $ServerID(s) and $DateTime(s) associated with them. Was Group-Object the best way to start this?
In the CSV file, one email address can be associated with many servers, but one server is only associated with one email address. The values in the file will be updated constantly - thus the need for dynamic variables for almost all objects.
$csv = import-csv "C:Example.csv"
$array = #()
ForEach ($server in $csv) {
$object = New-Object PSObject -Property #{"Email"= $server.Email; "ServerID" = $server.ServerID; "DateTime" = $server.DateTime}
$array += $object}
$array | Group-Object Email
Count Name Group
2 A#gmail.com {#{PG_Email=A#gmail.com; ServerID=333; Lease_End=10/10/15}, #{PG_Email=A#gmail.com; ServerID=111; Lease_End=12/12/15}}
1 B#gmail.com {#{PG_Email=B#gmail.com; ServerID=222; Lease_End=09/09/15}}
Yes, you should use Group-Object. I'm not sure what you mean with "call the group by name". If you want to select a group based by the name you would have to save the result from Group-Object and search for your group like:
$groups = $array | Group-Object Email
$selectedmail = "B#gmail.com"
$groups | Where-Object { $_.Name -eq $selectedmail }
Or you could save the groups in a hashtable:
#Create hashtable
$ht = #{}
#Fill hashtable with groups (email as key/ID)
$array | Group-Object Email | ForEach-Object { $ht[$_.Name] = $_.Group }
$selectedmail = "B#gmail.com"
#Get objects for selected mail
$ht[$selectedmail]
If you are going to mail everyone, I would simplify this to:
Import-Csv "C:\Example.csv" |
Group-Object Email |
ForEach-Object {
$to = $_.Name
#Get table of ServerID + Lease_End as string to send as mailbody
$mailbody = $_.Group | Format-Table -Property ServerID, Lease_End -AutoSize | Out-String
#You could also create a HTML-table with:
$htmlbody = $_.Group | Select-Object -Property ServerID, Lease_End | ConvertTo-HTML -Property Name, Length
#Send mail
#Send-MailMessage -To $to -Subject "Lease-status" -Body $mailbody -From "mysupersecret#mail.com"
}

Powershell Custom object - not passing foreach variable

I'm trying to create a custom object based on server names from a text file.
The script I have goes and imports the txt file into a Variable. Then runs a foreach server in the servers variable to create the custom object. I would like to be able to output the object's properties as a table that doesn't include the header info each time.
See script and output below:
$SERVERS = gc c:\servers.txt
foreach ($srv in $SERVERS)
{
$Obj = New-Object PsObject -Property`
#{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
echo $obj | ft Computername,RebootDay -autosize
}
This is the output currently:
Computername RebootDay
SERVER007 Sunday
Computername RebootDay
SERVER009 Sunday
Computername RebootDay
SERVER003 Sunday
I'd like it to look more like:
Computername RebootDay
SERVER007 Sunday
SERVER001 Sunday
SERVER009 Sunday
TessellatingHeckler was on the right track really. The issue with his code is that you can't pipe a ForEach($x in $y){} loop to anything (not to be confused with a ForEach-Object loop that you usually see shortened to just ForEach like $Servers | ForEach{<code here>}) You don't want to pipe objects to Format-Table one at a time, you want to pipe a collection of objects to it so that it looks nice. So here's the modified code:
$SERVERS = gc c:\servers.txt
$Results = foreach ($srv in $SERVERS)
{
New-Object PsObject -Property #{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
}
$Results | FT ComputerName,RebootDay -auto
That collects the objects in an array, then you pass the whole array to Format-Table
Don't put the "ft" (Format-Table) command inside the loop, put it outside, once, at the end. e.g.
$SERVERS = gc c:\servers.txt
$results = foreach ($srv in $SERVERS)
{
$Obj = New-Object PsObject -Property`
#{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
$Obj
}
$results | ft Computername,RebootDay -autosize
Edit: Fixed for foreach pipeline bug.
You could possibly neaten it a bit because you don't need to make a new PSObject for a hashtable, and then put the object into the pipeline; you don't need to repeat the Get-QADComputer commands three times. I'm suspicious that the $obj.combined line isn't doing anything - how can you refer to an object inside the properties of the new-object call, before it gets assigned that name? And the repeated splits could probably be combined because it operates on individual characters, not strings.
gc c:\servers.txt | foreach {
$memberof = (Get-QADComputer $_).memberof
#{
Computername = $_;
SecurityGroup = $memberof;
RebootDay = $memberof.split(', ')[2];
Combined = $memberof.split(', =')[1];
# ?? RebootTime = $obj.combined.substring(0,4)
}
} | ft Computername,RebootDay -autosize

Resources