Download an image from website - powershell

I ran this powershell script to download an image from a website (to download it, certain steps had to be made, that's why I used IE navigate). I put a random string with a space between 4 and 4 characters.
But I get an error and it doesn't even start to fill the blank with the string:
Exception from HRESULT: 0x800A01B6
At E:\getbd.ps1:13 char:1
+ $ie.Document.getElementsByTagName("text") | where { $.name -eq "words ...
Here is the code:
$url = "https://fakecaptcha.com"
$set = "abcdefghijklmnopqrstuvwxyz0123456789".ToCharArray()
for($i=1; $i -le 4; $i++){
$result += $set | Get-Random}
$result += ' '
for($i=1; $i -le 4; $i++){
$result += $set | Get-Random}
$ie = New-Object -comobject InternetExplorer.Application
$ie.visible = $true
$ie.silent = $true
$ie.Navigate( $url )
while( $ie.busy){Start-Sleep 1}
$ie.Document.getElementsByTagName("text") | where { $.name -eq "words" }.value = $result
$generateBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'submit' -and $_.Value -eq 'Create it now!'}
$generateBtn.click()
while( $ie.busy){Start-Sleep 1}
$readyBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'button' -and $_.Value -eq 'Your captcha is done! Please click here to view it!!'}
$readyBtn.click()
while( $wc.busy){Start-Sleep 1}
$downloadBtn = $ie.Document.getElementsById('input') | Where-Object {$_.Type -eq 'button' -and $_.Value -eq 'DOWNLOAD'}
$downloadBtn.click()
while( $ie.busy){Start-Sleep 1}
$source = $ie.document.getElementsByTagName('img') | Select-Object -ExpandProperty src
$file = '$E:\bdlic\'+$result+'.jpg'
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($source,$file)
while( $wc.busy){Start-Sleep 1}
$ie.quit()

You have 2 syntax errors in that line:
$ie.Document.getElementsByTagName("text") | where { $.name -eq "words" }.value = $result
# ^ ^^^^^^
$.Name: The "current object" variable is $_, not just $.
where {...}.value: You cannot use dot-notation on the scriptblock of a Where-Object statement. You need to put the entire statement in a (sub)expression for that.
Change the line to this:
($ie.Document.getElementsByTagName("text") | where { $_.name -eq "words" }).value = $result

Related

Powershell for retrieve local USB printer details and insert into WMI

I am trying to get USB printer model and serial from the Win32_PnPEntity, then insert these info into a custom WMI namespace, so that I can use the Hardware inventory function in SCCM to collect the info for reporting.
I noticed that the location storing these info varies from one manufacturer to another.
such as the FujiXerox stores in "USB Printing Support" while HP and Brother stores in "USB Composite Device"
what weird is after I somehow got the below code working on my computer, when I try it on other computers, it only returns the first character, such as:
Results on my computer (two USB printers connected)
Model
Serial
HEWLETT-PACKARDHP_LASERJET_400_M401D
VNH3G0XXXX
FUJI_XEROXDOCUPRINT_P355_DB
YWG-50XXXX
Results on other computers (eg. a HP printer with serial no. starting with V)
Model
Serial
H
V
I am a system administrator managing SCCM, occasionally using PowerShell to help on my tasks, I just completely stuck at the moment as I didn't find any hint that will trim the results.
Thanks
Andrew
$ModelInfo = #()
$SerialInfo = #()
$FullInfo = #{}
$Final=#()
$USBPrinterModels = Get-WmiObject Win32_PnPEntity | Where-Object {$_.DeviceID -Match "USBPRINT"}|select DeviceID
$USBPrinterSerials2 = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB 列印支援" -or $_.Description -Match "USB Printing Support"}|select DeviceID
$USBPrinterSerials = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB Composite Device"}|select DeviceID
Foreach ($USBPrinterModel in $USBPrinterModels)
{
$ModelFull = $USBPrinterModel.DeviceID
$Model = #{}
$Model.model += ($ModelFull.Split("\"))[1]
$ModelInfo += $Model
}
Foreach ($USBPrinterSerial in $USBPrinterSerials)
{
$SerialFull = $USBPrinterSerial.DeviceID
$Serial = #{}
$Serial.serial += $SerialFull.Split("\")[2]
If($Serial.serial -notmatch "&")
{
$SerialInfo += $Serial
}
}
Foreach ($USBPrinterSerial2 in $USBPrinterSerials2)
{
$SerialFull2 = $USBPrinterSerial2.DeviceID
$Serial2 = #{}
$Serial2.serial += $SerialFull2.Split("\")[2]
If($Serial2.serial -notmatch "&")
{
$SerialInfo += $Serial2
}
}
$MaxLength = [Math]::Max($ModelInfo.Length, $SerialInfo.Length)
for ($loop_index = 0; $loop_index -lt $MaxLength; $loop_index++)
{
$Final += new-object psobject -Property #{
Model=$ModelInfo.model[$loop_index]
Serial=$SerialInfo.serial[$loop_index]
}
# $Final+=$ModelInfo[$loop_index]
# $Final+=$SerialInfo[$loop_index]
}
$Class = Get-WmiObject Win32_USBPrinterDetails -ErrorAction SilentlyContinue
If ($Class) {Remove-WmiObject -Class Win32_USBPrinterDetails}
$WMIClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
$WMIClass["__CLASS"] = "Win32_USBPrinterDetails";
$WMIClass.Qualifiers.Add("Static", $true)
$WMIClass.Properties.Add("Model", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Model"].Qualifiers.Add("read", $true)
$WMIClass.Properties.Add("Serial", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Serial"].Qualifiers.Add("key", $true)
$WMIClass.Properties["Serial"].Qualifiers.Add("read", $true)
$WMIClass.Put()
ForEach ($FInfo in $Final) {
[void](Set-WmiInstance -Path \\.\root\cimv2:Win32_USBPrinterDetails -Arguments #{Model=$FInfo.model; Serial=$FInfo.serial})
}
I am so stupid,
When there is only ONE USB printer, the $ModelInfo and $SerialInfo become "String" type and caused the [loop_index] return the first character of the string instead of the first entry of the array.
I added a gettype check on the variables (surely not perfect).
$ModelInfo = #()
$SerialInfo = #()
$FullInfo = #{}
$Final=#()
$USBPrinterModels = Get-WmiObject Win32_PnPEntity | Where-Object {$_.DeviceID -Match "USBPRINT"}|select DeviceID
$USBPrinterSerials2 = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB 列印支援" -or $_.Description -Match "USB Printing Support"}|select DeviceID
$USBPrinterSerials = Get-WmiObject Win32_PnPEntity | Where-Object {$_.Description -Match "USB Composite Device"}|select DeviceID
Foreach ($USBPrinterModel in $USBPrinterModels)
{
$ModelFull = $USBPrinterModel.DeviceID
$Model = #{}
$Model.model += ($ModelFull.Split("\"))[1]
$ModelInfo += $Model
}
Foreach ($USBPrinterSerial in $USBPrinterSerials)
{
$SerialFull = $USBPrinterSerial.DeviceID
$Serial = #{}
$Serial.serial += $SerialFull.Split("\")[2]
If($Serial.serial -notmatch "&")
{
$SerialInfo += $Serial
}
}
Foreach ($USBPrinterSerial2 in $USBPrinterSerials2)
{
$SerialFull2 = $USBPrinterSerial2.DeviceID
$Serial2 = #{}
$Serial2.serial += $SerialFull2.Split("\")[2]
If($Serial2.serial -notmatch "&")
{
$SerialInfo += $Serial2
}
}
If ($ModelInfo.model.GetType().name -eq "String") {
$Final += new-object psobject -Property #{
Model=$ModelInfo.model
Serial=$SerialInfo.serial
}
}
ElseIf ($ModelInfo.model.GetType().name -ne "String"){
$MaxLength = [Math]::Max($ModelInfo.Length, $SerialInfo.Length)
for ($loop_index = 0; $loop_index -lt $MaxLength; $loop_index++)
{
$Final += new-object psobject -Property #{
Model=$ModelInfo.model[$loop_index]
Serial=$SerialInfo.serial[$loop_index]
}
}
}
$Class = Get-WmiObject Win32_USBPrinterDetails -ErrorAction SilentlyContinue
If ($Class) {Remove-WmiObject -Class Win32_USBPrinterDetails}
$WMIClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null);
$WMIClass["__CLASS"] = "Win32_USBPrinterDetails";
$WMIClass.Qualifiers.Add("Static", $true)
$WMIClass.Properties.Add("Model", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Model"].Qualifiers.Add("read", $true)
$WMIClass.Properties.Add("Serial", [System.Management.CimType]::String, $false)
$WMIClass.Properties["Serial"].Qualifiers.Add("key", $true)
$WMIClass.Properties["Serial"].Qualifiers.Add("read", $true)
$WMIClass.Put()
ForEach ($FInfo in $Final) {
[void](Set-WmiInstance -Path \\.\root\cimv2:Win32_USBPrinterDetails -Arguments #{Model=$FInfo.model; Serial=$FInfo.serial})
}
$final|FT

How to output from a Powershell hashtable to a output file

I am trying to get the .NetFramwork version from all the windows servers. I am using powershell script. I can get the output displayed but unable to get the output from the hashtable to a output file. Also how would I get rid of the "..." from VersionDetails : {1.0.3705, 1.1.4322, 2.0.50727, 3.0...} and show the full content.
Any help will be greatly appreciated
here is the code I am using:
$username = "username"
$password = "Password"
$secstr = New-Object -TypeName System.Security.SecureString
$password.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $secstr
$query = "select name from win32_directory where name like 'c:\\windows\\microsoft.net\\framework\\v%'"
$ComputerNames = Get-Content "d:\Scripts\serverList.txt"
foreach ($ComputerName in $ComputerNames)
{
write-host "ComputerName = $ComputerName"
$ComputerName | ForEach-Object {
$res = Get-WmiObject -query $query -Credential $cred -ComputerName $ComputerName | ForEach-Object {
Split-Path $_.name -Leaf } | # returns directories
Where-Object { $_ -like 'v*' } | # only include those that start with v
ForEach-Object { [system.version]( $_ -replace "^v" ) }
# remove "v" from the string and convert to version object
# Create hashtable with computername and version details
$prop = #{
ComputerName = $ComputerName
#V1_Present = &{ if ( $res | Where-Object { $_.Major -eq 1 -and $_.Minor -eq 0 } ) { $true } }
#V1_1Present = &{ if ( $res | Where-Object { $_.Major -eq 1 -and $_.Minor -eq 1 } ) { $true } }
V2_Present = &{ if ( $res | Where-Object { $_.Major -eq 2 -and $_.Minor -eq 0 } ) { $true } }
V3_Present = &{ if ( $res | Where-Object { $_.Major -eq 3 -and $_.Minor -eq 0 } ) { $true } }
V3_5Present = &{ if ( $res | Where-Object { $_.Major -eq 3 -and $_.Minor -eq 5 } ) { $true } }
V4_Present = &{ if ( $res | Where-Object { $_.Major -eq 4 -and $_.Minor -eq 0 } ) { $true } }
VersionDetails = $res
}
# Create and output PSobject using hashtable
New-Object PSObject -Property $prop
}
=========================================================
Output dispalys
PS D:\Scripts> .\GetDotNetFrameworkver.ps1
in for loop ComputerName = XXXXXXX
V4_Present : True
V3_5Present : True
V2_Present : True
V3_Present : True
ComputerName : XXXXX
VersionDetails : {1.0.3705, 1.1.4322, 2.0.50727, 3.0...}
Based on the answer of link there is a "simpler" (and faster) solution to fetch the versions.
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version,Release -ErrorAction Ignore | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select PSChildName, Version, Release
If you want to get the versions of different remote machines you can use PowerShell remoting. Be aware that you've to enable PS remoting .If your OS version is WIN10/WIN2012R2 it is enabled per default. If you're using an older OS you've to call Enable-PSRemoting on the remote machine. See this link for details.
Example:
$result = Invoke-Command -ComputerName computer1.domain, computer1.domain -Credential (Get-Credential ) -ScriptBlock {
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version,Release -ErrorAction Ignore | Where { $_.PSChildName -match '^(?!S)\p{L}'} | Select PSChildName, Version, Release
}
$hash = $result | group PSComputerName -AsHashTable # Group the .Net versions by computername
$hash.'computer1.domain' # Print all .Net version of computer1
$hash.'computer1.domain'.Version # Only print the version
Hope that helps.

Issues with powershell invoke-command, Unexpected token '-ArgumentList' in expression or statement

I am quite new to powershell. I am trying to remote to PCs in a csv file and extract the status of a registry key. However I get the error
"Unexpected token '-ArgumentList' in expression or statement." when I try to execute. And I am not pretty sure on the syntax of the Invoke-Command, can one of your pls verify if it is correct? Appreciate your help.
So basically, what I intend to do is, get computer names, specify an output path which I will be then using inside the invoke command. Testing the online status of the PC, checking the required registry value and writing it to the file.
$computers = Get-Content "C:\Temp\AutoSug\Computers.txt"
$output_path = "C:\Temp\AutoSug\output.csv"
foreach($computer in $computers)
{
Test-Connection -computername $computer -Quiet
If (Test-Connection $computer -count 1 -quiet)
{
Invoke-Command -computer $computer -ScriptBlock
{
param(
$output_path
)
$hostname = (Get-CIMInstance CIM_ComputerSystem).Name
$objExcel = New-Object -ComObject Excel.Application
if ($objExcel.Version -eq "15.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\15.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\15.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" | Add-Content $output_path
}
}
}
}
if ($objExcel.Version -eq "14.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\14.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\14.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content -path $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" | Add-Content $output_path
}
}
}
}
if ($objExcel.Version -eq "12.0")
{
$HKEY_USERS = Get-ChildItem REGISTRY::HKEY_USERS | where-object { ($_.Name -like "*S-1-5-21*") -and ($_.Name -notlike "*_Classes") }
$Users = #()
$value = #()
foreach ($User in $HKEY_USERS)
{
$PROFILESID = Get-ChildItem REGISTRY::"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" | Where-Object { $_.name -like "*" + $USER.PSChildName + "*" }
$SID = $PROFILESID.PSChildName
foreach ($value in $SID)
{
$key = Get-Item REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\12.0\Outlook\Preferences -ErrorAction SilentlyContinue
$gold = $key.property
if($gold -like 'ShowAutoSug')
{
$grail = (Get-ItemProperty REGISTRY::HKEY_USERS\$VALUE\Software\Microsoft\Office\12.0\Outlook\Preferences).ShowAutoSug
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname, $objUser, $value , $grail | Add-Content $output_path
}
else
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($value)
$objUser = $objSID.Translate([System.Security.Principal.NTAccount])
$hostname,$objUser, $value , "The Auto Complete is not disabled" |Add-Content $output_path
}
}
}
}
} -ArgumentList $output_path
}
else
{
$status = 'Offline'
$computer , $status | Add-Content $output_path
}
}
To fix your error, simply cut -ArgumentList $output_path and put it before -ScriptBlock like:
Invoke-Command -computer $computer -ArgumentList $output_path -ScriptBlock ....

Extracting Content from Webpage with ParsedHtml

I've been trying to use the invoke-Webrequest and the "ParsedHtml.getElements"
ParsedHtml.getElementsByTagName("div") | Where{ $_.className -eq 'pricingContainer-priceContainer' } ).innerText
to try to get the value $8.29 but using it on the below code produces no result. What am I doing wrong?
<div class="pricingContainer pricingContainer--grid u-ngFade noCenterTag" ng-class="::{'noCenterTag': !showCenterTag}" ng-if="::featuresEnabled">
<!-- ngIf: ::(product.IsOnSpecial && !product.HideWasSavedPrice) -->
<div class="pricingContainer-priceContainer">
<span class="pricingContainer-priceAmount" ng-class="::specialClass">$8.29</span>
<!-- ngIf: ::product.CupPrice --><span ng-if="::product.CupPrice" class="pricingContainer-priceCup">
$5.19 / 100G
</span><!-- end ngIf: ::product.CupPrice -->
</div>
</div>
By replacing className by class:
($html.getElementsByTagName("span") | Where{ $_.class -eq 'pricingContainer-priceCup' }).innerText
or
($html.getElementsByTagName("div") | Where{ $_.class -eq 'pricingContainer-priceContainer' }).innerText
An example:
$Site = "http://example.com/index.html"
$all = Invoke-WebRequest -URI $Site
# $all contains all informations of the page
$html = [xml]$all.Content
#[xml] is a cast to convert code to xml
$html.getElementsByTagName("div")
You can use automation with IE. You choose a div witch contains the Card and you can get the innerHTML like this:
$ie = New-Object -ComObject "InternetExplorer.Application"
$ie.Navigate("http://www.example.com/index.html")
$ie.Visible = $true
while ($ie.Busy -eq $true) { Start-Sleep -Milliseconds 2000; }
$html= $ie.Document.body.getElementsByTagName('div') | Where-Object {$_.className -eq "cardList-cards cardList-isotopeContainer"}
$lines = $html.innerHTML.split("`n")
$prices = $lines | Where-Object { $_ -Match '<span class=\"pricingContainer\-priceAmount\"' }
$prices = $prices | foreach { [regex]::matches($_, '>([0-9.$]*)</span>').Groups[1].Value }
echo $prices
Worked this bad boy out by opening the webpage , wait for correct html to load over the dynamic html then dumps to a txt file to read and search.
$path = "c:\sourcecode.txt"
$ie = New-Object -com InternetExplorer.Application
$ie.visible=$true
$ie.navigate("blahblahblahblah insert webpage here")
while($ie.ReadyState -ne 4) {start-sleep -s 10}
$ie.Document.body.outerHTML | Out-File -FilePath $path
$pricebf = select-string -path $path -pattern "pricingContainer-priceAmount" | select-object -First 1 | select Line
$Descriptionbf = select-string -path $path -pattern "canOpenDetail --><a title=" | select-object -First 1 | select Line

Using powershell to modify notes.ini

I have a powershell script that parses a lotus notes INI file and replaces text inside the file. But only the replaced text is showing up in the output file.
# Example of PowerShell -replace parameter
## Get-DistinguishedName -- look up a DN from a user's (login) name
function Get-DistinguishedName {
Param($UserName)
$ads = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$ads.filter = "(&(objectClass=Person)(samAccountName=$UserName))"
$s = $ads.FindOne()
return $s.GetDirectoryEntry().DistinguishedName
}
clear-Host
set-executionpolicy remotesigned
$original_file = '.\notes.ini'
$destination_file = '.\notes2.ini'
$OS = Get-WmiObject -Class win32_OperatingSystem -namespace "root\CIMV2" -ComputerName .
$username = [Environment]::UserName
$userprofile = $env:userprofile
$fullname = Get-DistinguishedName($username) | %{$data = $_.split(","); $data[0].Substring(3)}
write-Host "Creating $userprofile"
if (($OS.Version -eq "5.1.2600") -or ($OS.Version -eq "5.2.3790")) {
$lookupTable = #{
'^SU_FILE_CLEANUP=' = 'SU_FILE_CLEANUP=' + $userprofile + '\Local Settongs\Application Data\smkits'
'%username%' = $username
'%fullname%' = $fullname
'%userprofile%' = $userprofile
'^Directory=' = 'Directory=' + $userprofile + '\Local Settongs\Application Data\Lotus\Notes\Data'}
} else {
$lookupTable = #{
'SU_FILE_CLEANUP=' = 'SU_FILE_CLEANUP=' + $userprofile + '\AppData\Roaming\smkits'
'%username%' = $username
'%fullname%' = $fullname
'%userprofile%' = $userprofile
'Directory=' = 'Directory=' + $userprofile + '\AppData\Local\Lotus\Notes\Data'}
}
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line -replace $_.Key, $_.Value
#break
}
}
write-Host $line
} | Set-Content -Path $destination_file
What am I missing
On this line, you are writing he output of the replace operator onto the pipeline, this will then get picked up by Set-Content
$line -replace $_.Key, $_.Value
whereas on this line, you are writing the output to the host (i.e. the powershell console) it will not end up on the pipeline and will not get picked up up Set-Content:
write-Host $line
To fix this, just replace write-host with write-output:
Write-Output $line