Comparing two text files and only keeping unique values - powershell

All,
I am VERY new to powershell and am attempting to write a script and have run into an issue.
I currently have two text files. For argument sake the first can be called required.txt and the second can be called exist.txt.
I have a script which queries a server and determines a list of all existing groups and writes these to a text file. At the same time the customer has a list of new groups they wish to create. I want to compare the new list (required.txt) with the existing list (exist.txt) and anything which doesn't exist be piped out to a new text file which is then picked up and imported using another process.
I've got the scripting done to gather the list from the server I just need to know how to do the comparison between the existing and required.
Any suggestions welcome.
Richard

you don't have to use as much variables :
$FinalGroups=Compare-Object (get-content .\required.txt) (get-content .\existing.txt) |
where {$_.SideIndicator -eq "<="} |
select -ExpandProperty inputObject |
sort

Related

Export two or three azure cmdlets into the same .csv in powershell

First thank for those who stop here and try to help !
It's my first time touching powershell.
I search a way to list and export unattached or orphaned resources in Azure (IPs, RGs, Disks ...)
Get-AzDisk | Select-Object -Property Name,AttachedTo,Location | Export-Csv -path C:\Users\David\test.csv ";" -NoTypeInformation
With this I only have Azdisk on a single .csv file but how can I add per example "Get-AzPublicIpAddress" into the same .csv ?
You want to use a script to do this, not a one-liner.
First, create a function to build a custom object with as many properties as you need, using as many commandlets as you wish. Get this to work for one object.
Learn about PSCustomObject.
Learn about functions.
Then build a loop that runs the function against all of the objects creating an array holding items which are instances of your custom object.
At the end of the loop, output to CSV.
Objects, especially custom objects, are one of the things that sets Powershell apart.

What does the vertical line character mean in powershell?

I am using a script from here
http://blog.kuppens-switsers.net/sharepoint/finding-cewps-with-script-in-your-sharepoint-sites/
And there is a specific part of the script that I don't understand. In this part
# Libraries and lists have views and forms which can contain webparts... let's get them also
$lists = $web.GetListsOfType("DocumentLibrary") | ? {$_.IsCatalog -eq $false}
What exactly does | ? {$_.IsCatalog -eq $false} mean? And if possible does anyone know why this person chose to only check document libraries?
What the point of the script is, it scans all content editor web parts and checks if their contents have any script tags.
Thanks
PowerShell heavily relies on the concept of the pipeline. You execute a command which returns a collection of objects and you pipe those into another command which does something with it.
| also known as the pipe character or pipe operator is used to connect the various parts of the pipeline.
In your case, you get all DocumentLibraries in a SharePoint website and pipe (pass) them into a Where-Object cmdlet (? for short) to apply a filter. The result is then assign to a variable.

Rename Files with Index(Excel)

Anyone have any ideas on how to rename files by finding an association with an index file?
I have a file/folder structure like the following:
Folder name = "Doe, John EO11-123"
Several files under this folder
The index file(MS Excel) has several columns. It contains the names in 2 columns(First and Last). It also has a column containing the number EO11-123.
What I would like to do is write maybe a script to look at the folder names in a directory, compare/find an associated value in the index file(like that number EO11-123) and then rename all the files under the folder using a 4th column value in the index.
So,
Folder name = "Doe, John EO11-123", index column1 contains same value "EO11-123", use column2 value "111111_000000" and rename all the files under that directory folder to "111111_000000_0", "111111_000000_1", "111111_000000_2" and so on.
This possible with powershell or vbscript?
Ok, I'll answer your questions in your comment first. Importing the data into PowerShell allows you to make an array in powershell that you can match against, or better yet make a HashTable to reference for your renaming purposes. I'll get into that later, but it's way better than trying to have PowerShell talk to Excel and use Excel's search functions because this way it's all in PowerShell and there's no third party application dependencies. As for importing, that script is a function that you can load into your current session, so you run that function and it will automatically take care of the import for you (it opens Excel, then opens the XLS(x) file, saves it as a temp CSV file, closes Excel, imports that CSV file into PowerShell, and then deletes the temp file).
Now, you did not state what your XLS file looks like, so I'm going to assume it's got a header row, and looks something like this:
FirstName | Last Name | Identifier | FileCode
Joe | Shmoe | XA22-573 | JS573
John | Doe | EO11-123 | JD123
If that's not your format, you'll need to either adapt my code, or your file, or both.
So, how do we do this? First, download, save, and if needed unblock the script to Import-XLS. Then we will dot source that file to load the function into the current PowerShell session. Once we have the function we will run it and assign the results to a variable. Then we can make an empty hashtable, and for each record in the imported array create an entry in the hashtable where the 'Identifier' property (in your example above that would be the one that has the value "EO11-123" in it), make that the Key, then make the entire record the value. So, so far we have this:
#Load function into current session
. C:\Path\To\Import-XLS.ps1
$RefArray = Import-XLS C:\Path\To\file.xls
$RefHash = #{}
$RefArray | ForEach( $RefHash.Add($_.Identifier, $_)}
Now you should be able to reference the identifier to access any of the properties for the associated record such as:
PS C:\> $RefHash['EO11-123'].FileCode
JD123
Now, we just need to extract that name from the folder, and rename all the files in it. Pretty straight forward from here.
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "(?<= )(\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $RefHash['$($Matches[1])'].FileCode
For($i = 1;$i -lt $files.count;$i++){
$Files[$i] | Rename-Item -New "$NewName_$i"
}
}
Edit: Ok, let's break down the rename process here. It is a lot of piping here, so I'll try and take it step by step. First off we have Get-ChildItem that gets a list of folders for the path you specify. That part's straight forward enough. Then it pipes to a Where statement, that filters the results checking each one's name to see if it matches the Regular Expression "(?<= )(\S+)$". If you are unfamiliar with how regular expressions work you can see a fairly good breakdown of it at https://regex101.com/r/zW8sW1/1. What that does is matches any folders that have more than one "word" in the name, and captures the last "word". It saves that in the automatic variable $Matches, and since it captured text, that gets assigned to $Matches[1]. Now the code breaks down here because your CSV isn't laid out like I had assumed, and you want the files named differently. We'll have to make some adjustments on the fly.
So, those folder that pass the filter will get piped into a ForEach loop (which I had a typo in previously and had a ( instead of {, that's fixed now). So for each of those folders it starts off by getting a list of files within that folder and assigning them to the variable $Files. It also sets up the $NewName variable, but since you don't have a column in your CSV named 'FileCode' that line won't work for you. It uses the $Matches automatic variable that I mentioned earlier to reference the hashtable that we setup with all of the Identifier codes, and then looks at a property of that specific record to setup the new name to assign to files. Since what you want and what I assumed are different, and your CSV has different properties we'll re-work both the previous Where statement, and this line a little bit. Here's how that bit of the script will now read:
Get-ChildItem c:\Path\to\Folders -directory | Where{$_.Name -match "^(.+?), .*? (\S+)$"}|
ForEach{
$Files = Get-ChildItem $_.FullName
$NewName = $Matches[2] + "_" + $Matches[1]
That now matches the folder name in the Where statement and captures 2 things. The first thing it grabs is everything at the beginning of the name before the comma. Then it skips everything until it gets tho the last piece of text at the end of the name and captures everything after the last space. New breakdown on RegEx101: https://regex101.com/r/zW8sW1/2
So you want the ID_LName, which can be gotten from the folder name, there's really no need to even use your CSV file at this point I don't think. We build the new name of the files based off the automatic $Matches variable using the second capture group and the first capture group and putting an underscore between them. Then we just iterate through the files with a For loop basing it off how many files were found. So we start with the first file in the array $Files (record 0), add that to the $NewName with an underscore, and use that to rename the file.

Getting most recent objects in a folder excluding certain strings

I have folders that contain files, for the sake of the question, named as follows:
a-001.txt
a-002.txt
b-001.txt
b-002.txt
d-001.txt
d-002.txt
Now I am using PowerShell to initially order these files so that the top of the list is the most recent file in the folder:
d-002.txt
b-002.txt
a-001.txt
a-002.txt
b-001.txt
d-001.txt
EDIT: I then store the top X recent number of files into a variable. However, I want to ignore anything that starts with A if I already have one that begins with A in my array but still ensure I end up with X files which are the most recent. I.e. from above, I would want to end up with below if X was 4.
d-002.txt
b-002.txt
a-001.txt
b-001.txt
This is a simple example, the folders I am dealing with contain 1000s of files - with more complex naming conventions but the logic is the same. How can I handle this in PowerShell?
Removing the logic for any other Sort-Object and Select-Object criteria as you already have that addressed I present the following.
Get-ChildItem $somePath | Select-Object *,#{Label="Prefix";Expression={(($_.Name) -Split "-",2)[0]}} | Group-Object prefix | ForEach-Object{
$_.Group | Select-Object -First 1 -Property Fullname
}
What happens here is that we add a property to the output of Get-ChildItem called "Prefix". Now, your criteria might be more complicated but given the sample I assumed the files were being grouped by the contents of the name before the first "-". So we take every file name and build its prefix based on that. The magic comes from Group-Object which will group all items and then we just select the first one. In your case that would be the newest X amount. Let me know if you are having trouble integrating this.
Aside from grouping logic any sorting an what not would need to exists before the Select-Object in our example above.
FYI for other readers
There were issues with OP's actual data since the above code didnt work exactly. We worked it out in chat and using the same logic we able to address the OPs concern. The test data in the question and my answer work as intended.

PowerShell: Compare CSV to AD

I'm fairly new to PowerShell and I'm posting this on many forums but I've had success with programming assistance from here before and although this isn't strictly programming, I was hoping someone might know the answer.
My organization had about 5,300 users we needed to disable for a client. Someone decided the best use of our time was have people go through AD and disable them one at a time. Soon as I got wind of this I put a stop to it and used PowerShell to take the CSV list we already had, and ran a cmdlet to disable all of the users in the CSV list.
This appeared to work, but I wanted to run a comparison. I want to compare the users from the CSV file, to the users in AD, and confirm that they are all disabled without having to check all 5300 individually. We checked about 60 random ones to verify my run worked, but I want to make sure none slipped through the cracks.
I've tried a couple scripts and I've tried some variations of cmdlets. None of the scripts I tried even worked, spammed with errors. When I try to run a search of AD either using get-content or import-CSV from the csv file, when I export its giving me about 7600 disabled users (if I search by disabled). There were only 5300 users in total, so it must be giving me all of the disabled users in AD. Other cmdlets i've run appear to do the same thing, its exporting an entire AD list instead of just comparing against my CSV file.
Any assistance anyone can provide would be helpful.
Without knowing the exact structure of your CSV I'm going to assuming it is as such:
"CN=","OU=","DC="
"JSmith","Accounting","Foo.com"
"BAnderson","HR","Foo.com"
"JAustin","IT","Foo.com"
That said, if your first field actually has CN= included (i.e. "CN=JSmith","OU=Accounting","Foo.com") you will want to trim that with .TrimStart("CN=").
$ToRemove = Import-CSV UserList.csv
$UserList=#()
ForEach($User in $ToRemove){
$Temp = ""|Select "User","Disabled"
$Temp.User = $User.'CN='
If((Get-aduser $Temp.User -Prop Enabled).Enabled){$Temp.Disabled='False'}else{$Temp.Disabled='True'}
$UserList+=$Temp}
$UserList|?{$_.Disabled -eq 'False'}
That loads the CSV into a variable, runs each listing through a loop that checks the 'CN=' property, creates a custom object for each user containing just their name and if they are disabled, and then adds that object to an array for ease of use later. In the end you are left with $UserList that lists everybody in the original CSV and if they are disabled. You can output it to a file, filter it for just those that are still enabled, or whatever you want. As noted before if your CSV actually has CN=JSmith for each line you will want to update line 5 to look as such:
$Temp.User = $User.'CN='.TrimStart("CN=")
If you don't have any headers in the CSV file you may want to inject them. Just put a line at the top that looks like:
CN=,OU=,DC=
Or, if you have varying OU depths you may be better off doing a GC and then running each line through a split, taking the first part, trimming the CN= off the beginning, and checking to see if they are disabled like:
GC SomeFile.CSV||%{$_.split(",")[0].trimstart("CN=")|%{If((get-aduser $_ -prop enabled).enabled){"$_ is Enabled"}else{"$_ is Disabled"}}}
Assuming your CSV has a column called DN you can run the following which will return all users from your spreadsheet which are enabled
import-csv YourUsersCSV.csv | Get-ADUser -Filter
{DistinguishedName -eq $_.DN } |
where{$_.enabled -eq $true} |
Select-Object -Property DistinguishedName,samaccountname,enabled