Skip to content

Instantly share code, notes, and snippets.

@davidlu1001
Last active June 27, 2024 06:20
Show Gist options
  • Save davidlu1001/75b7a4b1adf1385b2e8c05f733073b5c to your computer and use it in GitHub Desktop.
Save davidlu1001/75b7a4b1adf1385b2e8c05f733073b5c to your computer and use it in GitHub Desktop.
Operation scripts for copy files and run commands on remote servers
## Usage Examples
#
### Execute a command on multiple remote computers
# .\ops.ps1 -ComputerName Server1, Server2 -Command "Get-Service | Where-Object {`$_.Status -eq 'Running'}"
#
### Run a script on a remote computer with arguments
# .\ops.ps1 -ComputerName Server1 -ScriptPath C:\Scripts\DiskCleanup.ps1 -ArgumentList @{DaysOld = 30; LogPath = "C:\Logs"}
#
### Copy files to multiple remote computers
# .\ops.ps1 -ComputerName Server1, Server2 -Source C:\Updates\Patch.msp -Destination C:\Temp -Recurse
#
### Use a config file to specify target computers
# .\ops.ps1 -ConfigFile C:\Servers.txt -Command "Get-EventLog -LogName System -Newest 10"
#
### Execute a command with specific credentials
# $cred = Get-Credential
# .\ops.ps1 -ComputerName RemoteServer -Command "Get-WmiObject Win32_LogicalDisk" -Credential $cred
#
### Perform a dry run of a script execution
# .\ops.ps1 -ComputerName Server1, Server2 -ScriptPath C:\Scripts\UpdateSoftware.ps1 -DryRun
#
### Execute a command and save results to a file
# .\ops.ps1 -ComputerName Server1, Server2 -Command "Get-HotFix" -OutputFile C:\Reports\InstalledUpdates.csv
#
### Limit concurrent operations
# .\ops.ps1 -ConfigFile C:\LargeServerList.txt -Command "ipconfig /all" -ThrottleLimit 5
#Requires -Version 5.0
[CmdletBinding(DefaultParameterSetName='Run')]
param (
[Parameter(ParameterSetName='Run', Mandatory=$true)]
[Parameter(ParameterSetName='Copy', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName,
[Parameter(ParameterSetName='Run')]
[ValidateNotNullOrEmpty()]
[string]$Command,
[Parameter(ParameterSetName='Run')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ScriptPath,
[Parameter(ParameterSetName='Run')]
[hashtable]$ArgumentList,
[Parameter(ParameterSetName='Copy', Mandatory=$true)]
[ValidateScript({Test-Path $_})]
[string[]]$Source,
[Parameter(ParameterSetName='Copy', Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Destination,
[Parameter(ParameterSetName='Copy')]
[switch]$Recurse,
[Parameter(ParameterSetName='Run')]
[Parameter(ParameterSetName='Copy')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$ConfigFile,
[Parameter(ParameterSetName='Run')]
[switch]$DryRun,
[Parameter(ParameterSetName='Run')]
[Parameter(ParameterSetName='Copy')]
[ValidateNotNullOrEmpty()]
[string]$OutputFile,
[Parameter(ParameterSetName='Run')]
[Parameter(ParameterSetName='Copy')]
[ValidateRange(1, 3600)]
[int]$Timeout = 300,
[Parameter(ParameterSetName='Run')]
[Parameter(ParameterSetName='Copy')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]
$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(ParameterSetName='Run')]
[Parameter(ParameterSetName='Copy')]
[ValidateRange(1, 100)]
[int]$ThrottleLimit = 10
)
function Get-RemoteHosts {
[CmdletBinding()]
param (
[string]$ConfigFile,
[string[]]$ComputerName
)
if ($ConfigFile) {
if (Test-Path $ConfigFile) {
$hosts = Get-Content $ConfigFile | Where-Object { $_ -notmatch '^\s*$' -and $_ -notmatch '^\s*#' }
if ($hosts.Count -eq 0) {
throw "No valid hosts found in config file: $ConfigFile"
}
return $hosts
} else {
throw "Config file not found: $ConfigFile"
}
} elseif ($ComputerName) {
return $ComputerName
} else {
throw "No remote hosts specified. Use either -ConfigFile or -ComputerName"
}
}
function Invoke-RemoteOperation {
[CmdletBinding()]
param (
[string[]]$ComputerName,
[hashtable]$Params
)
$scriptBlock = {
param($Params)
try {
switch ($Params.ParameterSet) {
'Run' {
if ($Params.Command) {
Invoke-Expression $Params.Command
} elseif ($Params.ScriptPath) {
& $Params.ScriptPath @Params.ArgumentList
}
}
'Copy' {
$drive = New-PSDrive -Name TempDrive -PSProvider FileSystem -Root $Params.Destination -Credential $Params.Credential -ErrorAction Stop
foreach ($src in $Params.Source) {
$dest = Join-Path -Path "TempDrive:" -ChildPath (Split-Path -Path $src -Leaf)
Copy-Item -Path $src -Destination $dest -Recurse:$Params.Recurse -Force -ErrorAction Stop
}
Remove-PSDrive -Name TempDrive
"Files copied successfully to $($Params.Destination)"
}
}
}
catch {
throw "Error on $env:COMPUTERNAME: $_"
}
}
$invokeCommandParams = @{
ScriptBlock = $scriptBlock
ComputerName = $ComputerName
ArgumentList = $Params
ErrorAction = 'Stop'
ThrottleLimit = $Params.ThrottleLimit
}
if ($Params.Credential -and $Params.Credential -ne [System.Management.Automation.PSCredential]::Empty) {
$invokeCommandParams.Credential = $Params.Credential
}
$results = Invoke-Command @invokeCommandParams | ForEach-Object {
[PSCustomObject]@{
ComputerName = $_.PSComputerName
Status = "Success"
Output = $_
}
}
return $results
}
try {
$remoteHosts = Get-RemoteHosts -ConfigFile $ConfigFile -ComputerName $ComputerName
$params = $PSBoundParameters
$params.ParameterSet = $PSCmdlet.ParameterSetName
if ($params.ParameterSet -eq 'Copy') {
$params.Source = $params.Source | ForEach-Object { Resolve-Path -Path $_ -ErrorAction Stop | Select-Object -ExpandProperty Path }
}
if ($DryRun) {
Write-Host "Dry run mode. The following operations would be performed:"
foreach ($computer in $remoteHosts) {
if ($params.ParameterSet -eq 'Run') {
if ($Command) {
Write-Host "On $computer, would execute command: $Command"
} elseif ($ScriptPath) {
Write-Host "On $computer, would execute script: $ScriptPath"
}
} elseif ($params.ParameterSet -eq 'Copy') {
Write-Host "Would copy files from $($params.Source -join ', ') to $($params.Destination) on $computer"
}
}
} else {
$results = Invoke-RemoteOperation -ComputerName $remoteHosts -Params $params
if ($results) {
$results | Format-Table -AutoSize
if ($OutputFile) {
$results | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Host "Results exported to $OutputFile"
}
} else {
Write-Host "No results obtained."
}
}
}
catch {
Write-Error "An error occurred: $_"
exit 1
}
finally {
# Ensure all jobs are stopped and removed
Get-Job | Remove-Job -Force -ErrorAction SilentlyContinue
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment