Powershell: Logging foreach changes - powershell

I have put together a script inspired from a number of sources. The purpose of the powershell script is to scan a directory for files (.SQL), copy all of it to a new directory (retain the original), and scan each file against a list file (CSV format - containing 2 columns: OldValue,NewValue), and replace any strings that matches. What works: moving, modifying, log creation.
What doesn't work:
Recording in the .log for the changes made by the script.
Sample usage: .\ConvertSQL.ps1 -List .\EVar.csv -Files \SQLFiles\Rel_1
Param (
[String]$List = "*.csv",
[String]$Files = "*.sql"
)
function Get-TimeStamp {
return "[{0:dd/MM/yyyy} {0:HH:mm:ss}]" -f (Get-Date)
}
$CustomFiles = "$Files\CUSTOMISED"
IF (-Not (Test-Path $CustomFiles))
{
MD -Path $CustomFiles
}
Copy-Item "$Files\*.sql" -Recurse -Destination "$CustomFiles"
$ReplacementList = Import-Csv $List;
Get-ChildItem $CustomFiles |
ForEach-Object {
$LogFile = "$CustomFiles\$_.$(Get-Date -Format dd_MM_yyyy).log"
Write-Output "$_ has been modified on $(Get-TimeStamp)." | Out-File "$LogFile"
$Content = Get-Content -Path $_.FullName;
foreach ($ReplacementItem in $ReplacementList)
{
$Content = $Content.Replace($ReplacementItem.OldValue, $ReplacementItem.NewValue)
}
Set-Content -Path $_.FullName -Value $Content
}
Thank you very much.
Edit: I've cleaned up a bit and removed my test logging files.
Here's the snippet of code that I've been testing with little success. I put the following right under $Content= Content.Replace($ReplacementItem.OldValue, $ReplacementItem.NewValue)
if ( $_.FullName -like '*TEST*' ) {
"This is a test." | Add-Content $LogFile
}
I've also tried to pipe out the Set-Content using Out-File. The outputs I end up with are either a full copy of the contents of my CSV file or the SQL file itself. I'll continue reading up on different methods. I simply want to, out of hundreds to a thousand or so lines, to be able to identify what variables in the SQL has been changed.

Instead of piping output to Add-Content, pipe the log output to: Out-File -Append
Edit: compare the content using the Compare-Object cmdlet and evaluate it's ouput to identify where the content in each string object differs.

Related

Powershell: ForEach Copy-Item doesn't rename properly when retrieving data from array

I am pretty new to PowerShell and I need some help. I have a .bat file that I want to copy as many times as there are usernames in my array and then also rename at the same time. This is because the code in the .bat file remains the same, but for it to work on the client PC it has to have the username as a prefix in the filename.
This is the code that I have tried:
$usernames = Import-Csv C:\Users\Admin\Desktop\usernames.csv
$file = Get-ChildItem -Path 'C:\Users\Admin\Desktop\generatedbat\' -Recurse
foreach ($username in $usernames)
{
ForEach-Object {Copy-Item $file.FullName ('C:\Users\Admin\Desktop\generatedbat\' + $username + $File.BaseName + ".bat")}
}
This copies everything and it kind of works but I have one problem.
Instead of having this filename: JohnR-VPNNEW_up.bat
I get this: #{Username=JohnR}-VPNNEW_up.bat
Any help? Thanks!
So you have one .bat file C:\Users\Admin\Desktop\generatedbat\VPNNEW_up.bat you want to copy to the same directory with new names taken from the usernames.csv --> Username column.
Then try
# get an array of just the UserNames column in the csv file
$usernames = (Import-Csv -Path 'C:\Users\Admin\Desktop\usernames.csv').Username
# get the file as object so you can use its properties
$originalFile = Get-Item -Path 'C:\Users\Admin\Desktop\generatedbat\VPNNEW_up.bat'
foreach ($username in $usernames) {
$targetFile = Join-Path -Path $originalFile.DirectoryName -ChildPath ('{0}-{1}' -f $username, $originalFile.Name)
$originalFile | Copy-Item -Destination $targetFile -WhatIf
}
I have added switch -WhatIf so you can first test this out. If what is displayed in the console window looks OK, then remove that -WhatIf safety switch and run the code again so the file is actually copied
I kept the code the same but instead of using a .csv file I just used a .txt file and it worked perfectly.

How to append new line text inside a log file using powershell script?

I need to pull logs from the original path in C:\ to log directory in D:\Logs but everytime the original path create new log, the script need to append new lines, not replace or rewrite the whole lines.
I already tried this but i guess this replace the whole file and I'm not sure about the Param things.
$SourceFolder = "C:\ProgramData\Sophos\Sophos Anti-Virus\logs"
$DestinationFolder = "D:\Logs\SophosAntivirus"
Function ChangeTabToSpace
{
Param(
[string] $OldFile = "",
[string] $NewFile = ""
)
$OldText = (Get-Content $OldFile -Raw)
#Change all tabt \t to space
$NewText = ($OldText -replace "`t"," ")
#Delete the last empty line
if ($NewText.Length -ge 2) {
$NewText = $NewText.Substring(0,$NewText.Length-2)
}
if (!(Test-path "$NewFile")) {
New-Item -type file "$NewFile" -force | Out-Null
}
#Write-Output $NewText | Out-File -Encoding utf8 "$NewFile"
[System.IO.File]::WriteAllLines($NewFile, $NewText)
}
If its a simple text file you can use the following
"the string you want or have" | out-file -path $path -append
This will add the string to a new line at the end of the file.
You don't need to pipe the input in like I did... its just how I learned to use it and just kept using it.

How can I write a script that will read other scripts and record their processes?

I have a folder of scripts that are being used for my company, and I need to know what each script does. I am trying to write a script in power shell that will record what each script does into a csv file.
I am a beginner in Powershell and am still learning so I apologize if I am being unclear.
I know that each of these scripts basic function is to map drives to a users computer, but there are too many to go through manually, any advice would be appreciated!
EDIT: Most of them are bat with a couple of vbs too. I want to record what drives are being mapped.
EDIT 2: I have now written my own script that looks like this :
Set-location z:\
get-Childitem "z:\Test"|
Foreach-object{
$filename = $_.Fullname
Get-content $filename|
foreach-object {
if ($_ -match "echo off") {
Write-output "$($filename): $_" | select-object $_ $filename
| export-csv "test.csv" -notypeinformation
}
}
}
I am having trouble exporting the data into a csv file as the error "A positional parameter cannot be found that accepts argument 'z:\Test\Test1.bat'"
The easiest way will be string parsing: look for the commands that map the drives. That's net use for bat files, or MapNetworkDrive in VBS. So look for those lines.
This will look through all the files in a folder and output the filename and the content of the line wherever it finds those lines:
Get-ChildItem "C:\Scripts" |
Foreach-Object {
$filename = $_.FullName
Get-Content $filename |
Foreach-Object {
if ($_ -match "net use" -or $_ -match "MapNetworkDrive") {
Write-Output "$($filename): $_"
}
}
}
That will not likely be exactly what you need, but it should get you started.

Parse directory listing and pass to another script?

I am trying to write a PowerShell script that will loop through a directory in C:\ drive and parse the filenames with the file extension to another script to use.
Basically, the output of the directory listing should be accessible to be parsed to another script one by one. The script is a compiling script which expects an argument (parameter) to be parsed to it in order to compile the specific module (filename).
Code:
Clear-Host $Path = "C:\SandBox\"
Get-ChildItem $Path -recurse -force | ForEach { If ($_.extension -eq ".cob")
{
Write-Host $_.fullname
}
}
If ($_.extension -eq ".pco")
{
Write-Host $_.fullname }
}
You don't need to parse the output as text, that's deprecated.
Here's something that might work for you:
# getmyfiles.ps1
Param( [string])$Path = Get-Location )
dir $Path -Recurse -Force | where {
$_.Extension -in #('.cob', '.pco')
}
# this is another script that calls the above
. getmyfile.ps1 -Path c:\sandbox | foreach-object {
# $_ is a file object. I'm just printing its full path but u can do other stuff eith it
Write-host $_.Fullname
}
Clear-Host
$Path = "C:\Sandbox\"
$Items = Get-ChildItem $Path -recurse -Include "*.cob", "*.pco"
From your garbled code am guessing you want to return a list of files that have .cob and .pco file extensions. You could use the above code to gather those.
$File = $Items.name
$FullName = $items.fullname
Write-Host $Items.name
$File
$FullName
Adding the above lines will allow you to display them in various ways. You can pick the one that suites your needs then loop through them on a for-each.
As a rule its not a place for code to be writen for you, but you have tried to add some to the questions so I've taken a look. Sometimes you just want a nudge in the right direction.

How do I make powershell script transverse zip files and report based off select-string -pattern

I have the following that is working but I need to also have the ability to read the contents of compressed file (zip)
function Search-Files {
param ([string[]]$Servers, [string]$SearchPath, [string]$SearchItem, [string[]]$LogName)
ForEach ($Server in $Servers) {
if ($LogName -eq $null) {
dir -Path \\$server\$SearchPath -Recurse -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Select-String -pattern $SearchItem -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Select-Object Filename, Path, Matches, LineNumber
}
Else {
dir -Path \\$server\$SearchPath -Recurse -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | ? {$_.Name -match $LogName} | Select-String -pattern $SearchItem -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | Select-Object Filename, Path, Matches, LineNumber
}
}
}
Currently I am getting the following out put displayed which is what I would like to do for zip files as well
ip.ininlog \CO200197L\C$\Temp\Test\Test\ip\ip.ininlog {3030872954} 136594
I have found the following just not sure how to proceed to get them implemented
Grep File in Zip
List File in Zip
I need the ability to transverse all zip files that are store in a directory
Sample of Directory Structure
2014-07-01 - root
zip.zip
zip_1.zip
zip_2.zip
etc
In case you have NET 4.5 framework installed, you can use 4.5's built-in ZIP support to extract files to a temporary path and run the selection on the temporary file. If no 4.5 is available, I recommend using SharpCompress (https://sharpcompress.codeplex.com/) which works in a similar way.
The following code snippet demonstrates extracting a ZIP archive into a temporary file, running the selection process from your script and the cleanup after the extraction. You can significantly simplify the code by extracting the entire ZIP file at once (just use ExtractToDirectory() on the archive) if it contains only the files you are seeking.
# import .NET 4.5 compression utilities
Add-Type -As System.IO.Compression.FileSystem;
# the input archive
$archivePath = "C:\sample.zip";
# open archive for reading
$archive = [System.IO.Compression.ZipFile]::OpenRead($archivePath);
try
{
# enumerate all entries in the archive, which includes both files and directories
foreach($archiveEntry in $archive.Entries)
{
# if the entry is not a directory (which ends with /)
if($archiveEntry.FullName -notmatch '/$')
{
# get temporary file -- note that this will also create the file
$tempFile = [System.IO.Path]::GetTempFileName();
try
{
# extract to file system
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($archiveEntry, $tempFile, $true);
# create PowerShell backslash-friendly path from ZIP path with forward slashes
$windowsStyleArchiveEntryName = $archiveEntry.FullName.Replace('/', '\');
# run selection
Get-ChildItem $tempFile | Select-String -pattern "yourpattern" | Select-Object #{Name="Filename";Expression={$windowsStyleArchiveEntryName}}, #{Name="Path";Expression={Join-Path $archivePath (Split-Path $windowsStyleArchiveEntryName -Parent)}}, Matches, LineNumber
}
finally
{
Remove-Item $tempFile;
}
}
}
}
finally
{
# release archive object to prevent leaking resources
$archive.Dispose();
}
If you have multiple ZIP files in the directory, you can enumerate them as follows (using your example script):
$zipArchives = Get-ChildItem -Path \\$server\$SearchPath -Recurse "*.zip";
foreach($zipArchive in $zipArchives)
{
$archivePath = $zipArchive.FullName;
...
}
You can place the demo code in ... or move it to a PowerShell function.
Sometimes is not desirable to extract a zip entry as a file. Instead it may be preferable to work with the file in memory. Extracting a Zip entry containing XML or JSON text so it can be parsed in memory is an example.
Here is a technique that will allow you to do this. This example assumes there is a Zip entry with a name ending in .json and it is this file which is to be retrieved. Clearly the idea can be modified to handle different cases.
This code should work with version of the .NET Framework that includes the System.IO.Compression namespace.
try
{
# import .NET 4.5 compression utilities
Add-Type -As System.IO.Compression.FileSystem;
# A variable to hold the recovered JSON content
$json = $null
$zip = [IO.Compression.ZipFile]::OpenRead($zipFileName)
$zip.Entries |
Where-Object { $_.Name.EndsWith(".json") } |
ForEach-Object {
# Use a MemoryStream to hold the inflated file content
$memoryStream = New-Object System.IO.MemoryStream
# Read the entry
$file = $_.Open()
# Copying inflates the entry content
$file.CopyTo($memoryStream)
# Make sure the entry is closed
$file.Dispose()
# After copying, the cursor will be at the end of the stream
# so set the position to the beginning or there will be no output
$memoryStream.Position = 0
# Use a StreamReader because it allows the content to be
# read as a string in one go
$reader = New-Object System.IO.StreamReader($memoryStream)
# Read the content as a string
$json = $reader.ReadToEnd()
# Close the reader and memory stream
$reader.Dispose()
$memoryStream.Dispose()
}
# Finally close the zip file. This is necessary
# because the zip file does get closed automatically
$zip.Dispose()
# Do something with the JSON in memory
if ( $json -ne $null )
{
$objects = $json | ConvertFrom-Json
}
}
catch
{
# Report errors
}