Powershell V2.0 SHA1 Calculation - loops

I posted a question on Monday about how I can calculate SHA1 hash within powershell.
The resultant code was as follows:
$file = 'C:\Zip\File.zip'
$sha1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
[System.BitConverter]::ToString( $sha1.ComputeHash([System.IO.File]::ReadAllBytes($file)))
This code works perfectly and does what I need it to do, but at the moment I have to specify the file I want the SHA1 hash to be calculated for. Is there any way I can get it to calculate the hashes for each files within that 'zip' folder.
I've been attempting to do this for the last two days by using a loop etc, and I just haven't moved anywhere. It doesn't help that my PowerShell skills are appalling.
Any help is greatly appreciated.

So basically, you just want to
Get a list of all files within C:\Zip\ and
Apply that snippet of code to each of them.
You want to look into using ForEach loops (can also be expressed in shortened-form, for use with piping, like so: $var | % {<#do this for each #>}).
You can read more about ForEach loops on all over the internet - two sites I have great luck with for researching PowerShell topics are TechNet and the Hey, Scripting Guy! Blog. I'd suggest checking them out, and reading up.
This should be a good start:
$zips = Get-ChildItem 'C:\Zip\' -Filter *.zip
$sha1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
foreach ($file in $zips) {
$return = "" | Select Name, Hash
$return.name = $file.Name
$return.hash = [System.BitConverter]::ToString($sha1.ComputeHash([System.IO.File]::ReadAllBytes($file.FullName)))
Write-Output $return
}

Related

How do I access properties of nested arrays in powershell within foreach loops?

I'm trying to write a script that checks whether a group of registry keys are set individually and then either sets them or modifies them conditional on thier current state.
The keys that I want to check/set contain a mix of Strings and DWORDs
I want to loop through an array that contains the key I want to set paired with the value.
I've tried as a hashtable/splatting but the input to Get-ItemProperty fails because of the value parameter so I tried basic arrays instead with no luck.
They are all at the same registry path but I was attempting to do something similar to this:
$Path = "HKLM:\Software\path\to\keys"
$Properties = (
('key', value),
('key2', value2),
('key3', 'value3')
)
foreach ($item in $Properties){
$exist = Get-ItemProperty -Path $Path -name $item[0]
if ($exist) {
Set-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
} else {
New-ItemProperty -Path $Path -Name $item[0] -Value $item[1]
}
}
But no matter what I've tried I cannot retrieve the individual elements of the inner arrays.
I realize I could probably do this long-form and just do it line by line rather than attempting to iterate through, but this is definitely a more elegant way, and would be a great template if I need to do something similar in the future.
Holy moly. Nothing breaks you out of a rut like posting the question to an online forum.
The .GetValue() method is what I needed, not the raw index number.
...So $Item.GetValue(0) for the key name and $Item.GetValue(1) for the value.
Feel silly answering my own question but hopefully it helps someone else!

Attempting to use two arrays as variables to search file names (powershell)

I need to search multiple (named) folders on multiple servers for files matching a specific date and copy those files to a local folder using Powershell. The number of folders are not the same size arrays as the number of servers. I.e. I need \server1\interfacefolders\folder1, \server1\interfacefolders\folder2, \server2\interfacefolders\folder1, \server2\interfacefolders\folder2, etc.
I have the following set up as arrays/variables preparing for this, I thought "nested" foreach loops would work, but it bombs out...any ideas how to get started on this?:
[string[]]$ProdServerArray = "server1", "server2", "server3"
[string[]]$InterfaceArray = "folder1", "folder2" "folder3" do {
$date = Read-host "Enter date (MM/DD/YYYY) : " } while ($date -as [datetime] -isnot [datetime])
$date = $date -as [datetime]
$destination = new-item c:\GetFilesResults\$($date.toshortdatestring().replace("/","-")) -type directory
$path = foreach ($ProdServer in $ProdServerArray)
{
$folder = foreach ($Interface in $InterfaceArray)
{
$file = "\\$path\InterfaceFolder\$folder\*"
if ("$file".LastWriteTime -gt $date.date)
{
Copy-Item -Path $file.fullname -Destination $destination
}
}
}
to build the full folder names from those two arrays, you can use two nested foreach loops. once you have the values, you can build the paths via something like the -f string format operator.
i left out the rest of your code since it does not appear to pertain to the question you asked. [grin]
$ProdServerArray = 'serverAAA', 'serverBbBbBb', 'server_CCC'
$InterfaceArray = 'folder1', 'folder2', 'folder3', 'folder666'
foreach ($PSA_Item in $ProdServerArray)
{
foreach ($IA_Item in $InterfaceArray)
{
'\\{0}\InterfaceFolders\{1}' -f $PSA_Item, $IA_Item
}
'=' * 30
}
output ...
\\serverAAA\InterfaceFolders\folder1
\\serverAAA\InterfaceFolders\folder2
\\serverAAA\InterfaceFolders\folder3
\\serverAAA\InterfaceFolders\folder666
==============================
\\serverBbBbBb\InterfaceFolders\folder1
\\serverBbBbBb\InterfaceFolders\folder2
\\serverBbBbBb\InterfaceFolders\folder3
\\serverBbBbBb\InterfaceFolders\folder666
==============================
\\server_CCC\InterfaceFolders\folder1
\\server_CCC\InterfaceFolders\folder2
\\server_CCC\InterfaceFolders\folder3
\\server_CCC\InterfaceFolders\folder666
==============================
First of all, you are missing a comma in the line:
[string[]]$InterfaceArray = "folder1", "folder2" "folder3"
Additionally, as far as I can tell, your do while loop doesn't appear to be accomplishing anything, as the only time this will ever be true is if the time 12:00:00AM exactly on the date specified. No matter what date you input in the format (MM/DD/YYYY), they will not be equal unless the case I said above.
Since you are searching multiple servers, Invoke-Command is your friend, as a foreach loop will act in series, while this will work in parallel. It will send out the search command to each server simultaneously.
I am not quite sure exactly what you are trying to do, so I did not fill in the actual search code(seen below), but the part i have left blank would be where you enter what filename/filename schema you are looking for. If you provide more clarity I can assist further if needed.
(Note: $filepath, although self explanatory, is the file paths you wish to search. You can generate them in a way similar to the one provided by Lee_Dailey. I'd recommend removing the divider lines and saving the paths generated to a String System.Array Object)
Invoke-Command -ComputerName $ProdServerArray -ScriptBlock {Get-Childitem –Path $filepath -Recurse -ErrorAction SilentlyContinue |where {<your code here>}}

Replace not working when Looping through an Array

Working on a project and I have a need to replace a 'path prefix' with a different prefix. Automating the copying of files and then I will do using those files locally. Rather than making new variables I figured I would re-purpose old ones I would not need after the copy is complete. Ran into an issue with doing a replace when looping through the Array. I was able to find a solution for this particular scenario but would like to understand why my loops were not working
No errors are shown to give me an indication why it is not working.
Any help on understanding why the replace isn't working when looping would be helpful
Sample code of how I am building paths
[string]$Root_Path = "\\Main_Blah_Path\"
[string]$Software = $Root_Path + "Software\"
[string]$Adobe_Reader = $Software + "Adobe_Reader\"
[string]$Firefox = $Software + "Firefox\"
[string]$Google_Chrome = $Software + "Google_Chrome\"
[System.Collections.ArrayList]$List_Of_Software = #(
$Adobe_Reader
$Firefox
$Google_Chrome
)
Example of the ways I have done the replacement. These work and will write the desired output to the console
foreach ($Path in $List_Of_Software) {
$Path -replace '\\\\Main_Blah_Path\\','C:\Folder\'
}
$List_Of_Software | ForEach-Object {$_ -replace '\\\\Main_Blah_Path\\','C:\Folder\'}
Example of failures I am having. I cannot replace and save the data into itself to do a replacement. I couldn't get .replace to work at all
foreach ($Path in $List_Of_Software) {
$Path = $Path -replace '\\\\Main_Blah_Path\\','C:\Folder\'
}
$List_Of_Software | ForEach-Object {$_ = $_ -replace '\\\\Main_Blah_Path\\','C:\Folder\'}
foreach ($Path in $List_Of_Software) {
$Path.Replace('\\\\Main_Blah_Path\\','C:\Folder\')
}
Solution I am using for my current scenario but I can foresee a few things in my future where this method may not be a viable option
$List_Of_Software = $List_Of_Software -replace '\\\\Main_Blah_Path\\','C:\Folder\'
You're almost there, you just need to assign the results of the replacement back to the variable when you're looping the array. Try:
$List_of_software = $List_Of_Software |
ForEach-Object {$_ -replace '\\\\Main_Blah_Path\\','C:\Folder\'}

Powershell: Comparing a value between two arrays, and extracting a related value

so here is what I'm trying to accomplish.
I have a form for a new starter, New Starter Form.csv, that has the following headers and information:
firstname,lastname,teamname,startdate
Joe,Bloggs,Security Admin,01/01/18
I have a different csv called Team List.csv, that has the following headers and information:
teamlead,teamname,resgroup
A B,Marketing,RESMARKETING01G
C D,Product,RESPRODUCT01G
E F,Advertising,RESADVERTISING01G
G H,Security Admin,RESSECURITYADMIN01G
I want to import both CSV files into Powershell, run a comparisson that takes the team name from the New Starter Form, and checks if there are any matches in the Team List, and if so, add the relevant RES group to the new starter in AD.
Currently, I can import them, compare them, find a match, and find an index number for the record, but I'm struggling to the take this index number, and use it to get the relevant RES group. So far the code looks like this:
$teamlist = import-csv "\\location\Team List.csv"
$newstarter = import-csv "\\otherlocation\New Starter Form.csv"
[string]$teamname = Compare-Object -includeequal -excludedifferent -PassThru $newstarter.teamname $teamlist.teamname
$teamname
[array]::indexof($teamlist,$teamname)
And running that, provides us with this in the console, showing that we can indeed see the match, and that the matching record is the last (-1) one:
PS C:\WINDOWS\system32> $teamlist = import-csv "\\location\Team List.csv"
$newstarter = import-csv "\\otherlocation\New Starter Form.csv"
[string]$teamname = Compare-Object -includeequal -excludedifferent -PassThru $newstarter.teamname $teamlist.teamname
$teamname
[array]::indexof($teamlist,$teamname)
Security Administration
-1
I've not got a lot of experience with Powershell, and my coding knowledge is pretty limited overall, but I'm used to the concept that I can save the index value as a variable, and then I could call that variable back to do something like $teamlist.resgroup[VARIABLE HERE].
But if I try and declare a new variable before [array]::indexof($teamlist,$teamname), Powershell isn't happy.
Whilst I've not looked into it, I believe a possible alternative could be to add in a huge switch statement, but I may be looking at having 100+ teams overall, and I'd like to avoid inefficient code wherever I can. Am I missing something obvious though? Is there a better way (Or even just a functioning way would be great!) that this could work?
Any help you can provide would be greatly appreciated!
$teamlist = import-csv "\\location\Team List.csv"
$newstarter = import-csv "\\otherlocation\New Starter Form.csv"
# get a single new starter
$person = $newstarter | Where-Object { $_.firstname -eq 'Joe' -and $_.lastname -eq 'Bloggs' }
# get the new starters team
$team = $teamlist | Where-Object { $_.teamname -eq $person.teamname }
# get the new starters resource group
$resgroup = $team.resgroup
# use the resource group - this simply writes it to the console
Write-Host $resgroup
The code above will:
import your two csvs
grab a single new starter from your new starter csv, based on first &
last name
grab the team & resource group for that new starter from the team list
display the resgroup (this is where you will need to use to populate AD)

Powershell - How to select a block of text from a text based log file that has a time stamp in the log file entry

Sorry if the Title isn't very clear, I think part of my issue is I can't think of a way to clearly describe what I am trying to acheive, which has not helped me in 'Googling' anything that may be of help.
I am trying to create a generic script / function that I can pass 2 time values, a start time and an end time that will output all log file entries between these 2 time stamps. This will become the core part of a script to extract very small time spans of log file entries from logfiles that often exceed 1GB a day and are very difficult to manipulate in file viewers or editors efficiently and therefore take significant time to extract relevant dta from, for the purpose of providing developers with data to aid investigations into problems.
The log files are of differing formats but most of these follow the general format below:
2418 22:19:58: CTracker::ReadAvlPackets - ENTER ...
b18 22:19:58: SICInterfaceApp::ListenerRead: Qtype < 15> Subtype < 27> #8597208 From <¸i` >
b18 22:19:58: CTracker::OnListenerRead - PktPrc: 0x00000001
2418 22:20:00: cAvlComIMobile::GetData - ErrSev: 0x00000000
2418 22:20:02: cAvlComIMobile::GetData - ErrSev: 0x00000000
I have created a basic function:
function Get-LogExtract ($StartTime){
$Log = "path-to-log-file"
Get-Content $Log |
ForEach-Object {
$parts = $_ -split ' '
if ($parts[1] -eq $StartTime) {
$_
}
}
}
which if I run it using the following command line
Get-LogExtract 22:19:58:
it will output only the logfile lines that have the timestamp of 22:19:58: in the line, which is fine, it was what I initally wanted to acheive, but since this point the requirement has expanded.
What I now want to be able to do is to provide a start time and an end time and have all lines between these output.
I have tried to implement a do while loop to do this but was obvioulsy not getting anywhere as I couldn't acheive the goal I am trying to reach.
So I have a few questions.
1. is there a better way to tackle what I am trying to acheive?
2. if not, how can I implement a loop to acheive my goal?
Updated Code:
OK, so here's a code segment tweaked as advised by PSGuy below, which works a treat with logfiles that have a time stamp entry on every line.:
function Get-LogExtract ($StartTime, $EndTime) {
$Log = "path-to-log-file"
Get-Content $Log |
ForEach {
$parts = $_ -split ' '
if ($parts[1] -ge $StartTime -and $parts[1] -le $EndTime) {
$_
}
}
}
There are a few things you can do here. The first is, if you want a true date-time object, then you can run the following static method of the System.DateTime class:
[DateTime]::Parse($Time) # 22:19:58 == Friday, September 19, 2014 10:19:58 PM
You can also do string comparisons, because any comparison operator against a string will evaluate each character by Char value, so "22:19:59" -gt "22:19:58" is true.
I think that converting to a DateTime format will be easier for you, however, doing a simple string comparison will work. Something like:
If $line[$I] -ge "22:19:59" -and $line[$I] -le "23:59:59" will theoretically work in your proposed script / function. I would also recommend doing a for loop to step through each line of the log file, however, it's not required. I'm not a big fan of the foreach-object cmdlet if I'm not using actual objects in the pipleline, where I can call a static method against the object itself. Here, it's just simple string work, and I would personally use a for loop, but it's all a matter of preference. The for construct would look like this:
$logFile = Get-Content myLogFile.log # or whatever file format, it's an input arg anyway
for ($I = 0; $I -le $logFile.GetUpperBound(""); $I++)
{
$logFile[$I] # reads the line; you can assign a variable at this point
}
A foreach loop would also work. I personally like using for, but foreach is effectively the same thing. The foreach equivalent is:
foreach ($line in $logFile)
{
$line # perform whatever split operations you want to perform
}
Please let us know if it works!

Resources