Powershell take list of servers, and add "server" property - arrays

I'm looking to combine data from two sources in an existing script.
I have one list of servers which is pulled via a Citrix command called Get-XAServer. Using this cmdlet, an array is created, with two properties, server and logonmode. Running $1stList looks like this:
SERVER LOGONMODE
Server1 AllowLogOns
Server2 AllowLogOns
Now, I want to update this list of servers that can't be pulled via the get-XAserver cmdlet. So, inside the script, I've just got an array variable that's like this, but from a list of servers that's in the following format:
$2ndList = "Server3", "Server4", "Server5"
Problem is, the server property isn't attached to the 2nd list. So, when i try to combine the arrays, they aren't parsed properly.
How do I iterate through the 2nd list so that the server and logonmode properties are both added to each/every server in the $2ndList array?

You could use a foreach:
foreach ($server in $2ndlist) {
$1stList += [pscustomobject]#{
SERVER = $server
LOGONMODE = ""
}
}
Or a ForEach-Object loop:
$2ndList | % {
$1stList += [pscustomobject]#{
SERVER = $_
LOGONMODE = ""
}
}

Related

Powershell - Exporting data from powershell into a csv file using custom objects

So I received a list of users from a co-worker who needed to confirm who in the list was still employed and who wasn't. I chose to filter out all users that either didn't exist in AD or were disabled and assign them to $TerminatedUser. I took all active users that assigned them to $EmployeedUser. (I know I spelled "Employed" wrong) I then tried to use the data from $EmployeedUser and $TerminatedUser and create a report within $EmployementStatus.
What I end up with is two columns which is awesome but I also only get 1 cell for each column. All the data for each column is bunched into one cell which makes it hard to read. At first when outputting $EmployementStatus to a csv file was only getting the headers and [system.object] for each cell. I was able to get around that.
So my question here now is: Is it possible to export $EmployementStatus to a csv where the data is listed out and each "Employed"/"Terminated" user receives their own cell as opposed to them all being bunched in cells A2 and B2?
Teach me something!
This is sample code, since I'm not going to type out all that stuff again. And it isn't tested.
What you want, apparently, is to check there's an enabled AD user account that matches your userlist. For Powershell versions greater than 3.0, you can output [pscustomobject] directly into an array from a Foreach.
You just need ONE query to AD to determine if a user exists and whether the account is enabled ("Enabled" is one of the default properties returned in Get-AdUser).
It's probably more convenient for output if you simply have a "Verified" column and set that to TRUE or FALSE. Or you can have a "Status" column and output text to that like "Disabled" or "NotPresent" or "Verified". Whatever, really, I'm going with the easiest.
The try/catch is so you don't get a load of errors when the user doesn't exist. If you want to set different statuses for each "state", then you can place strings in there rather than $true/$false.
$employmentStatus = Foreach ($GID in $MyList) {
$ID = $GID.SamAccountname
try {
# if the user isn't found, it'll go to the Catch block after the next line
$u = get-aduser $ID -erroraction stop
if ($u.enabled) {
$verified = $true
}
else {
$verified = $false
}
}
catch {
# if the user doesn't exist, they're not verified
$verified = $false
}
# output the per-user status as a pscustomobject in $employmentStatus
[pscustomobject]#{
ADUser = $ID
Verified = $verified
}
}
You should find that if you process your userlist with that, you can check the result with $employmentStatus | out-gridview.
That should show the "AdUser" and "Verified" columns, with TRUE or FALSE for each user.
If that looks OK, so will your CSV export: $employmentStatus | export-csv [path].
If you're using an old PS version, then you may need to predefine your output array as you did originally. Then you'd just fix up the line with the [pscustomobject] to append it to the array. Everything else works the same.
$employmentStatus = #()
Foreach ($GID in $MyList) {
...
# output the per-user status as a pscustomobject - append to $employmentStatus
$employmentStatus += [pscustomobject]#{
ADUser = $ID
Verified = $verified
}
}

Powershell Array -> HTML as string not as .Length

Currently I have a script that does a bunch of things, one thing is that it checks a .txt file to get all the current servers and then just does a ping to check connectivity before it does the rest of the script. I currently have that setup to add the servers it could not ping into an array so that it will not slow down the rest of the process attempting on failed servers. At the end of the script I have it all converted to an HTML document for ease of viewing. I would like to add the servers that failed the connection to the end of the HTML document to show that those servers failed.
As the script is now, it just prints the .Length property of the array I put those servers in. Here is the code I have that sets up the array, adds servers to it, and then the convertto-html part.
$servers = Get-Content "path\to\text.txt"
$failedConn = New-Object 'System.Collections.Generic.List[System.Object]'
foreach ($server in $servers)
{
$pingResult = Get-Ciminstance -ClassName win32_pingstatus -Filter "address='$server' and timeout=1000"
if ($pingResult.StatusCode -eq 0)
{
Write-Host "$server ping successful
}
else
{
Write-Host "$server ping Fail" -ForegroundColor Red
$failedConn.Add($server)
<# Also tried $failedConn += ($server) with the same error #>
}
}
<# Code for a forloop to do tasks where it adds an HTML variable called CombinedBody styling the tables whatnot #>
$CombinedBody += $failedConn | ConvertTo-Html | Out-File -Append C:\Path\To\html.html
The result just puts the failed connections at the very bottom of the HTML document, but it prints it as the length of each server name. How do I get it to print the actual server name? Any help is greatly appreciated!!
Length is the only property in the String object that ConvertTo-Html sees so that's what gets output. As a workaround, you can wrap the server names in another object that only have a single property containing the name, then it should output the actual names. Like this:
$failedConn | foreach{[pscustomobject]#{"Failed Servers" = $_}} | ConvertTo-Html | Out-File -Append C:\Path\To\html.html
Note that I've removed $CombinedBody += since Out-File doesn't return any output so that won't do anything.

How to reference Script Arguments in DSC Script Resource

I'm using TFS 2015 Update 2 to create a release. One of my release steps is a "PowerShell on Target Machines" task that I'm using to apply a DSC configuration.
I'd like to use the Script Arguments field to pass in parameters from TFS to the DSC script.
My script looks like this:
Param(
[string]$data
)
configuration ApplyConfig
{
Script Backup {
SetScript = {
#do some stuff with $data
}
TestScript = {
Write-Output "Print param"
Write-Output $data
return $true
}
GetScript = {
return #{"Test" = "test data"}
}
}
}
ApplyConfig
The Script Arguments field contains this:
-Destination "$(ApplicationPath)"
However, at this point, $data seems to always be null. How can I get the argument defined in the Script Arguments field into my Script Resource?
When you reference $data in the TestScript you need the 'using' scope:
TestScript = {
Write-Output "Print param"
Write-Output $using:data
return $true
}
The TestScript executes on a different PowerShell context; 'using' allows you to copy the value of $data across those contexts.
My recommendation for flexibility is to declare a configuration hash table in your DSC script and pass parameters in to configure it. My Continuous Delivery with TFS / VSTS – Server Configuration and Application Deployment with Release Management blog post has a complete walkthrough of how to use DSC and Release Management in TFS 2015 Update 2.
Getting the parameters in then becomes a case of declaring your parameters as follows:
param(
[Parameter(Position=1)]
[string]$myFirstParameter,
[Parameter(Position=2)]
[string]$mySecondParameter
)
and then passing in the value in either directly:
Script Arguments field contains 'myFirstValue' 'mySecondValue'
or better as variables:
Script Arguments field contains $(myFirstValue) $(mySecondValue)

Index is out of range powershell

I have a script that builds a GUI with a list of printers that will be selected by the user.
These printers are also on a CSV file built like this :
Computer (name of the printer); IP
xxxx;x.x.x.x
I want to collect all the selected values in an array named x
Then I want to take every entry in the CSV that corresponds to the selected item and put it in another array named y
Finally I export the y array into a new CSV that will be used to install the printers on the domain.
I tried to go straight from second step to last step but i couldn't.
Here is the part of the code :
$OKButton.Add_Click({
foreach ($objItem in $objListbox.SelectedItems)
{$x += $objItem}
y=#()
for ($i=0; $i -lt $x.length; $i++)
{
$y[$i]=Import-Csv C:\Users\Administrateur\Desktop\Classeur33.csv | Where-Object {$_.Computer -eq $x[$i]}
}
$y > C:\Users\Administrateur\Desktop\allezjoue.csv
I've tried to do it with a 3 values x array in another script and it worked fine, but I really need to keep the listbox that allows the user to select the printers he wants.
Powershell always returns me "Index out of range"
I tried to put "$y=$x" so they have the same range, but when I do this it returns that I can't index in an object which has "System.string" type.
This is PowerShell and very object oriented. Use the objects and collections at hand.
Decriptive variable names are your friend.
$objListbox.SelectedItems is already a collection of objects.
Put it in a variable and loop through it with Foreach-Object aka foreach.
Import-CSV returns a collection of objects.
$Selection = $ObjListbox.SelectedItems
$printers = Import-CSV 'C:\Users\Administrateur\Desktop\Classeur33.csv'
foreach ($chosen in $Selection) {
$printers = $printers | where-object { $_.Computer -eq $Chosen.Name }
}
$printers | Export-CSV 'C:\Users\Administrateur\Desktop\allezjoue.csv' -NoTypeInformation
$Chosen.Name should be edited to conform with whatever objects you get in $Selection. You can test this by $ObjListbox.SelectedItems | Get-Member and examining the members for a property with the name of the item selected, then assuming the names match what's in your CSV, you should be good.
(bonus note) Storing data in and running as local admin is bad practice, even on your home lab. Your mistakes will have the power of local admin, and your users will not be able to run the scripts since the source/results files are in admin's desktop.

Copying an SMO collection into an array in Powershell

I've written a Powershell script which will compare two databases and come up with a list of objects in one of the databases to remove. I put those items (as a customized object with a name and schema) into an array.
In the next step of my script I iterate through the objects in the database and see if they match an object in my array. If I find a match then I go ahead and drop the object from my database. The problem that I ran into though, was that if I try to drop the object then the collection through which I'm iterating gets changed and I get an error message that the collection changed and IEnumerable won't work when that happens.
I tried to make a copy of collection, but I can't seem to stuff it into an array using the CopyTo method. Any suggestions?
My current code is below. When I run this the array $sprocs is empty.
function DropSQLObjects
{
param([object]$database, [object]$objectsToDrop)
$sprocs = #()
$database.StoredProcedures.CopyTo($sprocs, 0)
# If I do a $sprocs | out-host I see that the array is still empty
foreach ($objectToDrop in $objectsToDrop)
{
foreach ($sproc in $sprocs)
{
if ($sproc.Name -eq $objectToDrop.Name -and $sproc.Schema -eq $objectToDrop.Schema)
{
$sproc.Drop()
LogToSQL $database "Dropped Stored Procedure: $($objectToDrop.Schema).$($objectToDrop.Name)"
}
}
}
}
I'm adding this as an answer in case anyone else has need of this in the future. It turns out that I was really making things harder than they needed to be. Since I was using Powershell, the "where" function was better than iterating through the stored procedures.
Here's the code which solved my issue:
function DropSQLObjects
{
param([object]$database, [object]$objectsToDrop)
foreach ($objectToDrop in $objectsToDrop)
{
if ($database.StoredProcedures.Contains($objectToDrop.Name, $objectToDrop.Schema))
{
$sproc = $database.StoredProcedures | where {$_.Schema -eq $objectToDrop.Schema -and $_.Name -eq $objectToDrop.Name}
$sproc.Drop()
LogToSQL $database "Dropped Stored Procedure: $($objectToDrop.Schema).$($objectToDrop.Name)"
}
}
}
In actuality, I also have code to go against UserDefinedFunctions, but the code is mostly a cut-and-paste from the StoredProcedures portion.

Resources