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"
}
Related
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
Environment:
System Center Configuration Manager 1810
Workstations = Windows 10 1709 / Windows 7 SP1
Application spécific with add-on script to accomplish the tasks
Way to accomplish it:
Deploy package without licence
Push licence file
Stop service
Read licence with SCCM tack and push it if necessary
Start service
Hundreds of computers are affected and the editor doesn't submit a guideline to accomplish it without rebooting computers without prompting end-users.
We are using SCCM to deploy and check packages (WMI query, registry, ...). We can use, powershell to query more objects, like reading the licence file to check if it is the good one.
Reading licence is done this way:
if ((Get-Content "C:\Program Files\XXX\X.LIC") -contains serial_no=XXXXX")) {
Write-Host "License OK"
}
If the licence, isn't the good one, a little is launched on the workstation (somewhere in a folder like C:\Windows\CCMCACHE\a)
like
If (Test-Path ("C:\Program Files (x86)\NetSupport\NetSupport School"))
{
If (Test-Path ("C:\Program Files (x86)\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files (x86)\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Source $PSScriptRoot\X.LIC -Destination ("C:\Program Files (x86)\XXX") -Force
}
ElseIf(Test-Path ("C:\Program Files\XXX"))
{
If (Test-Path ("C:\Program Files\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Source $PSScriptRoot\X.LIC -Destination ("C:\Program Files\XXX") -Force
}
Do I need PowerShell v3? How can i do it with PSv2 ?
First of all, the Copy-Item was wrong -> -Path instead of -Source
And, in Windows PowerShell 2.0, $PSScriptroot is valid only in script modules (.psm1). Beginning in Windows PowerShell 3.0, it is valid in all scripts.
So, i do it like this :
$scriptpath = split-path -parent $MyInvocation.MyCommand.Definition
If (Test-Path ("C:\Program Files (x86)\XXX"))
{
If (Test-Path ("C:\Program Files (x86)\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files (x86)\XXX\X.LIC" -NewName ("NSM.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Path ($scriptpath + "\X.LIC") -Destination ("C:\Program Files (x86)\XXX") -Force
}
ElseIf(Test-Path ("C:\Program Files\XXX"))
{
If (Test-Path ("C:\Program Files\XXX\X.LIC"))
{
Rename-Item -Path "C:\Program Files\XXX\X.LIC" -NewName ("X.LIC." + (Get-Date).ToString("yyyyMMdd")) -Force
}
Copy-Item -Path ($scriptpath + "\X.LIC") -Destination ("C:\Program Files\XXX") -Force
}
Thanks for your help ^^
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 have an interesting situation going on where an agent was installed and it communicates back to the server. This agent is now EOL and support with the vendor was not renewed. Many of our agents are no longer communicating back to the server because its on stuck calling back to our DMZ network where the server was decommissioned. The agent has a tamper protection with password and the old admin doesn't know the password.
What I found we can do is on these servers, install the agent again and then the appliance that is on our inside network change the password to whatever, for my case I did blank.
To make things worse the agent is installed in either Program Files, Program Files (x86) and in some cases it's installed twice on the same server.
That's the background of the script I'm trying to develop. My idea was to download the agent from the appliance, detect the architecture and run the install then run an uninstall and loop it for each Program Files and/or Program Files(x86). I have it working OK, but it's not removing the application from the x86 directory. Also, I was hoping to grab some formatting tips.
if ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "64-bit") {
# Download Application
$source = "http://heartrate0001/x64/HeartRate.exe"
$filename = [System.IO.Path]::GetFileName($source)
$destination = "$ENV:USERPROFILE\Desktop\$filename"
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($source,$destination)
$patha = "C:\Program Files\ICU\HeartRate.exe"
$pathb ="C:\Program Files (x86)\ICU\HeartRate.exe"
$Folder1Path = 'C:\Program Files\ICU\HeartRate.exe'
$Folder2Path = 'C:\Program Files (x86)\ICU\HeartRate.exe'
}
# Install Application
$process = Start-Process $destination -PassThru -Wait
$process.ExitCode
Write-Host $process.Exitcode
# Uninstall
if ((Test-Path -Path $Folder1Path) -eq "true") {
{
$Folder1Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder1Path $Arg1
}
}
if ((Test-Path -Path $Folder2Path) -eq "true") {
{
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
} elseif ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq "32-bit") {
# Download Application
$source = "http://heartrate0001/HeartRate.exe"
$destination = "$ENV:USERPROFILE\Desktop\$filename"
$webclient = New-Object System.Net.WebClient
$webclient.DownloadFile($source,$destination)
# Install Application
$process = Start-Process $destination -PassThru -Wait
$process.ExitCode
Write-Host $process.Exitcode
# Uninstall
$app = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $app $Arg1
}
You have a script block inside your if statement. However, you never invoke that script block.
Change:
if ((Test-Path -Path $Folder2Path) -eq "true") {
{
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
}
to:
if ((Test-Path -Path $Folder2Path) -eq "true") {
$Folder2Path = "C:\Program Files\ICU\HeartRate.exe"
$arg1 = "-uninstall"
& $Folder2Path $Arg1
}
This answer is purely for a formatting example as requested by OP. Most of your work was redundant:
If ((Get-WmiObject Win32_OperatingSystem).OSArchitecture -eq '64-bit')
{
$Source = 'http://heartrate0001/x64/HeartRate.exe'
$Destination = "$env:UserProfile\Desktop\$([IO.Path]::GetFileName($Source))"
# PSv3+
Invoke-WebRequest -Uri $Source -OutFile $Destination
$x64 = "$env:ProgramFiles\ICU\HeartRate.exe"
$x86 = "${env:ProgramFiles(x86)}\ICU\HeartRate.exe"
(Start-Process $Destination -PassThru -Wait).ExitCode
If (Test-Path $x64) { & $x64 -uninstall }
If (Test-Path $x86) { & $x86 -uninstall }
}
Else
{
$Source = 'http://heartrate0001/HeartRate.exe'
$Destination = "$env:UserProfile\Desktop\$([IO.Path]::GetFileName($Source))"
Invoke-WebRequest -Uri $Source -OutFile $Destination
$x86 = "$env:ProgramFiles\ICU\HeartRate.exe"
(Start-Process $Destination -PassThru -Wait).ExitCode
If (Test-Path $x86) { & $x86 -uninstall }
}
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.