PowerShell 101: Instantly Copy Files and Auto-Install Applications on Multiple Servers
Photo by İsmail Enes Ayhan on Unsplash
Use Case:
Managing multiple servers often involves repetitive tasks, such as installing specific software. Manual installation on each server can be time-consuming and prone to errors. I will demonstrate how to use PowerShell to automate the installation.
My sample application is Notepad++ which will be installed to several servers in my lab environment.
You can download the Notepad++ installer from the official website.
Solution:
Will use PowerShell script with few functions. In addition, will create log file for reference and troubleshoot purpose.
Result:
Using the PowerShell script, Notepad++ will be installed on multiple servers, and a log file will be created. This eliminates the need for manual installation on each server. Full code at the end of article.
Lets get started!
Pre-requisite
Ensure each server has access to the network location where the Notepad++ installer is stored.
Ensure PowerShell Remoting is enabled on all servers.
Ensure the user running the script has administrative privileges on all target servers.
In production environment, you need to make sure network and firewall settings allow for PowerShell Remoting and access to the network share.
$installerLocalPath = "C:\CopyFiles\npp.8.6.7.Installer.x64.exe"
$installerRemotePath = "C:\Program Files\npp.8.6.7.Installer.x64.exe"
$serverList = "C:\Copyfiles\server.txt"
$installerArguments = "/S" # Silent install argument for Notepad++
$servers = Get-Content -Path $serverList
$logFilePath = "C:\CopyFiles\install_log.txt"
$installerLocalPath
the variable for Notepad++ installer as source file
$installerRemotePath
Destination where the installer will be copied
$serverList
is text file that contain list of server. For test purpose I just use two servers in Lab Environment as below. Can use IP address but of course not convenient
$installerArguments
is set to /S
which the installer will run in silent mode without user interaction. There are more other option like:
/D=<path>
to specify installation directory
/noDesktopShortcut
will not create desktop shortcut
/noUpdater
prevent automatic updates especially in controlled environment.
Best practice is specify installation directory and silent installation /S
to avoid manual intervention
$servers = Get-Content -Path $serverList
Reads the content for specified file in $serverList variable. Each line inside the file represent different server
Start-Transcript -Path $logFilePath -Append
Start a transcript which will log all output from the script. It will append to the file if it already exist.
foreach ($server in $servers) {
try {
$session = New-PSSession -ComputerName $server
Copy-Item -Path $sourcepath -Destination $destinationPath -ToSession $session -Force
Write-Host "Copy to $server successful"
foreach ($server in $servers)
will create a loop based on server.
I'm using try
to handle any error during execution of the command will pass information.
$session = New-PSSession -ComputerName $server
will create PowerShell session to server specified which is variable $server
Copy-Item -Path $sourcepath -Destination $destinationPath -ToSession $session -Force
:It will copy installer from $sourcePath
and paste it to $destinationPath
.
-ToSession $session
is needed to specify to which remote session it will be copied. If not, it will copy the file locally and not into the remote servers
-force
will overwrite if there is existing file
# Command to install the software
$installCommand = {
param($installerPath, $installerArguments)
Start-Process -FilePath $installerPath -ArgumentList $installerArguments -Wait}
param($installerPath, $installerArguments)
there is two parameters. The installer path and also installer arguments. As we declare before installer path and $installerArguments
we already set as /s
which will be silent installation.
Start-Process -FilePath $installerPath -ArgumentList $installerArguments -Wait
Start-Process
will start new process
-Wait
will ask PowerShell to wait for the process to complete before proceeding to the next command. This ensures that the installation finishes before moving on.
Later, it will be use Invoke-command
to run all parameters
Invoke-Command -Session $session -ScriptBlock $installCommand -ArgumentList $softwareInstallerPath, $installerArguments
Write-Host "Software installation on $server successful"
Invoke-Command
will run a command or script block on remote computers
It passes the installer path and arguments to the script block.
If success, output "Software installation on $server successful"
catch { Write-Host "Error during operation on {$server}: $_"}
Any errors that occur during the try block and outputs an error message to the console, including the server name and the error details
} finally { Remove-PSSession -Session $session Write-Host "Session ended for $server" }
Ensures that the PowerShell session to the server is removed. This is best practice to make sure after work completed, remove remote session.
Stop-Transcript
Stop the log. and it will generate the txt file.
FULL SCRIPT
$installerLocalPath = "C:\CopyFiles\npp.8.6.7.Installer.x64.exe"
$installerRemotePath = "C:\Program Files\npp.8.6.7.Installer.x64.exe"
$serverList = "C:\Copyfiles\server.txt"
$installerArguments = "/S" # Silent install argument for Notepad++
$servers = Get-Content -Path $serverList
$logFilePath = "C:\CopyFiles\install_log.txt"
Start-Transcript -Path $logFilePath -Append
foreach ($server in $servers) {
try {
$session = New-PSSession -ComputerName $server
# Copy the installer to the remote server
Copy-Item -Path $installerLocalPath -Destination $installerRemotePath -ToSession $session -Force
Write-Host "Installer copy to $server successful"
# Command to install the software
$installCommand = {
param($installerPath, $installerArguments)
Start-Process -FilePath $installerPath -ArgumentList $installerArguments -Wait
}
Invoke-Command -Session $session -ScriptBlock $installCommand -ArgumentList $installerRemotePath, $installerArguments
Write-Host "Software installation on $server successful"
}
catch {
Write-Host "Error during operation on {$server}: $_"
}
finally {
Remove-PSSession -Session $session
Write-Host "Session ended for $server"
}
}
Stop-Transcript
Improvement
Can have unit test to validate functionality
GUI version where can be user-friendly like can browse the resource file etc.
Let me know if you have any comment.