I have made he following code using the code present on this forum.
cls
$computername = Get-Content 'C:\Users\C201578-db\Documents\server.txt'
$sourcefile = "\\iceopsnas\LNT_SoftwareRep.grp\CORE\COTS\EMC\Avamar\Avamar_7.0\CR06794393\AvamarClient-windows-x86_64-7.0.102-47.msi"
#This section will install the software
foreach ($computer in $computername)
{
$destinationFolder = "\\$computer\C$\Avamar"
#This section will copy the $sourcefile to the $destinationfolder. If the Folder does not exist it will create it.
if (!(Test-Path -path $destinationFolder))
{
New-Item $destinationFolder -Type Directory
}
Copy-Item -Path $sourcefile -Destination $destinationFolder
Write-Host "Copied Successfully"
Invoke-Command -ComputerName $computer -ScriptBlock { & cmd /c "msiexec.exe /i C:\Avamar\AvamarClient-windows-x86_64-7.0.102-47.msi" /qb ADVANCED_OPTIONS=1 CHANNEL=100}
Write-Host "Installed Successfully"
}
I tried all permutations and combinations but no luck. Tried all the suggestions that I got while posting this question but nothing. The copy procedure is successful but the .msi file is not getting installed. Maybe this question gets marked duplicate but still suggest some edits before doing that.
try defining your command as a script block instead:
$command = "msiexec.exe /i C:\Avamar\AvamarClient-windows-x86_64-7.0.102-47.msi"
$scriptblock = [Scriptblock]::Create($command)
Invoke-Command -ComputerName $computer -ScriptBlock $scriptblock
As a workaround (the lack of details doesnt help to daignose the problem), you could use the third party tool psexec.exe to run the installer on the remote host.
Try to replace your invoke-command with
psexec.exe \\$computer -s -u Adminuser -p AdminPassword msiexec /i C:\Avamar\AvamarClient-windows-x86_64-7.0.102-47.msi /qb ADVANCED_OPTIONS=1 CHANNEL=100
It's working fine with psexec.exe, I have installed it on more than 100 user's desktop. Setup your user's ip addresses on clients.txt file. Below is my code :
cls
$computername = Get-Content 'C:\Setup\clients.txt'
$sourcefile = "C:\Setup\MySyncSvcSetup.msi"
$serviceName = "MySyncWinSvc"
$adminUserName = "username"
$adminPassword = "password#123"
#This section will install the software
foreach ($computer in $computername)
{
#First uninstall the existing service, if any
C:\PSTools\psexec.exe \\$computer -s -u $adminUserName -p $adminPassword msiexec.exe /x C:\SetupFiles\MySyncSvcSetup.msi /qb
Write-Host "Uninstalling Service"
$destinationFolder = "\\$computer\C$\SetupFiles"
#This section will copy the $sourcefile to the $destinationfolder. If the Folder does not exist it will create it.
if (!(Test-Path -path $destinationFolder))
{
New-Item $destinationFolder -Type Directory
}
Copy-Item -Path $sourcefile -Destination $destinationFolder
Write-Host "Files Copied Successfully"
C:\PSTools\psexec.exe \\$computer -s -u $adminUserName -p $adminPassword msiexec.exe /i C:\SetupFiles\MySyncSvcSetup.msi /qb /l* out.txt
Write-Host "Installed Successfully"
C:\PSTools\psexec.exe \\$computer -s -u $adminUserName -p $adminPassword sc.exe start $serviceName
Write-Host "Starting the Service"
}
Related
I am trying to have a script that will install exe and msi files in silent mode, but it's opening the installers instead of running silently.
#network drive path
$nwDrivePath = "\\server\folder\" #"
#check if path is valid
if (test-path $nwDrivePath){
Set-Location $nwDrivePath
}
else{
#if path is not valid abort
Write-Output "No path found for $nwDrivePath"
Write-Output "Aborting script"
break script
}
#install .exe files
$allFiles = Get-ChildItem $nwDrivePath -Filter *.exe | ForEach {
Start-Process $_.Fullname -ArgumentList "/s"
}
#install .msi files
$allFiles = Get-ChildItem $nwDrivePath -Filter *.msi | ForEach {
Start-Process $_.Fullname -ArgumentList "/qn"
}
I'm new to Powershell. I have 80 servers that I need to connect to and run a Pshell script on remotely to find files recursively in one share by last access date and move them to another \server\share for archiving purposes. I also need the file creation, last accessed etc. timestamps to be preserved.
I would welcome any help please
thank you
You need to test this thoroughly before actually using it on all 80 servers!
What you could do if you want to use PowerShell on this is to use Invoke-Command on the servers adding admin credentials so the script can both access the files to move as well as the destination Archive folder.
I would suggest using ROBOCOPY to do the heavy lifting:
$servers = 'Server1', 'Server2', 'Server3' # etcetera
$cred = Get-Credential -Message "Please supply admin credentials for archiving"
$scriptBlock = {
$SourcePath = 'D:\StuffToArchive' # this is the LOCAL path on the server
$TargetPath = '\\NewServer\ArchiveShare' # this is the REMOTE path to where the files should be moved
$LogFile = 'D:\ArchivedFiles.txt' # write a textfile with all fie fullnames that are archived
$DaysAgo = 130
# from a cmd box, type 'robocopy /?' to see all possible switches you might want to use
# /MINAGE:days specifies the LastWriteTime
# /MINLAD:days specifies the LastAccessDate
robocopy $SourcePath $TargetPath /MOVE /MINLAD:$DaysAgo /COPYALL /E /FP /NP /XJ /XA:H /R:5 /W:5 /LOG+:$logFile
}
Invoke-Command -ComputerName $servers -ScriptBlock $scriptBlock -Credential $cred
If you want to do all using just PowerShell, try something like this:
$servers = 'Server1', 'Server2', 'Server3' # etcetera
$cred = Get-Credential -Message "Please supply admin credentials for archiving"
$scriptBlock = {
$SourcePath = 'D:\StuffToArchive' # this is the LOCAL path on the server
$TargetPath = '\\NewServer\ArchiveShare' # this is the REMOTE path to where the files should be moved
$LogFile = 'D:\ArchivedFiles.txt' # write a textfile with all fie fullnames that are archived
$refDate = (Get-Date).AddDays(-130).Date # the reference date set to midnight
# set the ErrorActionPreference to Stop, so exceptions are caught in the catch block
$OldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# loop through the servers LOCAL path to find old files and move them to the remote archive
Get-ChildItem -Path $SourcePath -File -Recurse |
Where-Object { $_.LastAccessTime -le $refDate } |
ForEach-Object {
try {
$target = Join-Path -Path $TargetPath -ChildPath $_.DirectoryName.Substring($SourcePath.Length)
# create the folder in the archive if not already exists
$null = New-Item -Path $target -ItemType Directory -Force
$_ | Move-Item -Destination $target -Force
Add-Content -Path $LogFile -Value "File '$($_.FullName)' moved to '$target'"
}
catch {
Add-Content -Path $LogFile -Value $_.Exception.Message
}
}
$ErrorActionPreference = $OldErrorAction
}
Invoke-Command -ComputerName $servers -ScriptBlock $scriptBlock -Credential $cred
I am currently trying to run a .bat file on around 150 servers. I can get the script to run through as if there's no issues - the .bat copies to the servers, however it does not seem to be executing at all.
Running on windows 2012 servers mainly.
#Variables
$servers = "D:\Apps\Davetest\servers.txt"
$computername = Get-Content $servers
$sourcefile = "D:\Apps\Davetest\test.bat"
#This section will install the software
foreach ($computer in $computername)
{
$destinationFolder = "\\$computer\C$\Temp"
<#
It will copy $sourcefile to the $destinationfolder. If the Folder does
not exist it will create it.
#>
if (!(Test-Path -path $destinationFolder))
{
New-Item $destinationFolder -Type Directory
}
Copy-Item -Path $sourcefile -Destination $destinationFolder
Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process 'c:\Temp\test.bat'}
}
I am looking for it to run the .bat once it hits the servers and currently it only seems to be copying over.
That's because Start-Process immediately returns. Use the -Wait Parameter.
Start-Process -FilePath 'c:\Temp\test.bat' -NoNewWindow -Wait -PassThru
microsoft:
-PassThru
Returns a process object for each process that the cmdlet started. By default, this cmdlet does not generate any output.
-Wait
Indicates that this cmdlet waits for the specified process and its descendants to complete before accepting more input. This parameter suppresses the command prompt or retains the window until the processes finish.
-PassThru returns you a process object, where you can check the ExitCode parameter:
$p = Start-Process -FilePath your_command -ArgumentList "arg1", "arg" -NoNewWindow -Wait -PassThru
if ($p.ExitCode -ne 0) {
throw "Failed to clone $buildItemName from $buildItemUrl to $($tmpDirectory.FullName)"
}
As an alternative to Start-Process you could also use Invoke-Expression which will return you the stdout of the console.
To check if Invoke-Expression was successful you can use:
$output = Invoke-Expression $command
if ((-not $?) -or ($LASTEXITCODE -ne 0)) {
throw "invoke-expression failed for command $command. Command output: $output"
}
I already searched for this and found lot of answers. But, none of them seems to work.
I am working a script, which will be used to copy some files from local machine to remote servers. Before copying the files, I need to check if the file/folder already exists. If the folder doesn't exists, then create a new folder and then copy the files. If the file already exists at the specified location, just overwrite the file.
I got the logic on how to do this. But, for some reason Test-Path doesn't seems to work.
$server = #list of servers
$username = #username
$password = #password
$files = #list of files path to be copied
foreach($server in $servers) {
$pw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($username, $pw)
$s = New-PSSession -computerName $server -credential $cred
foreach($item in $files){
$regex = $item | Select-String -Pattern '(^.*)\\(.*)$'
$destinationPath = $regex.matches.groups[1]
$filename = $regex.matches.groups[2]
#check if the file exists on local system
if(Test-Path $item){
#check if the path/file already exists on remote machine
#First convert the path to UNC format before checking it
$regex = $item | Select-String -Pattern '(^.*)\\(.*)$'
$filename = $regex.matches.groups[2]
$fullPath = $regex.matches.groups[1]
$fullPath = $fullPath -replace '(.):', '$1$'
$unc = '\\' + $server + '\' + $fullPath
Write-Host $unc
Test-Path $unc #This always returns false even if file/path exists
if(#path exists){
Write-Host "Copying $filename to server $server"
Copy-Item -ToSession $s -Path $item -Destination $destinationPath
}
else{
#create the directory and then copy the files
}
}
else{
Write-Host "$filename does not exists at the local machine. Skipping this file"
}
}
Remove-PSSession -Session $s
}
The condition to check if the file/path exists on remote machine always fails. Not sure why.
I tried the following commands manually on powershell and the command returns true on the remote machine and false on the local machine.
On local machine:
Test-Path '\\10.207.xxx.XXX\C$\TEST'
False
On Remote machine:
Test-Path '\\10.207.xxx.xxx\C$\TEST'
True
Test-Path '\\localhost\C$\TEST'
True
So, it is clear that the command fails even if I try manually or through script. But the command passes when I try to do it from remote system or server.
But I need to check if the file exists on remote machine from local system.
Am I missing something ? Can someone help me to understand what's going on here ?
Thanks!
First of all, you're not using your PSSessions for anything. They look redundant.
If your local path is the same as the destination and you're using WMF/Powershell 4 or newer; I would suggest that you stop with your regex and UNC paths and do the following, which would simplify and drop most of your code:
$existsOnRemote = Invoke-Command -Session $s {param($fullpath) Test-Path $fullPath } -argumentList $item.Fullname;
if(-not $existsOnRemote){
Copy-Item -Path $item.FullName -ToSession $s -Destination $item.Fullname;
}
This identical code has been used in 3 servers, and only one of them does it silently fail to move the items (it still REMOVES them, but they do not appear in the share).
Azure-MapShare.ps1
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path "${DriveLetter}:"))
{
cmd.exe /c "net use ${DriveLetter}: ${StorageLocation} /u:${StorageUser} ""${StorageKey}"""
}
Get-Exclusion-Days.ps1
param (
[datetime]$startDate,
[int]$daysBack
)
$date = $startDate
$endDate = (Get-Date).AddDays(-$daysBack)
$allDays =
do {
"*"+$date.ToString("yyyyMMdd")+"*"
$date = $date.AddDays(-1)
} until ($date -lt $endDate)
return $allDays
Migrate-Files.ps1
param(
[string]$Source,
[string]$Filter,
[string]$Destination,
[switch]$Remove=$False
)
#Test if source path exist
if((Test-Path -Path $Source.trim()) -ne $True) {
throw 'Source did not exist'
}
#Test if destination path exist
if ((Test-Path -Path $Destination.trim()) -ne $True) {
throw 'Destination did not exist'
}
#Test if no files in source
if((Get-ChildItem -Path $Source).Length -eq 0) {
throw 'No files at source'
}
if($Remove)
{
#Move-Item removes the source files
Move-Item -Path $Source -Filter $Filter -Destination $Destination -Force
} else {
#Copy-Item keeps a local copy
Copy-Item -Path $Source -Filter $Filter -Destination $Destination -Force
}
return $True
The job step is type "PowerShell" on all 3 servers and contains this identical code:
#Create mapping if missing
D:\Scripts\Azure-MapShare.ps1 -DriveLetter 'M' -StorageKey "[AzureStorageKey]" -StorageLocation "[AzureStorageAccountLocation]\backup" -StorageUser "[AzureStorageUser]"
#Copy files to Archive
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "D:\Databases\BackupArchive"
#Get date range to exclude
$exclusion = D:\Scripts\Get-Exclusion-Days.ps1 -startDate Get-Date -DaysBack 7
#Remove items that are not included in exclusion range
Remove-Item -Path "D:\Databases\BackupArchive\*.bak" -exclude $exclusion
#Move files to storage account. They will be destroyed
D:\Scripts\Migrate-Files.ps1 -Source "D:\Databases\Backup\*.bak" -Destination "M:\" -Remove
#Remove remote backups that are not from todays backup
Remove-Item -Path "M:\*.bak" -exclude $exclusion
If I run the job step using SQL then the files get removed but do not appear in the storage account. If I run this code block manually, they get moved.
When I start up PowerShell on the server, I get an error message: "Attempting to perform the InitializeDefaultDrives operation on the 'FileSystem' provider failed." However, this does not really impact the rest of the operations (copying the backup files to BackupArchive folder, for instance).
I should mention that copy-item also fails to copy across to the share, but succeeds in copying to the /BackupArchive folder
Note sure if this will help you but you could try to use the New-PSDrive cmdlet instead of net use to map your shares:
param (
[string]$DriveLetter,
[string]$StorageLocation,
[string]$StorageKey,
[string]$StorageUser
)
if (!(Test-Path $DriveLetter))
{
$securedKey = $StorageKey | ConvertTo-SecureString -AsPlainText -Force
$credentials = New-Object System.Management.Automation.PSCredential ($StorageUser, $securedKey)
New-PSDrive -Name $DriveLetter -PSProvider FileSystem -Root $StorageLocation -Credential $credentials -Persist
}
Apparently I tricked myself on this one. During testing I must have run the net use command in an elevated command prompt. This apparently hid the mapped drive from non-elevated OS features such as the Windows Explorer and attempts to view its existence via non-elevated command prompt sessions. I suppose it also was automatically reconnecting during reboots because that did not fix it.
The solution was as easy as running the net use m: /delete command from an elevated command prompt.