I am new to powershell Deploy Chrome it drops a folder on the remote machine then sits in the "Installing update" and never finishes - powershell-remoting

Variables
$computernames = Get-Content "C:\psexec\Chrome.txt"
$computernames = "NMOTC-sb10-9020"
$sourcefile = "\nmotc-ap-sccm\nmotc_packages\Chrome\googlechromestandaloneenterprise.msi"
This section will install the software
foreach ($computer in $computernames)
{
$destinationFolder = "\\$computer\c$\Program Files\Google"
#This section will copy the $sourcefile to the $destinationfolder. If the Folder does not exist it will create it.
if (!(Test-Path -path $destinationFolder))
{
Write-Host -ForegroundColor yellow Creating Directory $computer\c$\Program Files\google\application\Chrome
New-Item $destinationFolder -Type Directory
}
Write-Host -ForegroundColor Yellow Copying Update
Copy-Item -Path $sourcefile -Destination $destinationFolder
Write-Host -ForegroundColor Yellow Installing Update
Invoke-Command -ComputerName $computer -ScriptBlock {& cmd /c msiexec.exe -ArgumentList "i C:\Program Files\google\Chrome\googlechromestandaloneenterprise.msi"
}
Write-Host -ForegroundColor Yellow Cleaning Up Installation Files
#Remove-Item $destinationFolder -recurse
}

If you run your code that's in your ScriptBlock locally on a machine you want to install Chrome on does it work? I've used:
msiexec.exe /i "C:\Software\Chrome\GoogleChromeStandaloneEnterprise64.msi" /q /norestart
That command has worked for me when running it locally in PowerShell, seems like that should work in your ScriptBlock as well.

Variables
$computernames = Get-Content "C:\psexec\Chrome.txt"
$computernames = "xxxx-sb10-3020"
$sourcefile = "\nmotc-ap-sccm\xxxx_packages\Chrome\googlechromestandaloneenterprise.msi"
This section will install the software
foreach ($computer in $computernames)
{
$destinationFolder = "\\$computer\c$\Program Files\Chrome"
#This section will copy the $sourcefile to the $destinationfolder. If the Folder does not exist it will create it.
if (!(Test-Path -path $destinationFolder))
{
Write-Host -ForegroundColor yellow Creating Directory $computer\c$\Windows\System32\11.0.19
New-Item $destinationFolder -Type Directory
}
Write-Host -ForegroundColor Yellow Copying Update
Copy-Item -Path $sourcefile -Destination $destinationFolder
Write-Host -ForegroundColor Yellow Installing Update
Invoke-Command -ComputerName $computer -ScriptBlock { & cmd /c msiexec.exe /i "C:\Program Files\Chrome\GoogleChromeStandaloneEnterprise.msi"
}
Write-Host -ForegroundColor Yellow Cleaning Up Installation Files
Remove-Item $destinationFolder -recurse
}
I did get this to work well

Related

Function not Returning Data

I am using PowerShell to compare the file count and size (per file extension) in 2 separate directories.
$User = $env:username
$pwd = pwd
clear
write-host "`n"
write-host "`n"
write-host "`n"
write "The current user is: $User"
write-host "`n"
write "The current path is: $pwd"
write-host "`n"
write-host "`n"
write-host "`n"
write "We need to know the following information:"
write "`n"
write "`n"
$UserDesktopPath = Read-Host "New PC User Desktop Path" # This should be the new PC Desktop Path
$UserDocumentPath = Read-Host "New PC User Document Path" # This should be the new PC Document Path
$USBDesktopPathServer = Read-Host "USB User Desktop Path" # This should be the USB User Desktop Path
$USBDocumentPathServer = Read-Host "USB User Document Path" # This should be the USB User Document Path
clear
write-host "`n"
write-host "`n"
write-host "`n"
write "This is the results for your Desktop Folder Paths:"
write-host "`n"
$folder_new = Get-ChildItem -Recurse -path "$USBDesktopPathServer" # Recurses the New PC Desktop
$folder_old = Get-ChildItem -Recurse -path "$UserDesktopPath" # Recurses the USB Backup Desktop
Compare-Object -ReferenceObject "$folder_new" -DifferenceObject "$folder_old" # Compares the two folders for the path to identify discrepancies
write-host "`n"
write "This is the results for your Documents Folder Paths:"
write-host "`n"
write-host "`n"
write-host "`n"
$folder_new1 = Get-ChildItem -Recurse -path "$UserDocumentPath" # Recurses the New PC Documents
$folder_old1 = Get-ChildItem -Recurse -path "$USBDocumentPathServer" # Recurses the USB Backup Documents
Compare-Object -ReferenceObject "$folder_new1" -DifferenceObject "$folder_old1" # Compares the two folders for the path to identify discrepancies
write-host "`n"
write-host "`n"
write-host "`n"
write "Now we shall compare file sizes of your Documents:"
write-host "`n"
write-host "`n"
write-host "`n"
write-host "`n"
function doc{
$DirectoryDocuments = "$USBDocumentPathServer", "$UserDocumentPath"
foreach ($Directory in $DirectoryDocuments) {
Get-ChildItem -Path $Directory -Recurse |
Where-Object {-not $_.PSIsContainer} |
Tee-Object -Variable Files |
Group-Object -Property Extension |
Select-Object -Property #{
n = "Directory"
e = {$Directory}
},
#{
n = "Extension"
e = { $_.Name -replace '^\.' }
},
#{
n = "Size (MB)"
e={ [math]::Round( ( ( $_.Group | Measure-Object Length -Sum ).Sum / 1MB ), 2 ) }
},
Count
$Files |
Measure-Object -Sum -Property Length |
Select-Object -Property #{
n = 'Extension'
e = { 'Total' }
},
#{
n = 'Size (MB)'
e = { [math]::Round( ( $_.Sum / 1MB ), 2 ) }
},
Count
}
}
When using the ISE and calling dtop I get the correct return:
PS C:\Users\Michael Nancarrow> dtop
Directory Extension Size (MB) Count
--------- --------- --------- -----
D:\Deployment Kit\Test\Desktop2 txt 0 1
Total 0 1
D:\Deployment Kit\Test\Desktop1 txt 0 11
Total 0 11
Yet when run in the script, it does not return any value. I have attempted to call a function write $tst which runs dtop and that does the same (writes null).
Furthermore, I have removed the { so it does not run as a function, and it operates without an issue. My concern is perhaps the -Path file cannot be parsed at the same time as the input - meaning: when I call dtop from ISE it already has the $Directory variable stored in memory.
Are there any obvious errors here? I am rather new to PowerShell and am unsure where the mistake lies.

Powershell for creating folders and subfolders from csv

I try to prepare a script that will create folders and subfolders from CSV.
When I create a CSV with 1 column 'Name' like so
Name
XYZ
ZXX
FFF
with the following script I can achieve creation of folders
$folder = "D:\Test\"
$name = Import-Csv D:\Test\Test.csv
Foreach ($line in $name)
{
New-Item -path $folder -Name $line.Name -Type Directory
}
What I would like to achieve is to have CSV like this
Name;Letter
XYZ;A
ZXX;B
FFF;B
I want that the script creates folder $Folder\Letter\Name (so the final path is D:Test\A\XYZ; D:Test\B\ZXX; D:Test\B\FFF). Any advice is welcomed.
Thanks a lot.
I tried to improve the script little bit. In CSV files is the name and letter that works fine with the script what was provided by DAXaholic, but would need to add some more subfolders into the folder Name. I tried this, but it doesn't work as I need.
$folder = "D:\Test\"
$subfolders = "Volvo","Skoda","Tesla"
$file = Import-Csv 'D:\Test\Test.csv' -Delimiter ';'
$file |
% {
$path = Join-Path $folder $_.Letter;
New-Item -Path $path -Name $_.Name -Type Directory
}
foreach ($subfolder in $subfolders)
{
$file |
%{
$path2 = Join-Path $folder $_.Letter $_.Name;
New-Item -Path $path2\$subfolder -Type Directory
}
}
What I would like to achieve is following. Script should check the CSV file, create the user in CSV file put it in the folder and create 3 subfolders in the name folder. E.g D:\Test\A\XYZ\Volvo,Skoda,Tesla. What am I missing in the script? Thanks
if somebody would need similar script, then here is the solution
$folder = "D:\Test\"
$subfolders = "Volvo","Skoda","Tesla"
$file = Import-Csv 'D:\Test\Test.csv' -Delimiter ';'
$file |
% {
$path = Join-Path $folder $_.Letter;
New-Item -Path $path -Name $_.Name -Type Directory
}
foreach ($subfolder in $subfolders)
{
$file | %{
$path2 = Join-Path $folder $_.Letter;
$path3 = Join-Path $path2 $_.Name;
New-Item -Path $path3\$subfolder -Type Directory
}
}
This should give you the desired result
Import-Csv 'D:\Test\Test.csv' -Delimiter ';' |
% {
$path = Join-Path $folder $_.Letter;
New-Item -Path $path -Name $_.Name -Type Directory
}
What I did was put the desired path in my CSV file.
Silmiar to the above discussion.
"FilePath" can be replaced with any filepath that you have access to read/write from/to.
"Folders.csv" is the name of the CSV File that I used. You can name that csv file whatever you want as long as powershell can access it. And that it is something that the computer does not have a problem with the name of the file. Most computers will tell you if it does not like the csv filename that you use. If not PowerShell will tell you.
Here is an example of my PowerShell Code:
$path = "FilePath"
$Name = import-csv -Path 'FilePath\Folders.csv'
foreach ($i in $Name) {
New-Item -Path $path -Name $i.Name -ItemType Directory
}
Sample CSV File has this:
Name;Testing\A\A.1;Testing\B\B.1;Testing\C\C.1;Testing\D\D.1
Note: In the CSV file it would look more like this:
Name
Testing\A\A.1
Testing\B\B.1
Testing\C\C.1
Testing\D\D.1
I think this is reasonable code:
Set-Location "D:\Folder"
$subfolders = "1.Requested","2.Evidence","3.Complete"
$Folders = Import-Csv D:\Folder\csv\Fodlers.csv
ForEach ($Folder in $Folders) {
New-Item $Folder.Name -type directory
Foreach ($subfolder in $subfolders)
{
$path = Join-Path $Folder.Name -ChildPath " ";
New-Item $path\$subfolder -Type Directory
}
}
Copyright: Nguyen Ngo Anh Tuan

Can't export csv in powershell, empty CSV file

I'm trying to get what permissions files and folders have and export to a csv file. I can get the info to display on screen, but when I try to export it the resulting csv file is empty.
The code:
function Test-IsWritable(){
<#
.Synopsis
Command tests if a file is present and writable.
.Description
Command to test if a file is writeable. Returns true if file can be opened for write access.
.Example
Test-IsWritable -path $foo
Test if file $foo is accesible for write access.
.Example
$bar | Test-IsWriteable
Test if each file object in $bar is accesible for write access.
.Parameter Path
Psobject containing the path or object of the file to test for write access.
#>
[CmdletBinding()]
param([Parameter(Mandatory=$true,ValueFromPipeline=$true)][psobject]$path)
process{
Write-Host "Test if file $path is writeable"
if (Test-Path -Path $path -PathType Any){
$target = Get-Item $path -Force
try{
$writestream = $target.Openwrite()
$writestream.Close() | Out-Null
Remove-Variable -Name writestream
Write-Host "File is writable" -ForegroundColor DarkGreen
Write-Output $true
}
catch{
Write-Host "File is not writable" -ForegroundColor DarkRed
Write-Output $false
}
Remove-Variable -Name target
}
else{
Write-Host "File $path does not exist or is a directory" -ForegroundColor Red
Write-Output $false
}
}
}
write-host "WARNING: If checking deep folders (where the full path is longer than 248 characters) please " -foregroundcolor Yellow -NoNewline
Write-Host "MAP THE DRIVE " -ForegroundColor Red -NoNewline
Write-Host "in order to keep the names as short as possible" -ForegroundColor Yellow
$basefolder = Read-Host -Prompt 'What is the folder or files you want to get permissions of?'
write-host "WARNING: if permissions.csv already exists, it will be overwritten!" -foregroundcolor Yellow
Write-Host 'Export results to CSV? (y/n): ' -ForegroundColor Magenta -NoNewline
$export = Read-Host
if ($export -like "y")
{
Write-Host "Name the file (ex: permissions.csv): " -ForegroundColor Magenta -NoNewline
$FileName = Read-Host
$Outfile = “$PSScriptRoot\$FileName”
write-host "Will write results to $PSScriptRoot\$FileName" -ForegroundColor Green
}
else
{
write-host "User did not type 'y', continuing" -ForegroundColor DarkYellow
}
$files = get-childitem $basefolder -recurse -File
Write-Host $files
Write-Host "=========================" -ForegroundColor Black
#$subfiles = Get-ChildItem $folders -Recurse -File
#Write-Host $folders
#Write-Host "=========================" -ForegroundColor Black
#Write-Host $subfiles
$results = foreach($folder in $files) {
New-Object psobject -Property #{
File = $folder;
Access = "$basefolder\$folder" | Test-IsWritable
}
Write-Host $folder
}
#$subresults = foreach($subfile in $subfiles) {
# New-Object psobject -Property #{
# File = $subfiles;
# Access = $subfile | Test-IsWritable;
# }
#}
Write-Host $results
Write-Host "Finished combo loop, exporting..." -ForegroundColor Green
$results | Export-Csv $Outfile -NoTypeInformation -Delimiter ";"
Write-Host "Converting delimited CSV to Column Excel Spreadsheet"
$outputXLSX = $PSScriptRoot + "\$Filename.xlsx"
$excel = New-Object -ComObject excel.application
$workbook = $excel.Workbooks.Add(1)
$worksheet = $workbook.worksheets.Item(1)
$TxtConnector = ("TEXT;" + $Outfile)
$Connector = $worksheet.QueryTables.add($TxtConnector,$worksheet.Range("A1"))
$query = $worksheet.QueryTables.item($Connector.name)
$query.TextFileOtherDelimiter = ';'
$query.TextFileParseType = 1
$query.TextFileColumnDataTypes = ,2 * $worksheet.Cells.Columns.Count
$query.AdjustColumnWidth = 1
$query.Refresh()
$query.Delete()
$Workbook.SaveAs($outputXLSX,51)
$excel.Quit()
Remove-Item $Outfile
Write-Host "See $PSScriptRoot\$Filename.xlsx for results" -ForegroundColor Green
UPDATE: Mostly working, strange output though:
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file1.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file2.txt
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
Z:\testfolder\file3.rar
The specified path, file name, or both are too
long. The fully qualified file name must be less than 260 characters,
and the directory name must be less than 248 characters.
In the next column:
FileAccess
FullControl
FullControl
FullControl
Modify, Synchronize
ReadAndExecute, Synchronize
Modify, Synchronize
Modify, Synchronize
FullControl
FullControl
FullControl
Modify, Synchronize
...
The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the
directory name must be less than 248 characters.
I'm not sure why it's showing multiple rows for the same file, I'd like to have 1 row per file with the true File Access.
Remove Write-Host before using Export-Csv. Write-Hostconsumes the data from the pipeline and only outputs it on screen.
#(...)
$i = 0
$results = foreach($acl in $acls) {
$folder = (Convert-Path $acl.pspath)
Write-Progress -Activity "Getting Security" -Status "checking $folder" -PercentComplete ($i / $folders.Count * 100)
foreach($access in $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])) {
New-Object psobject -Property #{
Folder = $folder;
User = $acl.Owner;
Group=$acl.Group;
Mode = $access.AccessControlType;
FileAcess = $access.FileSystemRights;
}
}
$i++
}
Write-Host "Reached End, exporting..." -ForegroundColor Green
$results | Export-Csv $Outfile -NoTypeInformation -Delimiter ";"

Powershell script works in ISE but not in console

I am writing a powershell script that will start a process, monitor a folder for file changes, and then backup that folder when files are added/changed.
If I run my script from within the Powershell ISE, it works fine, I can monitor the folder and it will properly save a backup as expected.
The problem is that I want to run a batch file that will run the powershell script. But whenever I run the script from a powershell console, or whenver I run my batch file which runs the script, it doesn't work any longer. The script runs and the events are registered. When I copy a file over to the watched folder though, I ONLY get the changed event and not the created event, and the doStuff function is no longer called. I'm not sure how to go about debugging this :/
Following is my script. I have removed parts that don't pertain to what is actualyl wrong currently, so some variables I'm using here you won't see declared but they are there. I get the write-host's to the console when the changed event happens but not the created event (though as stated eariler in the ISE, I get both events and everything works just fine)
#unregister events, in case they weren't unregistered properly before. Just error siliently if they don't exist
Unregister-Event ConsoleStopped -ErrorAction SilentlyContinue
Unregister-Event FileCreated -ErrorAction SilentlyContinue
Unregister-Event FileChanged -ErrorAction SilentlyContinue
Unregister-Event TimerTick -ErrorAction SilentlyContinue
#start the console process
Write-Host Starting console process...
$consoleProcess = Start-Process "$consoleExe" -PassThru
#register to listen for when the console stops
Register-ObjectEvent $consoleProcess Exited -SourceIdentifier ConsoleStopped -Action {
Write-Host Console stopped
#unregister events
Unregister-Event ConsoleStopped -ErrorAction SilentlyContinue
Unregister-Event FileCreated -ErrorAction SilentlyContinue
Unregister-Event FileChanged -ErrorAction SilentlyContinue
if(!$timer.Enabled) {
Unregister-Event TimerElapsed -ErrorAction SilentlyContinue
Remove-Item $timer
}
Remove-Item $fsw
Remove-Item $consoleProcess
}
#watch all files/folders
$filter = '*.*' # You can enter a wildcard filter here.
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $true;NotifyFilter = [IO.NotifyFilters]'DirectoryName, FileName, LastWrite'}
#register for FileCreated event
Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
write-host Created event has occurred
doStuff($Event)
}
#register for FileChanged event
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
Write-Host Change event has occurred
doStuff($Event)
}
function doStuff($event)
{
write-host doStuff has been called
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' in '$folder' was $changeType at $timeStamp" -fore green
if(!$timer.Enabled) {
Write-Host Starting save timer
Register-ObjectEvent $timer Elapsed -SourceIdentifier TimerElapsed -Action $TimerAction
$timer.Start()
Out-File "$backupDir\backup.log" -Append -Force -InputObject "A request for a backup created at $timeStamp"
}
else {
Write-Host A backup has already been request
}
}
function backupSave ()
{
Write-Host Starting backup...
$timestamp = Get-Date -Format o | foreach { $_ -replace ":", "." }
Copy-Item $folder "$backupDir\backup_$timestamp" -Recurse -Force
}
Try moving your functions to the beginning of your script.
i.e. move function doStuff($event) and function backupSave () to the beginning of the script.
The problem may be because when you are calling the functions from the script they haven't been defined yet. It works in Powershell ISE because you ran the script several times and the functions were defined at one time or another.

PowerShell: Copy-Item throws DriveNotFoundException

My script keeps bugging me with the following exception
copy-item : Cannot find drive. A drive with the name 'F' does not exist.
At C:\Program Files (x86)\CA\ARCserve Backup\Templates\RB_Pre_Process.ps1:58 char:1
+ copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (F:String) [Copy-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
This is what my script looks like. I am mounting an ISO image on drive F: and I have added a "start-slepp -s 5" command so i can verify the image get's mounted, which it does!
$BACKUP_PATH = "E:\00_BACKUP_DATA"
$DR_PATH = "E:\01_DR_DATA"
$ISO_IMAGE = "C:\Program Files (x86)\CA\ARCserve Backup\Templates\Winpe_x64.iso"
$DST_DRIVE = "E:\"
try {
New-EventLog -LogName Application -Source "RB_Pre_Process.ps1" -ErrorAction Stop
} catch [System.InvalidOperationException] {
Write-host $_
}
try {
Write-Host "Preparing RDX cartridge..."
# Query for disk object
$disk_number = (Get-Disk | Where-Object -Property FriendlyName -like "TANDBERG RDX*").Number
# Remove partitions
Get-Disk $disk_number | Clear-Disk -RemoveData -Confirm:$false | Out-Null
# Create new partition
New-Partition -DiskNumber $disk_number -UseMaximumSize | Out-Null
# Format partition
Format-Volume -DriveLetter E -FileSystem NTFS -NewFileSystemLabel "RDX_TAPE" -Confirm:$false | Out-Null
# Set partition as active
Set-Partition -DriveLetter E -IsActive:$true | Out-Null
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventID 2 -Message $_
}
try {
Write-Host "Creating folder structure..."
new-item -itemtype directory -Path $BACKUP_PATH -ErrorAction stop | Out-Null
new-item -itemtype directory -path $DR_PATH -ErrorAction stop | Out-Null
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventID 2 -Message $_
}
try {
Write-Host "Mounting ISO image..."
$image = Mount-DiskImage -ImagePath $ISO_IMAGE -PassThru -ErrorAction Stop
} catch [ParameterBindingException] {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
$drive = ($image | Get-Volume).DriveLetter
$drive += ":\*"
Start-Sleep -s 5
try {
Write-Host "Copying ISO content..."
copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
} catch {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
try {
Write-Host "Unmounting ISO image..."
Dismount-DiskImage -ImagePath $ISO_IMAGE -ErrorAction Stop
} catch [System.Exception] {
Write-Host $_
Write-EventLog -LogName Application -Source $MyInvocation.MyCommand.Name -EventId 2 -Message $_
}
So, what's going wrong here? Sometimes it works sometimes not...
I "solved" the issue... my script is working perfectly fine when it's getting started directly from the PowerShell prompt instead of the PowerShell ISE... So the IDE is the culprit.
it seems the mounted image can't be reached in powershell. I think it's a limitation of the provider. A possible workaround is issuing CMD command. You could replace
copy-item -Path $drive -Destination $DST_DRIVE -Recurse -ErrorAction Stop
with
& cmd /C "copy F:\* G:\dest\"
Here I just give an example, you may need to do further work to copy recursively..you could use the xcopy or robocopy which could handle recursive copy.