asked on
Powershell read file name into variable and number
Basically I'm trying to create a file, starting with comptuerlist1, but only if comptuerlist1 doesn't exist, then the next available number.
Any help is appreciated.
# Split inputlist.txt into blocks of 5
$linecount = 0
$filenumber = 1
# 13 over +1 into variable...
$sourcefilename = "c:\script\source\computerlist.txt"
$destinationfolderpath = "c:\Script\"
$destinationfilesize = "5"
$maxsize = [int]$destinationfilesize
Remove-Item -path $destinationfolderpath\computerlist* # remove, put in win10qa without wildcard.
Get-Content $sourcefilename | Measure-Object | ForEach-Object { $sourcelinecount = $_.Count }
$content = get-content $sourcefilename | % {
Add-Content $destinationfolderpath\computerlist$filenumber.txt "$_"
$linecount ++
If ($linecount -eq $maxsize) {
$filenumber++
$linecount = 0
}
}
Clear-Content $sourcefilename
# Split inputlist.txt into blocks of 5
$fileIndexStart = 1
$fileIndexLength = 3
$fileNameTemplate = 'computerlist{0}.txt'
# 13 over +1 into variable...
$sourceFile = "C:\script\source\computerlist.txt"
$destinationFolder = "C:\Script"
$destinationFilesize = 5
# Remove-Item -Path "$($destinationFolder)\$($fileNameTemplate -f '*')" # remove, put in win10qa without wildcard.
$sourcelinecount = Get-Content -Path $sourceFile | Measure-Object | Select-Object -ExpandProperty Count
$linecount = 0
$currentFileIndex = $fileIndexStart
$destinationFile = $null
Get-Content -Path $sourceFile | ForEach-Object {
If ((-not $destinationFile) -or ($linecount -eq $destinationFilesize)) {
$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
While (Test-Path -Path $destinationFile) {
$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
}
Write-Host "Creating new file '$([IO.Path]::GetFileName($destinationFile))'"
$linecount = 0
}
Add-Content -Path $destinationFile -Value $_
$linecount++
}
Clear-Content -Path $sourceFile
ASKER
oBdA - this is it exactly - thank you!! I'm trying to tie this into my scripts to make this name somewhat dynamic - right now, I'm hard coding each input file (comuterlistxxx.txt into separate scripts and executing them in a wrapper.
If you can help, here is what I have, I very much appreciate your help!
1. I'm using the script you just updated to prepare the directory with input files I'm running against. I have been hard coding each input file to match each script (script1.ps1) below matches computerlist001.txt for example. (this is because I'm using workflow, with parallel processing and inline block which is a limit of 5)
2. After the directory and input files are prepared, I'm running one script per input file in a wrapper.ps1 below.
I would like to tie in somehow - not sure if this would be in the wrapper, or the script1.ps1 the newly generated computerlistxxx.txt into a variable I can use per each script, then delete after completed processing.
# Wrapper.ps1
$Dest = "c:\script"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script1-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script2-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script3-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script4-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script5-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script6-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script7-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script8-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script9-ps1"
Start-Process powershell -verb runas -ArgumentList "-file $Dest\script10-ps1"
# script1.ps1
$OutArray = @()
workflow foreachrerun {
param([string[]]$computers)
foreach –parallel ($computer in $computers) {
InlineScript {
$n = 5
$m = 1
function Test-MyConnection {
param (
[Parameter(Mandatory=$true)]
[System.String]
$ComputerName
)
Test-Connection $computername -Quiet -Count 2
}
# Misc $variables here
# Misc Functions here
# Misc Functions here
# Misc Functions here
# Misc Functions here
# function call block using iterations
For ($i=1; $i -le $n; $i++) {
foreach ( $computer in $using:computer )
{
If ( Test-MyConnection -ComputerName $computer )
{
#Function calls here
#Function calls here
#Function calls here
#Function calls here
#Function calls here
}
Else
{
Write-Output "$($using:computer) is offline, $i, check power state. Logged in Offline.log"
write-output "$using:computer Offline, $Today, $i, $Runninguser" | Out-File $OfflineLog -Append -ErrorAction SilentlyContinue
}
}
}
}
}
}
$ComputerList = Get-Content "C:\Script\computerlist001.txt"
foreachrerun -Computers $ComputerList
This is the wrapper; it collects the files it created and then starts a new process for each one.
# Absolute path to the worker script:
$workScript = "C:\script\source\Script.ps1"
# Split inputlist.txt into blocks of 5
$fileIndexStart = 1
$fileIndexLength = 3
$fileNameTemplate = 'computerlist{0}.txt'
# 13 over +1 into variable...
$sourceFile = "C:\script\source\computerlist.txt"
$destinationFolder = "C:\Script"
$destinationFilesize = 5
# Remove-Item -Path "$($destinationFolder)\$($fileNameTemplate -f '*')" # remove, put in win10qa without wildcard.
$sourcelinecount = Get-Content -Path $sourceFile | Measure-Object | Select-Object -ExpandProperty Count
$linecount = 0
$currentFileIndex = $fileIndexStart
$destinationFile = $null
$files = Get-Content -Path $sourceFile | ForEach-Object {
If ((-not $destinationFile) -or ($linecount -eq $destinationFilesize)) {
$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
While (Test-Path -Path $destinationFile) {
$destinationFile = "$($destinationFolder)\$($fileNameTemplate)" -f ($currentFileIndex++).ToString("D$($fileIndexLength)")
}
Write-Host "Creating new file '$([IO.Path]::GetFileName($destinationFile))'"
$linecount = 0
$destinationFile
}
Add-Content -Path $destinationFile -Value $_
$linecount++
}
$files | ForEach-Object {
Write-Host "Starting new powershell instance with '$($_)' ..."
Start-Process -FilePath powershell.exe -Verb runas -ArgumentList "-File `"$($workScript)`" -ComputerList `"$($_)`""
}
Clear-Content $sourceFile
Worker script:
Param(
[String]$ComputerList
)
Write-Host "Processing $($ComputerList) ..."
# Demo only:
Start-Sleep -Seconds 10
# ...
ASKER
oBdA - I'm sorry, could you please clarify a little, I don't have much experience passing arguments and am little confused about how to setup the worker script..
Would I put that block in my script1.ps1 or make changes to accept the input in my script1.ps1 in place of these lines?
$ComputerList = Get-Content "C:\Script\computerlist001.txt"
foreachrerun -Computers $ComputerList
Thanks for your patience, I put the full version of my script.ps1 for reference.
$OutArray = @()
workflow foreachrerun {
param([string[]]$computers)
foreach –parallel ($computer in $computers) {
InlineScript {
$n = 3 # Number of iterations through the patch process
$m = 1 # Number of minutes for the script to pause between iterations of the loop
# (1) Function Test-Connection to determine if online****************|
function Test-MyConnection {
param (
[Parameter(Mandatory=$true)]
[System.String]
$ComputerName
)
if (Test-Connection $computername -Quiet -Count 2) {
Write-Output "$($using:computer) is online"
}
else {
Write-Output "$($using:computer) is offline - check if powered on"
Send-EmailOffline -ComputerName $computername
}
} # End (1)
# (2) Function to detect missing or pending SCCM updates*************|
#Function Detect-Missing {
# } # End (2)
# (3) Function detect pending reboot*********************************|
Function Start-PendingReboot {
$rebootPending = Invoke-CimMethod -Namespace root/ccm/ClientSDK -ClassName CCM_ClientUtilities -MethodName DetermineIfRebootPending
if ($rebootPending.RebootPending){
Invoke-CimMethod -Namespace root/ccm/ClientSDK -ClassName CCM_ClientUtilities -MethodName RestartComputer
Write-Output "$using:computer PENDING REBOOT detected, rebooting"
}
} # End (3)
# (4) Function run advert********************************************|
Function Start-CCMRerunAdvertisement {
[CmdLetBinding()]Param(
[Parameter(Mandatory=$true)][string]$computerName,
[Parameter(Mandatory=$false)][string]$advertisementId = "*",
[Parameter(Mandatory=$false)][string]$packageId = "*",
[Parameter(Mandatory=$false)][int]$maxRun = 1
#[Parameters(Mandatory=$false)][switch]$moreThanPing = $false
)
if($advertisementId -eq "*" -and $packageId -eq "*") {
Write-Error "You must supply either an AdvertisementID or a PackageID"
return "Missing Parameters"
break
}
$searchString = "$advertisementId-$packageId-*"
if(!(Test-Connection -ComputerName $computername -ErrorAction SilentlyContinue)) {
if($moreThanPing) {
if(!(Get-ChildItem "\\$computername\c$" -ErrorAction SilentlyContinue)) {
Write-Error "System Offline"
Return "System Offline"
break
}
} else {
Return "$Computername is from Advert, check PC manually."
break
}
}
Write-Verbose "Getting ID of ScheduleMessage on $computername"
$schMsgs = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_Scheduler_ScheduledMessage
$thisMsg = $schMsgs | ? { $_.ScheduledMessageID -like $searchString } | Sort ActiveTime -Descending | select -First $maxRun
if(!$thisMsg) {
Write-Verbose "Cannot Find Advertisement/Package on Target Computer"
Return "Cannot Find Advertisment"
break
}
$thisMsg | % {
[xml]$activeMessage = $_.activeMessage
$amProgramId = $activeMessage.SoftwareDeploymentMessage.ProgramID
$amAdvId = $activeMessage.SoftwareDeploymentMessage.AdvertisementID
$amPkgId = $activeMessage.SoftwareDeploymentMessage.PackageID
$ScheduledMessageId = $_.ScheduledMessageId
Write-Verbose "Restarting $amArogramId (ADV=$amAdvId) (PKG=$amPkgId) for Schedule Message $ScheduledMessageId"
$softwareDist = Get-WmiObject -ComputerName $computername -Namespace "root\ccm\policy\machine\actualconfig" -Class CCM_SoftwareDistribution -Filter "ADV_AdvertisementID = '$amAdvId' and PKG_PackageID = '$amPkgId'"
$original_Rerun = $softwareDist.ADV_RepeatRunBehavior
if($original_Rerun -ne "RerunAlways") {
write-verbose "Changing Rerun Status from $original_Rerun to RerunAlways"
$softwareDist.ADV_RepeatRunBehavior = "RerunAlways"
$softwareDist.put() | Out-Null
}
Write-Verbose "Triggering Schedule on $computername"
Invoke-WmiMethod -ComputerName $computername -Namespace "root\ccm" -Class "SMS_CLIENT" -Name TriggerSchedule $ScheduledMessageId | Out-Null
Write-Verbose "Sleeping for 5 seconds"
Start-Sleep -Seconds 5
if($original_Rerun -ne "RerunAlways") {
Write-Verbose "Changing Rerun Status back to $original_Rerun"
$softwareDist.ADV_RepeatRunBehavior = "$original_Rerun"
$softwareDist.put() | Out-Null
}
#Return "Advert run on $computername"
}
} # End (4)
# (5) Function Invoke all available updates pending******************|
# Reference install all updates:
#([wmiclass]'ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager').InstallUpdates([System.Management.ManagementObject[]] (get-wmiobject -query 'SELECT * FROM CCM_SoftwareUpdate' -namespace 'ROOT\ccm\ClientSDK')) | Out-Null
function Invoke-SupInstall
{
Param
(
[String][Parameter(Mandatory=$True, Position=1)] $Computername,
[String][Parameter(Mandatory=$True, Position=2)] $SupName
)
Begin
{
$AppEvalState0 = "0"
$AppEvalState1 = "1"
$ApplicationClass = [WmiClass]"root\ccm\clientSDK:CCM_SoftwareUpdatesManager"
}
Process
{
If ($SupName -Like "All" -or $SupName -like "all")
{
Foreach ($Computer in $Computername)
{
#Write-Output "$using:computer (1) invoking adverts and installation of all updates, iteration # $i"
$Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState0)*" -or $_.EvaluationState -like "*$($AppEvalState1)*"})
Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer | Out-Null -ErrorAction SilentlyContinue
}
}
Else
{
Foreach ($Computer in $Computername)
{
#Write-Output "$using:computer (2) invoking adverts and installation of all updates, iteration # $i"
$Application = (Get-WmiObject -Namespace "root\ccm\clientSDK" -Class CCM_SoftwareUpdate -ComputerName $Computer | Where-Object { $_.EvaluationState -like "*$($AppEvalState)*" -and $_.Name -like "*$($SupName)*"})
Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$Application) -Namespace root\ccm\clientsdk -ComputerName $Computer | Out-Null -ErrorAction SilentlyContinue
}
}
}
End {}
} # End (5)
# (6) Function Process monitoring, logs, reboot or shutdown**********|
function Process-Monitor {
param (
[Parameter(Mandatory=$true)]
[System.String]$ComputerName
)
#Write-Output "$using:computer monitoring process count 'r'n"
$processList = @'
"Name", "Expected", "Running"
"cmd", "1", "0"
"TrustedInstaller", "1", "0"
"TIWorker", "1", "0"
"OfficeClickToRun", "2", "0"
'@ | ConvertFrom-Csv | ForEach-Object {$_.Expected = [int]$_.Expected; $_}
$splat = @{}
$splat['ComputerName'] = $computerName
#Write-Output "Monitoring processes on $($computerName)"
Do {
$processList | ForEach-Object {
$_.Running = @(Get-Process $_.Name @splat -ErrorAction SilentlyContinue).Count
}
($processList | Format-Table -AutoSize | Out-String).Split("`r`n", [StringSplitOptions]::RemoveEmptyEntries) | Write-Output
If ($running = @($processList | Where-Object {$_.Running -ge $_.Expected}).Count) {
Start-Sleep -Seconds 5
}
Write-Output "$($using:computer) process monitoring loop..."
} Until (-not $running)
$Today = (Get-Date).ToString('MM-dd-yyyy'),
$ExactTime = Get-Date -Format "MM-dd-yyyy HHmm tt"
$runningUser = & "whoami.exe"
$Logfile = "c:\scripts\UPDATEQA.csv"
$myobj = "" | Select "Computer", "ExactTime", "Admin"
$myobj.Computer = $ComputerName
$myobj.ExactTime = $($(Get-Date).ToString('yyyy-MM-dd::hh:mm:ss'))
$myobj.Admin = $runningUser
$OutArray += $myobj
$OutArray | Export-Csv $Logfile -NoTypeInformation -Append
#Return "Process count finished on $computername"
###Restart computer for all iterations when process count is satisfied, except last defined, then issue shutdown. Write to event logs
if($i -eq 3){
write-output "Final iteration #$i finished on $using:computer, Powering Off."
Send-EmailComplete -ComputerName $computername
##################################################
#SHUTDOWN /s /f /t 0 /m \\$using:computer/c
##################################################
New-EventLog -LogName Application -Source UPDATEQA -ErrorAction SilentlyContinue
Write-EventLog -LogName Application -Source UPDATEQA -EventId 555 -EntryType Information -Message "UPDATEQA $($computername) final iteration completed by $runninguser" -ComputerName $computername
} else {
New-EventLog -LogName Application -Source UPDATEQA -ErrorAction SilentlyContinue
Write-EventLog -LogName Application -Source UPDATEQA -EventId 556 -EntryType Information -Message "UPDATEQA $($computername) iteration $i complete, run by $runninguser" -ComputerName $computername
write-output "Iteration #$i finished on $using:computer, initiating reboot."
##################################################
Restart-Computer -Computer $using:computer -Force
##################################################
Write-Output "$using:computer Sleeping for ($m*350)"
Start-Sleep -Seconds ($m*350) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript
}
} # End (6)
# (6a) Funtion Send Email ********************************************|
function Send-EmailOffline {
Send-MailMessage -From “admin@domain.com" -To “admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) is offline! Scipt ran by $runninguser" -Body "$($ComputerName) is Offline! $runninguser, check PC is powered on."
}
function Send-EmailComplete {
Send-MailMessage -From “admin@domain.com" -To “admin1@domain.com" -SMTPServer mail.domain.com -Subject “$($computername) completed by $runninguser" -Body "$using:computer completed, ready for deployment. Executed by $runningUser"
} # End (6a)
# Main Function Calls ****************************************************|
For ($i=1; $i -le $n; $i++) { # Begin looping $n times - $n is defined at the top of the InlineScript
# (1) Write-Output "$using:computer starting Test-MyConnection function for ping
Test-MyConnection -ComputerName $using:Computer
# (2) Write-Output "$using:computer detected missing or pending updates, iteration $i"
#Detect-Missing -ComputerName $using:Computer
# (3) Write-Output "$using:computer PENDING REBOOT detected, rebooting
Start-PendingReboot -ComputerName $using:Computer
# (4) Write-Output "$using:computer invoking advert iteration # $i"
Start-CCMRerunAdvertisement –ComputerName $using:Computer -AdvertisementID "TIC62990"
# (5) Write-Output "$using:computer invoking adverts and installation of all updates, iteration # $i"
Invoke-SupInstall -Computername $using:computer -SUPName All
#Write-Output "$using:computer Sleeping for ($m*30)"
#Start-Sleep -Seconds ($m*30) # Wait $m minutes for computer to reboot - $m is defined at the top of the InlineScript
# (6) Write-Output "$using:computer process monitoring iteration # $i"
Process-Monitor -ComputerName $using:computer
}
} # End of InlineScript
} # End of ForEach -parallel
} # End of initial ForEachRerun workflow
$ComputerList = Get-Content "c:\scripts\computerlist.txt"
foreachrerun -Computers $ComputerList
if ( -not (Test-Path $sourcefilename) ) {
#Process file
}