Unzip a password protected file with Powershell - powershell

Powershell password protected file
Hi, I'm new to powershell and try to learn some trick with it.
I created a simple code that's supposed to unzip a file using 7zip and a known password.
Here's the code:
$7ZipPath = '"C:\Program Files\7-Zip\7z.exe"'
$zipFile = '"C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip"'
$path = 'C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\foldertest'
New-Item -ItemType directory -Path $path
Read-Host -Prompt 'step1'
$password = Read-Host -Prompt 'Input the password'
Write-Host $password
$command = "& $7ZipPath e -oC:\ -y -tzip -p$password $zipFile"
Invoke-Expression $command
I keep getting these errors :
7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04
Scanning the drive for archives:
1 file, 310 bytes (1 KiB)
Extracting archive: C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip
Path
= C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip
Type = zip
Physical Size = 310
ERROR: Can not open output file : Accès refusé. :
C:\ok.txt
Sub items Errors: 1
Archives with Errors: 1
Sub items Errors: 1

So here is what you are doing in function form to clean it up a bit
Function Open-7ZipFile{
Param(
[Parameter(Mandatory=$true)]
[string]$Source,
[Parameter(Mandatory=$true)]
[string]$Destination,
[string]$Password,
[Parameter(Mandatory=$true)]
[string]$ExePath7Zip,
[switch]$Silent
)
$Command = "& `"$ExePath7Zip`" e -o`"$Destination`" -y" + $(if($Password.Length -gt 0){" -p`"$Password`""}) + " `"$Source`""
If($Silent){
Invoke-Expression $Command | out-null
}else{
"$Command"
Invoke-Expression $Command
}
}
And here is how to run it
Open-7ZipFile -ExePath7Zip "C:\Program Files\7-Zip\7z.exe" -Source "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip" -Destination "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\foldertest" -Password "Password"
Make sure you have access to the folder you are trying to unzip to
If you do not have rights you will end up with the error you are getting now
ERROR: Can not open output file : Accès refusé. : C:\ok.txt
Edited the function to allow spaces and run silently.

You don't need Invoke-Expression; just run the command with the parameters you need. Here's a short sample script you can use (modify to suit your needs):
param(
[Parameter(Mandatory = $true)]
[String]
$ArchiveFilename,
[String]
$DestinationPath,
[Switch]
$HasPassword
)
$ARCHIVE_TOOL = "C:\Program Files\7-Zip\7z.exe"
function ConvertTo-String {
param(
[Security.SecureString] $secureString
)
try {
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
[Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
}
finally {
if ( $bstr -ne [IntPtr]::Zero ) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
}
if ( $HasPassword ) {
$securePwd = Read-Host -AsSecureString -Prompt "Enter archive password"
if ( $securePwd ) {
$password = ConvertTo-String $securePwd
}
}
if ( -not $DestinationPath ) {
$DestinationPath = (Get-Location).Path
}
& $ARCHIVE_TOOL e "-o$DestinationPath" "-p$password" $ArchiveFilename
If the script is named Expand-ArchiveFile.ps1, run it like this:
Expand-ArchiveFile.ps1 "C:\Users\touff\OneDrive\Bureau\0\Encrypted Zip\test.zip" -HasPassword
Note that when specifying filenames, you do not need the embedded quotes. (The quotes are not part of the file's name.)

Related

Powershell bind argument path

When I execute a particular script, the Write-Log function throws an error. This appears to be before I call the function. It is loaded from an external file. This problem is new, it worked previously with no issues.
Error
There are two errors, split by a VERBOSE output statement. The 2nd VERBOSE line is the last script line I included. The format (not colours) is the best I can recreate
C:\Scripts>powershell -file tca_export_tt_ftps.ps1
Test-Path : Cannot bind argument to parameter 'Path' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:75 char:24
+ if ((Test-Path $Path) -AND $NoClobber) {
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.TestPathCommand
VERBOSE: Testing C:\Scripts\log\ exists
Out-File : Cannot bind argument to parameter 'FilePath' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:110 char:67
+ ... FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Out-File], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.OutFileCommand
VERBOSE: Logfile is: C:\Scripts\log\kh_tca_out_20210222.log
Start of my Script
Function Upload-TCA
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false)]
[Alias('StartDate')]
[Alias('SeedDate')]
[DateTime]$dateSeed = (Get-Date),
#[string]$dateSeedStr = (Get-Date -format "yyyy-MM-dd"),
[Parameter(Mandatory=$false)]
[Alias('DisableFtp')]
[bool]$ftpDisabled=$false,
[Parameter(Mandatory=$false)]
[Alias('DisplayConfig')]
[bool]$emitConfig = $false,
[Parameter(Mandatory=$true)]
[string]$orgCode
)
#########################################################################
# Function Imports
#########################################################################
. c:\scripts\Function-Generate-Folder.ps1
. c:\scripts\Function-Write-Log.ps1
. c:\scripts\Function-Upload-WinSCP-FTPS.ps1
. c:\scripts\Function-Create-CredentialFromFile.ps1
#########################################################################
# Logging Setup
#########################################################################
$logDir = "C:\Scripts\log\"
Generate-Folder $logDir
$logName = "$orgCode_tca_out_$(Get-Date -Format "yyyyMMdd").log"
$logFile = Join-Path -Path $logDir -ChildPath $logName
Write-Log "Logfile is: $logFile" -Path $logFile
Write-Log
The Write-Log function is from the Microsoft Script Center Included here in case the link is broken
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.DESCRIPTION
The Write-Log function is designed to add logging capability to other scripts.
In addition to writing output and/or verbose you can write to a log file for
later debugging.
.NOTES
Created by: Jason Wasser #wasserja
Modified: 11/24/2015 09:30:19 AM
Changelog:
* Code simplification and clarification - thanks to #juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to #JeffHicks
* Revised the Force switch to work as it should - thanks to #JeffHicks
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
I hadn't read the Generate-Folder method correctly. I changed the calls in there for Write-Log to Write-Host. It was trying to use the log before it was fully setup.
Function Generate-Folder()
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$path='C:\scripts\logs'
)
Write-Host "Testing $path exists" # cant use write-log as not setup yet
$global:foldPath=$null
foreach($foldername in $path.split("\"))
{
$global:foldPath+=($foldername+"\")
if(!(Test-Path $global:foldPath))
{
New-Item -ItemType Directory -Path $global:foldPath
Write-Host "$global:foldPath Folder Created Successfully" #-Path $logFile
}
}
}

Dot-sourced script not referencing current directory on 2nd run

I have a script, below, which I have in a Scripts folder. It references a set of command prompt applications $cmd1.exe, etc.. Using either Powershell or the Integrated Powershell terminal in VS Code, I follow these steps to use it:
Dot source the script from the Scripts directory > .\new-customconfig.ps1,
Change to the working directory > cd [New Directory],
Use the function in the script and pass a filename from the working directory > New-CustomConfig .\FileName.
It passes the file from the working directory the first time I run the script, but from any subsequent runs, it looks for the file in the Scripts directory.
Is there something I am doing wrong?
Function New-CustomConfig {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]
$FileName
)
Begin {
#Set tools directory
$ToolD = '[Directory]\Tools'
#Clean up filename
$FileName = $FileName -replace '^[\W]*', ''
#Setup Executables
$cmdtools = "\cmd1.exe", "\cmd2.exe", "\cmd3.exe"
#Setup Arguments
$cmdargs = "cmd1args", "cmd2args", "cmd3args"
#Setup Write-Host Comments
$cmdecho = "echo1", "echo2", "echo3"
#Setup command object info
$cmdinfo = New-Object System.Diagnostics.ProcessStartInfo
$cmdinfo.RedirectStandardError = $true
$cmdinfo.RedirectStandardOutput = $true
$cmdinfo.UseShellExecute = $false
#Create command object
$cmd = New-Object System.Diagnostics.Process
"Generating Config for $FileName"
}
Process {
for ($i = 0; $i -le $cmdtools.Count; $i++) {
$cmdinfo.FileName = $ToolD + $cmdtools[$i]
$cmdinfo.Arguments = '' + $cmdargs[$i] + ''
Write-Host $i
Write-Host $cmdinfo.FileName
Write-Host $cmdinfo.Arguments
Write-Host $(Get-Location)
$cmdecho[$i]
$cmd.StartInfo = $cmdinfo
$cmd.Start() | Out-Null
$cmd.WaitForExit()
$stdout = $cmd.StandardOutput.ReadToEnd()
$stderr = $cmd.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
}
}
End {
"Press any key to continue..."
$null = $host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}
}

file not found in current directory

I have the following script:
function Export-sql-toExcel {
[CmdletBinding()]
Param (
[string]$scriptFile,
[string]$excelFile,
[string]$serverInstance,
[string]$database,
[string]$userName,
[string]$password
)
$tokens = ( [system.io.file]::ReadAllText( $scriptFile ) -split '(?:\bGO\b)' )
foreach ($token in $tokens) {
$token = $token.Trim()
if ($token -ne "") {
$lines = $token -split '\n'
$title = $lines[0]
if ($title.StartsWith("--")) {
$title = $title.Substring(2)
$title
}
Invoke-Sqlcmd -ServerInstance $serverInstance -Database $database -Username $userName -Password $password -Query $token |
Export-Excel -Path $excelFile -WorkSheetname $title -FreezeTopRow -ExcludeProperty RowError,RowState,Table,ItemArray,HasErrors
}
}
}
I have installed this function as a module. When I invoke the command from, for example, desktop folder, like this:
PS D:\Usuarios\mnieto\Desktop> Export-sql-toExcel -scriptFile .\EXPORT.txt -excelFile Excel.xlsx
I get the following error (the export.txt file is in the desktop folder):
Exception calling "ReadAllText" with "1" argument(s): "Can't find the file 'D:\Usuarios\<MyUserName>\EXPORT.txt'."
EDITED
if I debug and try [system.environment]::CurrentDirectory, it returns
'D:\Usuarios\<MyUserName>
That is because my script fails. NET functions and powershell don't share the 'current directory'
Any other way to get the content and parse the $scriptFile file?
I got the solution changing the NET call by a powershell command at this line
$content = Get-Content $scriptFile -Raw
$tokens = ( $content -split '(?:\bGO\b)' )
the trick was in the -Raw parameter, so the file is read as a single string
To my experience .dot NET functions don't like relative path's.
I'd use
$scriptfile = (Get-Item $Scriptfile).FullName
to resolve to a full path in the function just ahead :
$tokens = ( [system.io.file]::ReadAllText( $scriptFile ) -split '(?:\bGO\b)' )
I had the same situation, it was resolved when i added a dot "." to call the csv
$path2 = $path + ".\file.csv"
Or check for spaces at the $excelFile variable.

How to create directories from powershell on FTP server?

I want to run a PS script when I want to publish to FTP server. I took this script as structure : structure script.
I have very simple folder :
C:\Uploadftp\Files\doc.txt
C:\Uploadftp\Files\Files2
C:\Uploadftp\Files\Files2\doc2.txt
nothing fancy there.
Here is my script :
cd C:\Uploadftp
$location = Get-Location
"We are here: $location"
$user = "test" # Change
$pass = "test" # Change
## Get files
$files = Get-ChildItem -recurse
## Get ftp object
$ftp_client = New-Object System.Net.WebClient
$ftp_client.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$ftp_address = "ftp://test/TestFolder"
## Make uploads
foreach($file in $files)
{
$directory = "";
$source = $($file.DirectoryName + "/" + $file);
if ($file.DirectoryName.Length -gt 0)
{
$directory = $file.DirectoryName.Replace($location,"")
}
$directory = $directory.Replace("\","/")
$source = $source.Replace("\","/")
$directory += "/";
$ftp_command = $($ftp_address + $directory + $file)
# Write-Host $source
$uri = New-Object System.Uri($ftp_command)
"Command is " + $uri + " file is $source"
$ftp_client.UploadFile($uri, $source)
}
I keep getting this error :
Exception calling "UploadFile" with "2" argument(s): "An exception occurred during a WebClient request."
If I hardcode specific folder for $uri and tell source to be some specific folder on my computer, this script doesn't create directory, it creates a file. What am I doing wrong?
P.S. dont hit me too hard, its my fist time ever doing something in power shell.
Try the "Create-FtpDirectory" function from https://github.com/stej/PoshSupport/blob/master/Ftp.psm1
function Create-FtpDirectory {
param(
[Parameter(Mandatory=$true)]
[string]
$sourceuri,
[Parameter(Mandatory=$true)]
[string]
$username,
[Parameter(Mandatory=$true)]
[string]
$password
)
if ($sourceUri -match '\\$|\\\w+$') { throw 'sourceuri should end with a file name' }
$ftprequest = [System.Net.FtpWebRequest]::Create($sourceuri);
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::MakeDirectory
$ftprequest.UseBinary = $true
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($username,$password)
$response = $ftprequest.GetResponse();
Write-Host Upload File Complete, status $response.StatusDescription
$response.Close();
}

Script to run an exe with parameters and catch exit code

I have an import tool that has support for running in unattended mode. It accepts arguments like this:
importer.exe -organization "DEV" -dataFile "E:\importData.txt" -rightsFile "E:\importRights.txt" -logFile "C:\LogFile.log"
Now above is how the tool itself accepts the arguments.
I'm writing a ps-script to launch the tool with the above parameters.
<#
.SYNOPSIS
Executes the PWR Bulk Data Importer tool in unattended mode.
.DESCRIPTION
This script executes the PWR Bulk Data Importer tool in unattended mode. The Import files: Data and rights must be supplied and log file must also be provided.
.PARAMETER ImportToolExe
The full path and exe of the tool.
.PARAMETER Organization
The identifier of the organization.
.PARAMETER DataFile
The full path and filename of data file.
.PARAMETER RightsFile
The full path and filename of rights file.
.PARAMETER LogFile
The full path and filename of log file (will be created and if already exists, it'll be over-writtien).
.PARAMETER IsForced
If true, tool will run in override mode omitting all deletion warnings.
.EXAMPLE
Executer -ImportToolExe "D:\tool\PWR Bulk Data Importer.exe" -Organization "VTDEV" -DataFile "E:\importData.txt" -RightsFile "E:\importRights.txt" -LogFile "C:\LogFile.log"
Executer -ImportToolExe "D:\tool\PWR Bulk Data Importer.exe" -Organization "VTDEV" -DataFile "E:\importData.txt" -RightsFile "E:\importRights.txt" -LogFile "C:\LogFile.log" -IsForced true
#>
param(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)]
[string]$ImportToolExe,
[Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)]
[string]$Organization,
[Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)]
[string]$DataFile,
[Parameter(Mandatory = $true, Position = 3, ValueFromPipelineByPropertyName = $true)]
[string]$RightsFile,
[Parameter(Mandatory = $true, Position = 4, ValueFromPipelineByPropertyName = $true)]
[string]$LogFile,
[Parameter(Mandatory = $false)]
[bool]$IsForced
)
Write-Output ""
Write-Output "Script to execute Bulk Data Importer"
Write-Output ""
$params = "-organization " + $Organization + " -dataFile " + $DataFile + " -rightsFile " + $RightsFile + " -logFile " + $LogFile
Write-Output "Debuging"
Write-Output ($ImportToolExe + " " + $params)
Try
{
Write-Output " "
Write-Output "Executing..."
Invoke-Expression ($ImportToolExe + " " + $params)
Write-Output "Finished."
Write-Output "Checking exit code."
}
Catch [system.exception]
{
" "
"Exception while trying to execute"
Write-Output $_.Exception.GetType().FullName;
Write-Output $_.Exception.Message;
return
}
Finally
{
Write-Output " "
}
$IsImportSuccess = $false
IF ($lastexitcode -eq 0)
{
Write-Output "Import successful."
$IsImportSuccess = $true
}
ELSE
{
Write-Output "Import failed."
$IsImportSuccess = $false
}
IF ($IsImportSuccess -eq $true)
{
Try
{
$SMTPServer = "smtp.gmail.com"
$SMTPClient = New-Object Net.Mail.SMTPClient( $SmtpServer, 587 )
$SMTPClient.EnableSSL = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential( "GMAIL_USERNAME", "GMAIL_PASSWORD" );
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = $EmailFrom
foreach ( $recipient in $Arry_EmailTo )
{
$emailMessage.To.Add( $recipient )
}
$emailMessage.Subject = $EmailSubj
$emailMessage.Body = $EmailBody
# Do we have any attachments?
# If yes, then add them, if not, do nothing
# if ( $Arry_EmailAttachments.Count -ne $NULL )
# {
# $emailMessage.Attachments.Add()
# }
$emailMessage.Attachments.Add($LogFile)
$SMTPClient.Send( $emailMessage )
}
Catch [system.exception]
{
" "
"Exception while emailing"
write-host $_.Exception.GetType().FullName;
write-host $_.Exception.Message;
return
}
}
No I'm getting output with errors:
Script to execute Bulk Data Importer
Debuging
D:\tool\PWR Bulk Data Importer.exe -organization VTDEV -dataFile E:\importData.txt -rightsFile E:\importRights.txt -logFile C:\LogFile.log
Executing...
Exception while trying to execute
System.Management.Automation.CommandNotFoundException
The term 'D:\tool\PWR' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I'm facing a bit of learning curve with PS and had this script up by reading from SO so far.
I see the main issue where I print a debug line: All my quotes are gone already. And my manager told me that the Invoke-Expression is not a good idea. He recommends me to use Start-something
Now I'm stuck. Any pointer will be greatly appreciated and there will be upvotes as well.
Add -ErrorAction Stop inside try/catch, this should trigger desired action:
try{
...
Invoke-Expression ($ImportToolExe + " " + $params) -ErrorAction Stop
...
}
catch{
## catch code
}
You can also try Invoke-Command to start the exe so in your scenario replace Invoke-Expression with the following:
Invoke-command -ScriptBlock {&$ImportToolExe $args[0]} -ArgumentList $params