Powershell - foreach to array as job (local / multi-threaded) - arrays

I'm trying to parse a site to collect price and product details. The script works in a loop however it's very slow. So I'm trying to run a multi-threaded powershell script as a job.
I've tried a lot of suggestions but I'm struggling to get the results out even though I can see its working (the web-request screen flashing up)
I'm only selecting the last 10 but I'll put in a throttle later. Just can't get it to output. Essentially I'd like all results to flow back into $arr.
#Import Danmurphy Sitelist
[xml] $XmlDocument = (New-Object System.Net.WebClient).DownloadString("http://www.example.com/sites.xml")
#get websites listed
$ImportedProducts = $XmlDocument.DocumentElement.url | select -Last 10
"Killing existing jobs . . ."
Get-Job | Remove-Job -Force
"Done."
#loop through the products
#Create Array
$arr = #()
#$argumentlist
#ScriptBlock
$ScriptBlock = {
Param($product,$arr)
if ($product.loc -like "http://www.example.com/product/*"){
$uri = $product.loc
$WebResponse = Invoke-WebRequest -Uri $uri -SessionVariable WS
#mainpricetest
$mainprice = $WebResponse.AllElements | ? { $_.Class -eq 'price-main' } | select innerText
$MainPriceArray = $mainprice.innerText.Split(' ')
$MainUnitArry = $MainPriceArray[1..10]
$MainDollar = $MainPriceArray[0]
$MainUnit = $MainUnitArry -join ' '
$item = New-Object PSObject
$item | Add-Member -type NoteProperty -Name 'Product Site' -Value $($product.loc)
$item | Add-Member -type NoteProperty -Name 'Main Price' -Value $($MainDollar)
$item | Add-Member -type NoteProperty -Name 'Main Unit' -Value $($MainUnit)
$arr += $item
}
}
foreach ($product in $ImportedProducts){
Start-Job -InputObject $ImportedProducts -ScriptBlock $ScriptBlock -ArgumentList $product,$arr
}
$data = Get-Job * | Receive-Job
#Show Array
$arr

So you would want to use runspaces for that. Runspaces is a pretty complicated thing, luckily we have Posh-RSJob which handles everything for you. https://github.com/proxb/PoshRSJob
You can pass in the script block, so you would need very little adjustments.
Probably something like this:
foreach ($product in $ImportedProducts){
Start-RSJob -ScriptBlock $ScriptBlock
}
Get-RSjob | Receive-RSJob

If you want to get the results into $arr, you can't do it from within the script block as you are attempting to do. Multiple script blocks running in parallel cannot be allowed to access a single copy of a variable without taking additional steps not worth getting into.
The answer to your problem is going to be to write the output of each script block as regular output. That output is buffered until you use Receive-Job to get the results out of the job at which time you capture it into the $arr variable in a single threaded manner. Below is cod which should get you most of the way there.
#Import Danmurphy Sitelist
[xml] $XmlDocument = (New-Object System.Net.WebClient).DownloadString("http://www.example.com/sites.xml")
#get websites listed
$ImportedProducts = $XmlDocument.DocumentElement.url | select -Last 10
"Killing existing jobs . . ."
Get-Job | Remove-Job -Force
"Done."
#loop through the products
#Create Array
$arr = #()
#$argumentlist
#ScriptBlock
$ScriptBlock = {
Param($product)
if ($product.loc -like "http://www.example.com/product/*"){
$uri = $product.loc
$WebResponse = Invoke-WebRequest -Uri $uri -SessionVariable WS
#mainpricetest
$mainprice = $WebResponse.AllElements | ? { $_.Class -eq 'price-main' } | select innerText
$MainPriceArray = $mainprice.innerText.Split(' ')
$MainUnitArry = $MainPriceArray[1..10]
$MainDollar = $MainPriceArray[0]
$MainUnit = $MainUnitArry -join ' '
$item = New-Object PSObject
$item | Add-Member -type NoteProperty -Name 'Product Site' -Value $($product.loc)
$item | Add-Member -type NoteProperty -Name 'Main Price' -Value $($MainDollar)
$item | Add-Member -type NoteProperty -Name 'Main Unit' -Value $($MainUnit)
Write-Output $item
}
}
foreach ($product in $ImportedProducts){
Start-Job -InputObject $ImportedProducts -ScriptBlock $ScriptBlock -ArgumentList $product
}
do {
$arr += Get-Job -State Completed | Receive-Job -AutoRemoveJob
} while (Get-Job -State Running)
#Show Array
$arr

Related

Powershell - Start-ThreadJob Ids Incrementing Very Quickly, Why?

New to Powershell and Stackoverflow. Here's my first Powershell Script that I'm trying to optimize to the best of my abilities. My goal is to have the code run as efficiently as possible. Any help/suggestions on that front would be much appreciated!
This script shows new 'Established' TCP Connections (Get-NetTCPConnection) and their associated DNS Hostnames (Resolve-DnsName). Each new Connection is compared to an array of previous Connections. If they have the same 'RemoteAddress', the DNS Hostname is copied over to the new Connection and displayed; otherwise it creates a new (Resolve-DnsName) (Start-ThreadedJob), and moves on to the next new Connection. Once a Job is 'Completed' it copies over the 'NameHost' and displays the Connection.
I have hit a roadblock in my understanding. When the code is running, the Job 'Ids' seem to be incrementing very quickly even though no new Jobs where created in between the last Job and the new Job.
To test the script, run it and visit any Site. Watch as the 'Id' increment very quickly. Please note that it will create a Log File in "C:\Temp\Active_Connections.csv"
$logFile = 'C:\Temp\Active_Connections.csv'
if (-not(Test-Path $logFile -PathType Leaf)){
New-Item -ItemType File -Force -Path $logFile | Out-Null
} else {
Clear-Content $logFile
}
$headersAdded = $true
$newConnections = #()
While ($true){
$connections = #(Get-NetTCPConnection)
foreach ($connection in $connections){
if ($connection.State -eq "Established"){
if ($newConnections.InstanceID -notcontains $connection.InstanceID){
if ($newConnections.RemoteAddress -notcontains $connection.RemoteAddress){
if ((Get-Job).Name -notcontains $connection.RemoteAddress){
Start-ThreadJob -Name $connection.RemoteAddress -ScriptBlock {param($remoteAddress) Resolve-DNSName -Name $remoteAddress} -ArgumentList $connection.RemoteAddress >$null
}else{
$job = Get-Job | Where-Object {$_.Name -eq $connection.RemoteAddress}
if ($job.State -eq "Completed"){
Add-Member -InputObject $connection -MemberType NoteProperty -Name "Id" -Value $job.Id -Force
Try {
$receivedJob = $job | Receive-Job -ErrorAction Stop
Add-Member -InputObject $connection -MemberType NoteProperty -Name "NameHost" -Value $receivedJob.NameHost -Force
}catch{
$na = "N/A"
Add-Member -InputObject $connection -MemberType NoteProperty -Name "NameHost" -Value $na -Force
}
#Remove-Job -Id $job.Id
}
}
}else{
foreach ($newConnection in $newConnections){
if ($newConnection.RemoteAddress -eq $connection.RemoteAddress){
Add-Member -InputObject $connection -MemberType NoteProperty -Name "NameHost" -Value $newConnection.NameHost -Force
}
}
}
}
if ($null -ne $connection.NameHost){
if ($headersAdded) {
$formatting = #{n='CreationTime';e={$_.CreationTime.ToString("h:mm:ss tt")}},'Id','LocalAddress','LocalPort','RemoteAddress','RemotePort','NameHost'
$properties = #{Expression="CreationTime";Width=13},#{Expression="Id";Width=4},#{Expression="LocalAddress";Width=15},#{Expression="LocalPort";Width=10;Alignment="Left"},#{Expression="RemoteAddress";Width=15},#{Expression="RemotePort";Width=10;Alignment="Left"},#{Expression="NameHost";Width=100}
($connection | Select-Object $formatting | Format-Table -Property $properties | Out-String).Trim() | Tee-Object -FilePath $logFile -Append
$headersAdded = $false
} else {
($connection | Select-Object $formatting | Format-Table -HideTableHeaders -Property $properties | Out-String).Trim() | Tee-Object -FilePath $logFile -Append
}
$newConnections += $connection
}
}
}
}
Please, let me know what I can do better and if you have any ideas as to why the Job Id's are incrementing so quickly between new Connections.
Appreciate the help,
Chris
I have no explanation for the jumps in job ID values. While it would be good to know the reason, pragmatically speaking, it isn't necessarily a problem.
Your code creates a tight loop which is best avoided.
The following is a PowerShell-idiomatic reformulation of your code that tries to get results as early as possible, while sleeping a fixed amount of time between tries (which you can obviously adjust).
The upshot is that the output objects won't necessarily be ordered chronologically.
The Id property (column) of the output objects reflects the original output order as returned by Get-NetTCPConnection
# NOTE: What is created is NOT a CSV file.
# It is a plain-text file in tabular format FOR THE HUMAN OBSERVER.
$logFile = 'C:\Temp\Active_Connections.csv'
& {
$newConnections = [ordered] #{} # (Ordered) hashtable that stores all new connections.
while ($true) {
# Look for new connections, and start a thread job for each
# in order to resolve the remote adddress to a domain name, if possible.
Get-NetTCPConnection |
Where-Object { $_.State -eq 'Established' -and -not $newConnections.Contains($_.InstanceID) } |
ForEach-Object {
$jb = Start-ThreadJob { Resolve-DNSName -Name ($using:_).RemoteAddress }
$newConnections[$_.InstanceID] =
$_ |
Select-Object CreationTime,
#{
n = 'Id'
e = { $jb.Id }
},
LocalAddress, LocalPort, RemoteAddress, RemotePort,
#{
n = 'NameHost'
e = { $jb }
}
}
# Sleep a little, to avoid a tight loop.
Start-Sleep -Milliseconds 300
# Look for thread jobs that have completed, and output
# the connection-info objects with the job result.
$newConnections.Keys |
ForEach-Object {
if (($obj = $newConnections[$_]) -and ($jb = $obj.NameHost).State -notin 'NotStarted', 'Running') {
# A completed job: get its result.
$result = try { $jb | Receive-Job -ErrorAction Stop } catch { #{ NameHost = 'n/a' } }
$jb | Remove-Job -Force # Remove the completed job.
$obj.NameHost = $result.NameHost # Update the object with the job result.
$obj # Output the updated object.
$newConnections[$_] = $null # No need to hang on to the object in the hasthable.
}
}
}
} |
Format-Table #{ Name = 'CreationTime'; Expression = { $_.CreationTime.ToString('h:mm:ss tt') }; Width = 13 },
#{Expression = "Id"; Width = 4 },
#{Expression = "LocalAddress"; Width = 15 },
#{Expression = "LocalPort"; Width = 10; Alignment = "Left" },
#{Expression = "RemoteAddress"; Width = 15 }, #{Expression = "RemotePort"; Width = 10; Alignment = "Left" },
#{Expression = "NameHost"; Width = 100 } |
Tee-Object -FilePath $logFile

Adding array elements to a PSObject

Preface: I haven't any formal training with script writing. So I'm sure many of you will be banging you head against the table wondering what I'm doing.
I am working to gather information on our current catalog of Teams sites, and respective site owners. I have a PS script to gather the list of sites, then it loops through each site to get list of owners. After I gather that owner list (stored in a var) I loop through that var to get the user name and store each user name in an array as a new element. That element is then used later when adding a new member to a PSObject so I can export the results to a CSV. Here's a bit of the code;
#Var's
$Count = 1
$TSO = New-Object PSObject
$SiteOwner = #("SiteOwner0","SiteOwner1","SiteOwner2","SiteOwner3","SiteOwner4","SiteOwner5","SiteOwner6","SiteOwner7") #Array to create column headers in the CSV
#STEP 1: Get a list of the sites
$Sites = get-team | sort-object DisplayName
#STEP 2: Get a list of the owners of the different sites
FOREACH ($i in $Sites){
$h = Get-TeamUser -GroupID $i.GroupID | Where {$_.role -eq "Owner"}
$Count += 1
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Add the new columns to $TSO
$TSO | Add-Member -MemberType NoteProperty -Name "SiteName" -Value $i.DisplayName -Force
$TSO | Add-Member -MemberType NoteProperty -Name "GroupOwner" -Value $i.Description -Force
$TSO | Add-Member -MemberType NoteProperty -Name "Visibility" -Value $i.Visibility -Force
$TSO | Add-Member -MemberType NoteProperty -Name "Archived" -Value $i.Archived -Force
$TSO | Add-Member -MemberType NoteProperty -Name "SiteEmail" -Value $i.MailNickname -Force
#loop through each member to discover the assigned role
FOREACH ($o in $h){
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Build the dynamics of the owners before passing to $TSO
$OHolder += $o.user
} #END NESTED FOREACH
#Handle BLANK elements in the $OHolder array
#The number 8 is the number of array elements we need a value for
$Comp = 0
While($Comp -lt 8){
If($OHolder[$Comp] -ne $null){
$TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force
}
ELSEIF(-not ($OHolder[$Comp])){
$OHolder[$Comp] = "NA"
$TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force
}
#Increment the $Comp
$Comp += 1
}#END WHILE
#Resetting the $Comp outside the loop
$Comp = 0
#************* END STEP 2 *************************
#Squirt out the info to a CSV
$TSO | Export-CSV -Path $OPathOut -NoTypeInformation -Append -Force
#Reset the $OHOLDER Array
$OHolder = #()
} #END FOREACH
My problem is this; When adding the site owner to the PSObject for siteowner ($TSO | Add-Member -MemberType NoteProperty -Name $SiteOwner[$Comp] -Value $OHolder[$Comp] -Force) it's counting each character of the value as an element.
For example: $OHolder will have two elements, $OHolder[0] is supposed to equal "doe#domain.com" and $OHolder[1] is suppose to equal "john#domain.com". What actually happens is the length of the array becomes 29 (for each character) rather than 2.
How can I add the users to the PSObject as intended? Right now the output will have:
SiteOwner0 | SiteOwner1 | SiteOwner2
d | o | e
# |d |o
Do not use += to add items to an array. It is inefficient if the arrays become large. A new array is created each time after the current array contents are read into memory. You can simply output each item inside of the foreach and assign the array variable to the foreach output.
$OHolder = #(FOREACH ($o in $h){
Write-Host . -ForeGroundColor "Cyan" -NoNewLine
#Build the dynamics of the owners before passing to $TSO
$o.user # Output
})
In the simplistic example above, a loop is likely not even necessary as $OHolder = ,$h.user should suffice in PowerShell v3+.
You are not defining $OHolder variable before using += operator.
By default powershell seems to define a string variable in this case.
$undefVar = $null
$undefVar += "test"
$undefVar += "some"
# content of $undefVar is now testsome
Try to move $OHolder = #() before the loop FOREACH ($o in $h){

How to extract specific pattern lines from multiple files AND get the file name & date modified for each line - Powershell

Summary of the process:
There is a patching process that creates log files which all go into 1 folder and are not updated or modified again. All log files from all patches go here, and are not overwritten.
During this patching, it does multiple updates to multiple areas, but will only update the parts that have a version lower than the new update. (for example a version of 'Section.X' is 12, the patch is to make Section.X 15 <- this will update. If the patch was to make Section.X 11 it would not update.)
When this does update update, it starts the line with 'Applying' in the .log file with some other required information (all on 1 line, all of which is needed).
When it does not update a section, it starts with 'Not applying. I do not need this info currently.
What I am trying to do:
I am trying to Take:
- Specific lines from the .log files.
- The File name they came from .
- The modified date of the file they came from.
Tests / Script so far:
#Creating the String, and applying the filter to only take the 'Applying' lines, and then creating a new line for it to repeat.
$myString = (Select-String -Path "C:\FilePathHere\*.log" -Pattern '^Applying ').Line
# This will take the Modified date from the File.
$lastModifiedDate = (Get-Item "C:\FilePathHere\*.log").LastWriteTime
$arr = $myString -split ' '
$Groups1 = #()
$Groups = #()
#Create a table holding just the specific info from each line required, and putting that into a table.
foreach($members in $myString)
{
$arr = $members -split ' '
$Groups1 = New-Object PSObject
$Groups1 | Add-Member -type NoteProperty -Name 'Object Updated' -Value $arr[1]
$Groups1 | Add-Member -type NoteProperty -Name 'New Version' -Value $arr[3]
$Groups1 | Add-Member -type NoteProperty -Name 'Old Version' -Value $arr[8]
$Groups1 | Add-Member -type NoteProperty -Name 'Server/DB' -Value $arr[5]
$Groups1 | Add-Member -type NoteProperty -Name 'Updated Date' -Value $lastModifiedDate
$Groups += $Groups1
}
# to display the recorded and ordered info:
$Groups
Problems so far:
So, the Above actually does the majority of what I want. What it does DO:
- It takes the correct lines.
- It orders it correctly.
- It only takes the required information.
- It inputs it correctly into the Excel sheet.
what it does not do is:
Put the correct Modified Date by the correct line. - At the moment, it takes the first file and states that all of the lines have the same file modified date.
Thank you!
Sorry for the long post, I wanted to make sure I provided all of the details required here. I appreciate any help you can provide. I am quite new to Powershell and am still trying to piece things together at the moment, Please bear with me.
I would utilize the -Path parameter on the Select-String cmdlet to shorten the loop and output an array of PsObjects to a variable:
$result = Select-String -Path 'C:\FilePathHere\*.log' -Pattern '^Applying ' | ForEach-Object {
$arr = $_.Line -split ' '
[PsCustomObject]#{
'Object Updated' = $arr[1]
'New Version' = $arr[3]
'Old Version' = $arr[8]
'Server/DB' = $arr[5]
'LogFile' = $_.Path
'Updated Date' = (Get-Item -Path $_.Path).LastWriteTime
}
}
# output on screen
$result
# output to CSV
$result | Export-Csv -Path "D:\logInfo.csv" -NoTypeInformation
Hope that helps
The way you're defining $lastModifiedDate and then set it as value for 'Updated Date' causes the variable to never change, regardless of which log file $myString comes from. I would look at each file individually so the LastWriteTime of each file keeps evolving as my table is created. I would try this :
$logfiles = Get-ChildItem -Path C:\FilePathHere\*.log
$Groups1 = #()
$Groups = #()
foreach ($logfile in $logfiles) {
$myString = (Select-String -Path "C:\FilePathHere\$($logfile.Name)" -Pattern '^Applying ').Line
$arr = $myString -split ' '
$lastModifiedDate = $logfile.LastWriteTime
foreach ($members in $myString) {
$arr = $members -split ' '
$Groups1 = New-Object PSObject
$Groups1 | Add-Member -type NoteProperty -Name 'Object Updated' -Value $arr[1]
$Groups1 | Add-Member -type NoteProperty -Name 'New Version' -Value $arr[3]
$Groups1 | Add-Member -type NoteProperty -Name 'Old Version' -Value $arr[8]
$Groups1 | Add-Member -type NoteProperty -Name 'Server/DB' -Value $arr[5]
$Groups1 | Add-Member -type NoteProperty -Name 'Updated Date' -Value $lastModifiedDate
$Groups += $Groups1
}
}
# to display the recorded and ordered info:
$Groups

Powershell Array: HOWTO Dedup Output

When I run this parser script on my contacts.xml, which shows one line per user, I get multiple instances of the same data. I only want a single entry for the same data. How do I dedup the data before it writes to the CSV?
#https://stackoverflow.com/questions/29999682/powershell-parsing-a-text-file
$input = Get-Content $env:USERPROFILE\Downloads\contacts.xml\Downloads\contacts.xml
$array = #()
$input | % {
$writeobj = $false
$obj = New-Object System.Object
if ($_ -match 'email*') {
$Email = ($_ -split ':')[1]
}
if ($_ -match 'FN*') {
$NAME = ($_ -split ':')[1]
$writeobj = $true
}
if ($writeobj) {
$obj | Add-Member -Type NoteProperty -Name Email -Value $Email
$obj | Add-Member -Type NoteProperty -Name Name -Value $NAME
$array += $obj
}
Write-Host $Name, $email
}
$array | Export-Csv -Path C:\scripts\reports\test.csv -NoTypeInformation
I expect this to produce single entries but I get duplicates (and they don't line up right either).
(And yes I checked the XML file for single entries)
Select the unique objects.
$array |
Select-Object -Property * -Unique |
Export-Csv -Path 'C:\scripts\reports\test.csv' -NoType
As a side note, you may want to avoid appending to an array in a loop, as that is bound to perform poorly. Just pipe your ForEach-Object loop directly into Export-Csv.
I figured it out.
I REVERSED the $obj Add-Member variables *that fixed the order) and added another "$writeobj = $true" line to the FN match, and VOILĂ€ no more dupes.
Is that weird or what?
#https://stackoverflow.com/questions/29999682/powershell-parsing-a-text-file
$input = Get-Content $env:USERPROFILE\Downloads\contacts.xml $array =
#() $input | % {
$writeobj = $false
$obj = New-Object System.Object
If ($_ -match 'email') {
$Email = ($_ -split ':')[1]
$writeobj = $true
}
If ($_ -match 'FN') {
$NAME = ($_ -split ':')[1]
$writeobj = $true # <-- right here
}
If ($writeobj){
$obj | Add-Member -type NoteProperty -name Email -value **$NAME**
$obj | Add-Member -type NoteProperty -name Name -value **$Email**
$array += $obj
}
Write-Host $Name, $email } $array | Export-Csv -path C:\scripts\reports\test.csv -NoTypeInformation

Fill object within an loop and output the whole array to a CSV

I currently have a script that will run through a folder and open each PDF inside and an input box will pop up asking me what I want to rename the file to. This part works fine but I'm trying to populate an array with information on each file such as date created, date modified, old file name and new file name.
The code I currently have is:
Add-Type -AssemblyName Microsoft.VisualBasic
$folderpath = 'file path'
$items = Get-ChildItem -Recurse $folderpath *.pdf
$newFileName = ""
$counterID = 0
$amountOfScans = $items.Length
$typeOfScanner = ""
$scanArray = #()
$scanObject = New-Object System.Object
foreach( $i in $items) {
Start-Process ((Resolve-Path ("$folderpath$i")).Path)
Start-Sleep -Seconds 1
$counterID++
$orderID = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the new file name for scan $counterID / $amountOfScans :", $i)
if ($newFileName -eq "delete"){
$reasonForDeletion = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a reason for marking $i for deletion :", $i)
}
if ($i.Length -eq 24){
$typeOfScanner = "Xerox"
}
elseif ($ie.Length -eq 33) {
$typeOfScanner = "Lexmark"
}
$DateCreated = Get-ItemProperty ((Resolve-Path ("$folderpath$i")).Path) | select -exp CreationTime
$DateModified1 = Get-Date -Format dd/MM/yyyy
$DateModified2 = Get-Date -Format HH:mm:ss
$DateModifiedTotal = ("$DateModified1 $DateModified2")
$scanObject[$counterID] | Add-Member -type NoteProperty -name TempName -Value "$i"
$scanObject[$counterID] | Add-Member -type NoteProperty -name NewName -Value "$orderID"
$scanObject[$counterID] | Add-Member -type NoteProperty -name TypeOfScanner -Value "$typeOfScanner"
$scanObject[$counterID] | Add-Member -type NoteProperty -name DateCreated -Value "$DateCreated"
$scanObject[$counterID] | Add-Member -type NoteProperty -name DateModified -Value "$DateModifiedTotal"
$scanObject[$counterID] | Add-Member -type NoteProperty -name ReasonForDeletion -Value "$reasonForDeletion"
$scanArray += $scanObject[$counterID]
Stop-Process -Name "Acro*"
}
$scanArray | export-csv C:\Scans\renamed_stats.csv -notypeinformation
However after this script is ran "renamed_stats.csv" is completely blank with no error messages.
Any help would be appreciated :)
Assuming you don't have so many objects as to run out of memory, and that your value variables like $i contain the expected, you can get rid of $scanObject altogether and do this:
[array]$scanArray += [PSCustomObject] #{
TempName = "$i"
NewName = "$orderID"
TypeOfScanner = "$typeOfScanner"
DateCreated = "$DateCreated"
DateModified = "$DateModifiedTotal"
ReasonForDeletion = "$reasonForDeletion"
}
You could also improve the date part by using quotes:
$DateModified = Get-Date -Format "dd/MM/yyyy HH:mm:ss"
Try creating $scanObject inside loop this way:
$scanObject = New-Object PSObject -Property #{
TempName = "$i"
NewName = "$orderID"
TypeOfScanner = "$typeOfScanner"
DateCreated = "$DateCreated"
DateModified = "$DateModifiedTotal"
ReasonForDeletion = "$reasonForDeletion"
}

Resources