Execute SQLs in Parallel from directory - multithreading

I am able to execute all the .SQL files from directory on to the server serially using below command. But, we want to execute all these files in parallel.
My code for serial execution -
$PSScriptRoot = 'Y:\test\'
$ServerName = $env:COMPUTERNAME
foreach ($f in Get-ChildItem -path $PSScriptRoot -Filter *.sql | sort-object
-desc )
{
invoke-sqlcmd -InputFile $f.fullname -ServerInstance $ServerName
}

You could use jobs to have them execute in the background:
foreach ($f in Get-ChildItem -path $PSScriptRoot -Filter *.sql | sort-object
-desc)
{
Start-Job -ScriptBlock { invoke-sqlcmd -InputFile $args[0].fullname -ServerInstance $args[1] } -ArgumentList $f, $ServerName
}
If there is any output you can collect it via:
Get-Job | Wait-Job | Receive-Job

Related

Need to run app on N remotes hosts and handle them all

Im using this code to run app on N remote hosts and need to wait until all of them are finished and get their results. But the execution just passes throuhg, all jobs are being marked Completed and code exits.
How to make it wait till apps finished their execution?
$procArray = [System.Collections.ArrayList]#()
foreach ($key in $simulatorServers.keys) {
$unitHost = $simulatorServers[$key][0]
$EXE="C:\app.exe"
Wr-DebugReg "Running $EXE on $unitHost "
$ScriptString="Start-Process -FilePath $EXE "
$ScriptBlock=[System.Management.Automation.ScriptBlock]::Create($ScriptString)
$Session=New-PSSession -ComputerName $unitHost -EnableNetworkAccess -Name "session$counter" -Credential $crNonProd
$rez2 = Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -AsJob
$rez00=$procArray.Add($rez2)
Wr-DebugReg "Running process id=$($rez2.id) name=$($proc.Name)on $unitHost"
Wr-DebugReg ""
}
$procArray | Wait-Job
$procArray | Receive-Job
these jobs gone to status Completed even if launched processes still running
let invoke-command handle the the amount of jobs/sessions to open in parallel - you will receive 1 job with childs:
$scriptBlock = {
start-process -FilePath 'C:\app.exe'
}
$sessions = #(
foreach ($key in $simulatorServers.keys) {
$unitHost = $simulatorServers[$key][0]
New-PSSession -ComputerName $unitHost -EnableNetworkAccess -Name "session$counter" -Credential $crNonProd
}
)
$job = Invoke-Command -Session $Sessions -ScriptBlock $ScriptBlock -AsJob
$result = receive-job -Wait -Job $job
btw. I do not see, based on this sample, what you want to receive. you want to execute "start-process -FilePath 'C:\app.exe' " on the target machines but this won't give you anything back.
to get the information back modify the scriptblock like this:
$scriptBlock = {
$result = start-process -FilePath 'C:\app.exe' -wait -PassThru
return $result
}
This code is working. -Wait is the key to make it wait until all jobs are finished.
$procArray = [System.Collections.ArrayList]#()
foreach ($key in $hosts.keys) {
$unitHost = $hosts[$key][0]
$EXE="c:\app.exe"
$Session=New-PSSession -ComputerName $unitHost -EnableNetworkAccess -Name "session$counter" -Credential $crNonProd
$rez2 = Invoke-Command -Session $Session -ScriptBlock {Start-Process -FilePath $args[0] -Wait} -ArgumentList $EXE -AsJob
$rez00=$procArray.Add($rez2)
Wr-DebugReg "Starting process id=$($rez2.id) name=$($proc.Name)on $unitHost"
Wr-DebugReg ""
}
while ($procArray.State -contains 'Running')
{
Start-Sleep -Seconds 1
}

Powershell Script Can't Run Properly With Task Scheduler

I have an odd problem. I have a script that works fine when manually run it. I created a scheduled job in windows and run this script automatically. The script works fine until the last stage of script.
$deploymentfiles_mdm = Get-ChildItem 'D:\DeploymentTriggerApp\*'
Write-Host $deploymentfiles_mdm
$timestamp_app = Get-Date -Format o | ForEach-Object {$_ -replace ":", "."}
Write-Host $timestamp_app
$server = Get-Content 'C:\Users\Administrator\Desktop\Scripts\AutoDeployment\ProdMDMapps.txt'
$User = 'domain\user'
$SecurePassword = Get-Content C:\Users\Administrator\Desktop\Scripts\Password.txt | ConvertTo-SecureString
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
if (Test-Path -Path $deploymentfiles_mdm)
{
do{
try
{
$ServerSessions = New-PSSession -ComputerName $server -Credential $UserCred -ErrorAction Stop
Write-Host ("$ServerSessions")
}
catch [Exception]
{
Write-Host("Credential is incorrect or password is expired. Either change credential and run CredentialEncryption.ps1 or communicate with dc admin to open expired password!")
}
}while(!$ServerSessions)
Copy-Item "D:\Deployment_Files\*.zip" -ToSession $ServerSessions -Destination "D:\Deployment_Files\" -ErrorAction SilentlyContinue
try{
Invoke-Command -Session $ServerSessions -ScriptBlock {
param($timestampApp)
$appPath = Get-ChildItem 'D:\MDM\live\bin\'
Expand-Archive -Path 'D:\Deployment_Files\*.zip' -DestinationPath 'D:\Deployment_Files\' -Force
Remove-Item -Path 'D:\Deployment_Files\*.zip'
$nodeProcess = Get-Process | Where-Object { $_.Name -eq "node"}
if($nodeProcess -Or $appPath)
{
Get-Process | Where-Object { $_.Name -eq "node"} | Select-Object -First 1 | Stop-Process -Force
New-Item -Path 'D:\Backups\' -Name $timestampApp -ItemType 'directory'
Get-ChildItem -Path "D:\MDM\live\bin\" -Recurse | Move-Item -Destination "D:\Backups\$timestampApp\"
}
Copy-Item "D:\Deployment_Files\bin" -Destination "D:\MDM\live\" -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item "D:\Deployment_Files\*" -Recurse -Force
Start-Job -ScriptBlock{ node D:\MDM\live\bin\main.js}
} -ArgumentList $timestamp_app
}
catch
{
$_.Exception.Message
}
}
Remove-Item D:\DeploymentTriggerApp\*
In section Start-Job -ScriptBlock{ node D:\MDM\live\bin\main.js} the script can't start the node process. When I manually run it, it runs without any problem.
Any suggestions for that? (The node process needed to be in background job. If any alternative commands to that, I can also try that solution)
Below line solved my problem.
Start-Job -ScriptBlock{& 'C:\Program Files\nodejs\node.exe' D:\MDM\live\bin\main.js}

PowerShell multithreaded compress dirs

I have a rootDir with 3 dirs under it, I just want to compress each dir and listen the result by using multithreaded code, so I tried the following:
Set-Location "C:\test"
$sw = [Diagnostics.Stopwatch]::StartNew()
Get-Job | Remove-Job
$rootDirectory = $PWD
$dirs = $(Get-ChildItem -Path $rootDirectory -Directory).Name
# $dirs = d1, d2, d3
$sb = {
Param($init)
Compress-Archive -Path $init -DestinationPath "$init.zip" -CompressionLevel NoCompression
}
$jobs = #()
$dirs | ForEach-Object {
$jobs += Start-Job -ScriptBlock $sb -ArgumentList $_
}
Wait-Job -Job $jobs | Out-Null
Receive-Job -Job $jobs
I got
The path 'd1' either does not exist or is not a valid file system path.
+ CategoryInfo : InvalidArgument: (d1:String) [Compress-Archive], InvalidOperationException
+ FullyQualifiedErrorId : ArchiveCmdletPathNotFound,Compress-Archive
+ PSComputerName : localhost
if I run the compress command serially, not using the Start-Job, all seems to work.
The jobs don't inherit their working directory directory from your script. Put the Set-Location inside the scriptblock and pass the directory as a parameter:
$rootdir = 'C:\test'
...
$jobs = Get-ChildItem -Path $rootdir -Directory | ForEach-Object {
Start-Job -ScriptBlock {
Param($init, $dir)
Set-Location $dir
Compress-Archive -Path $init -DestinationPath "${init}.zip" -CompressionLevel NoCompression
} -ArgumentList $_, $rootdir
}
...
Better yet, pass the full path as the parameter, not just the name:
$rootdir = 'C:\test'
...
$jobs = Get-ChildItem -Path $rootdir -Directory | ForEach-Object {
Start-Job -ScriptBlock {
Param($init)
Compress-Archive -Path $init -DestinationPath "${init}.zip" -CompressionLevel NoCompression
} -ArgumentList $_.FullName
}
...

PowerShell Mutex & Jobs

I'm trying to run a PowerShell file delete operation that deletes files based on specific parameters. So, the code goes like this:
$sb = {
Get-ChildItem -Path $SubFolder | Remove-ChildItem
if ($Error[0] -eq $null) {
$results = New-Object PSObject -Property #{
$Foldername = $Subfolder.Name
$TotalFiles = $SubFolder.(Count)
}
} else {
$errorresult = "Error deleting files from $($Subfolder.Name)"
}
#Error Mutex
$ErrorLogmutex = New-Object System.Threading.Mutex($false, "ErrorLogFileHandler")
$ErrorLogmutex.WaitOne()
Out-File -Append -InputObject $errorresult -FilePath $Errorlog
$ErrorLogmutex.ReleaseMutex()
#Success Mutex
$Successmutex = New-Object System.Threading.Mutex($false, "SuccessFileHandler")
$SuccessLogmutex.WaitOne()
Out-File -Append -InputObject $results -FilePath $successlog
$Successmutex.ReleaseMutex()
}
#Calling scriptblock in multi-thread count of 5
{
foreach ($Subfolder in $Folder) {
while ((Get-Job -State Running).Count -ge 5) {
Start-Sleep -Seconds 5
}
Start-Job -ScriptBlock $sb -ArgumentList $arguments | Out-Null
}
The script runs, able to see the results if I explicitly call out Receive-Job -Name JobID, but the output does not produce any log files as I would've thought it would.

Wait until all threads complete before running next task

I would wrap everything inside foreach($computer in $computers) in a Start-Job to make them run simultaneously. The only problem is, I need to wait for all the jobs to complete before I do the ConvertTo-Json at the bottom.
$sb = "OU=some,OU=ou,DC=some,DC=domain"
$computers = Get-ADComputer -Filter {(Enabled -eq $true)} -SearchBase "$sb" -Properties *
$hasmanufacturer = New-Object System.Collections.Generic.List[System.Object]
foreach($computer in $computers)
{
$drives = try{#(Get-WMIObject -Class Win32_CDROMDrive -Property * -ComputerName $computer.Name -ErrorAction Stop)} catch {$null}
foreach($drive in $drives)
{
if($drive.Manufacturer)
{
$hasmanufacturer.Add($computer)
continue
}
} # inner foreach
}
ConvertTo-Json $hasmanufacturer
Use a Get-Job | Wait-Job before executing the ConvertTo-Json
How about using the array of computer names as a parameter to Invoke-Command. It will run, by default, 32 concurrent remote sessions. The number can be changed with the -Throttle parameter.
$computers = Get-ADComputer -Filter {(Enabled -eq $true)} -SearchBase "OU=Servers,DC=xxx,DC=com" -Properties Name |
Where-Object { $_.Name -match 'LAX_*' } |
ForEach-Object { $_.Name }
$computers
$j = Invoke-Command `
-ComputerName $computers `
-ScriptBlock { Get-WMIObject -Class Win32_CDROMDrive -Property * -ErrorAction Stop } `
-AsJob
while ( (Get-Job -Id $j.Id).Status -eq 'Running') {}
Get-Job -Id $j.Id | Wait-Job
$results = Receive-Job -Id $j.Id
$results

Resources