Automating zip archive - powershell

So I am trying to process files in to separate backup files, however the this craetes a single archive named as the last file in the array. I am not sure what I am missing here.
$process = "C:\Program Files (x86)\7-Zip\7z.exe"
$destinationdir = "M:\WIP\OUT\"
$sourcedir = "M:\WIP\ZIP\"
$password = "password"
$ziplist = get-childitem $sourcedir
foreach ($zip in $ziplist)
{$destinationfile= $zip+".zip"
Start-Process $process -ArgumentList "a $destinationfile $zip -o$destinationdir -p$password"-NoNewWindow -Wait
}

Change
$destinationfile= $zip+".zip"
to
$destinationfile= $zip.FullName+".zip"

Related

How does one execute a powershell script from another powershell script, with capability to wait and get error codes?

I need to call a powershell script from another powershell script. The powershell script in question, myrobocopy.ps1, is a generalised module, just tuned to our specific needs. I need to call this from another script and get the error code it returns.
Attempts:
I tried it with Invoke-Expression, like this:
$source = "C:/sourcefolder"
$destination = "C:/destinationfolder"
$filename = "filename /LOG+:log.txt"
$arguments = "-source $source -destination $destination -file $filename"
$robocopyscript = "myrobocopy.ps1"
Invoke-Expression "$robocopyscript $arguments"
if ($LASTEXITCODE -ne 1){
$problems = 1
}
I think the issue with this is that we can't force the process to wait until myrobocopy.ps1 is finished. Additionally, if I'm honest with you, I don't even think $LASTEXITCODE works here, since Invoke-Expression isn't some global error handling tool. I'm new to Powershell, so yeah, its dumb, just showing my work here.
Next attempt looked like this:
$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
$arguments = "-source $source -destination $destination -file $filename"
$p = Start-Process $robocopy $arguments -Passthru -Wait
$code = $p.ExitCode
For all I know, this should work, yet when this script runs, it just opens myrobocopy.ps1 in notepad. I've seen some other answers regarding how to associate ps1 files with powershell itself, but I don't believe (could be mistaken here) that is the issue here.
Finally, I read that I need to actually start powershell itself, and then call the ps script. I'm not really sure how to do that, so I tried the following:
$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
$arguments = "-source $source -destination $destination -file $filename"
$p = Start-Process "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -File $robocopy -ArgumentList $arguments -PassThru -Wait
$code = $p.ExitCode
This returns the following error: Start-Process : A positional parameter cannot be found that accepts argument 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe'
So yeah I'm slightly lost. I'm sure I just don't know the syntax on how to do it. Help would be greatly appreciated, thank you!
EDIT: Just to add more info, myrobocopy.ps1 accepts 3 string parameters and explicitly returns the $LASTEXITCODE after running robocopy.
ANSWER:
Using the call operator made this work, and you use $LASTEXITCODE to retrieve the exit code.
$robocopy = "C:\testfolder\myrobocopy.ps1"
$source = "C:\testfolder"
$destination = "C:\testdestination"
$filename = "test.bat"
& $robocopy $source $destination $filename
Write-Output $LASTEXITCODE
If your "C:\testfolder\myrobocopy.ps1" put the result code on the pipeline, the call operator should do the trick:
$result = & "C:\testfolder\myrobocopy.ps1"
Another way to run robocopy and get the correct exit code would be to use Invoke-Expression with $global:LASTEXITCODE:
Function Start-Robocopy {
Param (
[Parameter(Mandatory)]
[String]$Source,
[Parameter(Mandatory)]
[String]$Destination,
[Parameter(Mandatory)]
[String]$Switches,
[String]$File
)
Try {
$result = [PSCustomObject]#{
Source = $Source
Destination = $Destination
File = $File
Switches = $Switches
RobocopyOutput = $null
ExitCode = $null
Error = $null
}
$global:LASTEXITCODE = 0 # required to get the correct exit code
$expression = [String]::Format(
'ROBOCOPY "{0}" "{1}" {2} {3}',
$Source, $Destination, $File, $Switches
)
$result.RobocopyOutput = Invoke-Expression $expression
$result.ExitCode = $LASTEXITCODE
}
Catch {
$result.Error = $_
}
Finally {
$result
}
}
$startParams = #{
Source = '\\contoso\FolderA'
Destination = '\\contoso\FolderA'
Switches = '/MIR /Z /R:3 /W:10 /NP /MT:16'
}
Start-Robocopy #startParams

Powershell script to install multiple msi on remote machines

Good day, I would ask you to help me with finding the solution how to copy each MSI package to remote machine using link on nas storage.
# Get list of servers
param(
[ValidateSet('STUDENT_LAB', 'LIBRARY_LAB', 'TEACHER_LAB')]
[Parameter(Mandatory = $true,
HelpMessage = 'Select one of the valid servers by typing one of these names: STUDENT_LAB, LIBRARY_LAB, TEACHER_LAB')]
[string]$ServerGroup
)
$servers = #{
STUDENT_LAB = ('192.168.1.1','192.168.1.2','192.168.1.3')
LIBRARY_LAB = ('192.168.10.1','192.168.10.2','192.168.10.3')
TEACHER_LAB = ('192.168.15.1','192.168.15.2','192.168.15.3')
}[$ServerGroup]
Write-Output "The user chose $ServerGroup"
#this is what I don't know how to implement - download file from nas storage on remote machine
$sourcefiles = '\\NASSTORAGE\MSI\MICROSOFT\Microsoft-ODBCDriver-11-SQLServer-x64\msodbcsql.msi' ; '\\NASSTORAGE\MSI\MICROSOFT\Microsoft-ODBCDriver-17-SQLServr-x64\msodbcsql_17.2.0.1_x64.msi'; '\\NASSTORAGE\MSI\MICROSOFT\Microsoft-OLEDBDriver-SQL Server-x64\msoledbsql_18.1.0.0_x64.msi'
foreach($server in $servers) {
# Destination UNC path changes based on server name
$destinationPath = "\\$server\D$\tmp\"
# Check that full folder structure exists and create if it doesn't
if(!(Test-Path $destinationPath)) {
New-Item -ItemType Directory -Force -Path $destinationPath
}
# Copy the file across
Copy-Item $sourcefiles $destinationPath
#list of packages to install
$msiList = #(
'Microsoft-ODBCDriver-11-SQLServer-x64\msodbcsql.msi'
'Microsoft-ODBCDriver-17-SQLServr-x64\msodbcsql_17.2.0.1_x64.msi'
'Microsoft-OLEDBDriver-SQL Server-x64\msoledbsql_18.1.0.0_x64.msi'
)
#now I'm trying to install on remote machine
foreach ($msi in $msiList) {
$install = Join-Path -Path $destinationPath -ChildPath $msi
Start-Process "msiexec.exe" -ArgumentList "/I $install",'/qn' -Wait
}
}
And is there any way how to check if the msi was installed properly?
Thank you for your time.
you can add this at the installation section :
$LaunchMsi = Start-Process "msiexec.exe" -ArgumentList "/I $install",'/qn' -Wait -PassThru
$ReturnCode = $LaunchMsi.ExitCode
if (($ReturnCode -eq "0") -OR ($ReturnCode -eq "3010")) {Write-Host "installation OK, return code : $ReturnCode"}
Else {Write-host "installation KO, return code : $ReturnCode"}

Encrypt zip archive using PowerShell script

I have a PowerShell script function that creates a text file and then creates a zip file and encrypts it. The code that I have written is as below.
function Zip-File([String]$Name, [String]$getRandomPass, [String]$EncryptionKey) {
try {
$7ZipPath = "C:\Program Files\7-Zip\7z.exe";
$Filepath = "C:\Scripts\$Name.txt";
$ZipPath = "C:\Scripts\$Name.zip";
Set-Content -Path $Filepath -Value $getRandomPass -Force;
$arguments = "a -tzip ""$ZipPath"" ""$Filepath"" -mx9 -p$EncryptionKey";
$windowstyle = "Normal";
$p = Start-Process $7ZipPath -ArgumentList $arguments -Wait -Passthru -WindowStyle $windowstyle;
} catch {
Write-Host "Error is: $($_.Exception.GetType().FullName)"
Write-Host "Error is: $($_.Exception.Message)"
} finally {}
}
This code creates the zip folder $Name.zip with the file $Name.txt. However, only the file $Name.txt is encrypted with the encryption key. The zip archive $Name.zip is not encrypted.
I am not sure how to encrypt the $Name.zip zip archive. Could someone point out how to encrypt the $Name.zip zip archive?
If you want to protect your filenames, you can double archive it. Basically, you can again zip the .zip file. This should work.

Powershell script making swf extension open with internet explorer

I am trying to figure out how to write a powershell script that will set all .swf extensions to open up on Internet Explorer. I was trying to do this with a command prompt similar to the example below. Unfornately my boss is requiring this to be done through powershell. Any help with this would be greatly appreciated since I have a txt file that will loop through about 400 computers and need to make these changes on.
CMD Way
C:\>ASSOC .swf
.swf=ShockwaveFlash.ShockwaveFlash
C:\>FTYPE ShockwaveFlash.ShockwaveFlash
ShockwaveFlash.ShockwaveFlash="C:\bin\FlashPlayer.exe" %1
What I am Trying:
Function Get-FileName{
[CmdletBinding()]
Param(
[String]$Filter = "|*.*",
[String]$InitialDirectory = "C:\")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $InitialDirectory
$OpenFileDialog.filter = $Filter
[void]$OpenFileDialog.ShowDialog()
$OpenFileDialog.filename
}
$file = Get-FileName -InitialDirectory $env:USERPROFILE\Desktop -Filter "Text files (*.txt)|*.txt|All files (*.*)|*.*"
ForEach ($item in (Get-Content $file)) {
$sitem = $item.Split("|")
$computer = $sitem[0].Trim()
$user = $sitem[1].Trim()
cmd /c assoc .swf=InternetExplorer.Application
### Will the above line automatically install on every pc? ###
}
Any help with trying to insert how to change the FTYPE in powershell so that $computer can cycle through would be greatly appreciated!
ASSOC and FTYPE are CMD.exe built-in commands, not executables, which means they can only be run in the context of CMD. The easiest way to run them is to invoke CMD from PowerShell.
cmd /c assoc .swf
cmd /c ftype ShockwaveFlash.ShockwaveFlash
If you need a "pure" PowerShell implementation, then you need to go to the registry. ASSOC and FTYPE merely write to the registry under theHKEY_CLASSES_ROOT hive. PowerShell does not have a default PSDrive for HKCR:, but that hive is also accessible under HKLM:\Software\Classes.
$ext = '.swf'
$HKCR = 'HKLM:\Software\Classes'
$ftype = Get-ItemProperty -Path "$HKCR\$ext" | select -expand '(default)'
$commandLine = Get-ItemProperty -Path "$HKCR\$ftype\shell\open" | select -expand '(default)'
$commandLine
To update these values, you simply use Set-ItemProperty on the same path.
Set-ItemProperty -Path "$HKCR\$ext" -Name '(default)' -Value 'ShockwaveFlash.ShockwaveFlash'
This requires you to run with Admin privileges. This also assumes that the key already exists. If not, you will have to create it with New-Item
if (-not (Test-Path "$HKCR\$ext")) {
New-Item -Path "$HKCR\$ext"
}
However, if all you want to do is set .swf files to open in iexplore.exe, then retrieving the values is unnecessary, as is modifying the FTYPE key. You need only change the extension association to InternetExplorer.Application instead of ShockwaveFlash.ShockwaveFlash. The following full scripts will do this:
In Batch file:
assoc .swf=InternetExplorer.Application
In PowerShell:
cmd /c assoc .swf=InternetExplorer.Application
In "pure" PowerShell, by modifying the registry:
$key = "HKLM:\Software\Classes\.swf"
$defaultName = '(default)'
$newValue = 'InternetExplorer.Application'
if (-not (Test-Path $key)) {
New-Item -Path $key
}
Set-Itemproperty -Path $key -Name $defaultName -Value $newValue
Note that modifying the registry doesn't take effect immediately. You need to also send a WM_SETTINGCHANGE event, or simply restart explorer.exe (eg: by logging off). You can find code to send the event here, but usually this isn't a problem for automated scripts because they force the user to re-login anyway.

FTPS Upload in Powershell

I'm in the process of learning Powershell, and am working on a little script that will upload a group of files to an FTPS server nightly. The files are located on a network share in a sub-directory containing the date in the name. The files themselves will all begin with the same string, let's say "JONES_". I have this script working for FTP, but I don't quite get what I need to do to get it to work for FTPS:
# Set yesterday's date (since uploads will happen at 2am)
$YDate = (Get-Date).AddDays(-1).ToString('MM-dd-yyyy')
#Create Log File
$Logfile = "C:\powershell\$YDate.log"
Function LogWrite
{
Param ([string]$logstring)
Add-Content $Logfile -value $logstring
}
# Find Directory w/ Yesterday's Date in name
$YesterdayFolder = Get-ChildItem -Path "\\network\storage\location" | Where-Object {$_.FullName.contains($YDate)}
If ($YesterdayFolder) {
#we specify the directory where all files that we want to upload are contained
$Dir= $YesterdayFolder
#ftp server
$ftp = "ftp://ftps.site.com"
$user = "USERNAME"
$pass = "PASSWORD"
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$FilesToUpload = Get-ChildItem -Path (Join-Path $YesterdayFolder.FullName "Report") | Where-Object {$_.Name.StartsWith("JONES","CurrentCultureIgnoreCase")}
foreach($item in ($FilesToUpload))
{
LogWrite "Uploading file: $YesterdayFolder\Report\$item"
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
}
} Else {
LogWrite "No files to upload"
}
I'd rather not have to deal with a 3rd party software solution, if at all possible.
Using psftp didn't work for me. I couldn't get it to connect to the FTP over SSL. I ended up (reluctantly?) using WinSCP with this code:
$PutCommand = '& "C:\Program Files (x86)\WinSCP\winscp.com" /command "open ftp://USER:PASS#ftps.hostname.com:21/directory/ -explicitssl" "put """"' + $Item.FullName + '""""" "exit"'
Invoke-Expression $PutCommand
In the foreach loop.
I'm not sure if you would consider this as "3rd party software" or not, but you can run PSFTP from within Powershell. Here is an example of how you could do that (source):
$outfile=$YesterdayFolder"\Report\"$item.Name
"rm $outfile`nput $outfile`nbye" | out-file batch.psftp -force -Encoding ASCII
$user = "USERNAME"
$pass = "PASSWORD"
&.\psftp.exe -l $user -pw $pass $ftp -b batch.psftp -be