Copying an SMO collection into an array in Powershell - arrays

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.

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 - List View Validation

I'm trying to build a script to ease the access mirroring.
I'm comparing access groups of two users and then displaying only those that the second user is missing.
I have two List Views:
First - Filled in with access groups
Second - Empty view where I want to copy a group name.
I managed to copy the name from the first to second list view although I'm struggling with creating a validation.
Here's the code I have:
Function Add-ItemListView($Item){
if($firstItem -eq $true){
$userGroupsListView.Items.Add($Item)
$global:firstItem = $false
}
else{
foreach($i in $userGroupsListView.Items){
$itemText = $i.Name
if ($itemText -ne $Item){
$userGroupsListView.Items.Add($Item)
}
}
}
}
I think it's actually checking if there already exists an item with the same name, but I'm getting an error message:
Collection was modified; enumeration may not execute.
I will appreciate every direction or tip.
Also is there any documentation for ListView in Powershell?
I could find a bunch of helpful guides and documentation but for C#

Count occurrences of something in an array inside a foreach loop

I have a product CSV file that I have imported into $products
If something occurs more than once with the same name I want to populate the ParentSKU field, otherwise leave it blank.
Excuse the pseudocode but I'm imagining something like this:
foreach ($item in $products) {
if ($item.name.count -gt 1) {
$item.ParentSKU = $item.name }
else { } # do nothing
}
$item.name.count isn't correct but I hope my thinking is on the right track?
Many thanks for any advice
Powershell Object lists aren't smart enough to know that there's multiple of any one item, so you're going to have to iterate through (manually or otherwise) to find whether there's multiples here.
Since you're going to be making modifications to any duplicates, it may make sense to loop through and find duplicates manually, but it doesn't really follow the "powershell" philosophy / approach.
If you want to use powershell's built-in & powerful piping features, you might try a solution like this, which would grab all the PSObjects with duplicates using Where-Object, then sets the values for all those PSObjects.
$products |
Group-Object -Property Name |
Where-Object -FilterScript {
$_.Count -gt 1
} |
Select-Object -ExpandProperty Group |
Foreach-Object { $_.ParentSKU = $_.Name }
Since everything is passed by reference, your $products object will have the modified values!

Assistance in building array of Exchange powershell output

I am trying to build a script that can get a list of members of a distribution group recursively, and then output the data in a way that is manipulable.
The below code works fine so far, but only outputs a final list of recipients on the screen, with two columns (Name, RecipientType). I have tried a couple of ways to instead output the results to a variable or array in order to manipulate it, however none of them have worked.
What would be the best way to take the $Member variable, and add it to another variable or array?
function Get-DistributionGroupMemberRecursive ($Identity) {
$GroupMembers = Get-DistributionGroupMember -Identity $Identity
foreach ($Member in $GroupMembers) {
if($Member.RecipientType -like "*Group") {
Get-DistributionGroupMemberRecursive -Identity $Member.Identity
}
else {
$Member
}
}
}
Get-DistributionGroupMemberRecursive -Identity some_address#domain

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.

Resources