use powershell socket server to get remote command - sockets

I want use a powershell socket server to get remote command.
But I only can get the remote command once.
Can someone give me a help? thanks.
Here is my code:
$endpoint = new-object System.Net.IPEndPoint([system.net.ipaddress]::any, 12346)
$listener = new-object System.Net.Sockets.TcpListener $endpoint
while($true)
{
$listener.Start()
$client = $listener.AcceptTcpClient()
[System.IO.StreamReader]$stream = new-object System.IO.StreamReader -argumentList $client.GetStream()
while($true)
{
$line = $stream.ReadLine()
while($line -ne $null){
Write-Host $line -ForegroundColor Yellow
}
}
$client.Close()
$listener.Stop()
}

The problem is that your while check ($line -ne $null) will always be true because $line is not assigned the new value after initial assignment,
try this:
$endpoint = new-object System.Net.IPEndPoint([system.net.ipaddress]::any, 12346)
$listener = new-object System.Net.Sockets.TcpListener $endpoint
while($true)
{
$listener.Start()
$client = $listener.AcceptTcpClient()
[System.IO.StreamReader]$stream = new-object System.IO.StreamReader -argumentList $client.GetStream()
while($true)
{
do {
$line = $stream.ReadLine()
Write-Host $line -ForegroundColor Yellow
}while($line -ne $null)
}
$client.Close()
$listener.Stop()
}

Related

Powershell TCP Send and Receive response

I am using a PowerShell TCP listener accept a connection then send and receive over that connection.
I need to send:
$Ret = 0x02 + [string]"LS$(Current)" + 0x03
So an 0x02 start and 0x03 end but I cannot get it into a variable to use a:
$writer.Write($Ret)
$writer.Flush()
To transmit it over TCP
Any suggestions would help.
#Current function
Function Current {
$Now = Get-Date -format "yy:MM:dd:HH:mm:ss"
$IM = $Now -split ":"
$P1 = "|DA" + $IM[0] + $IM[1] + $IM[2]
$P2 = "|TI" + $IM[3] + $IM[4] + $IM[5] + "|"
$P3 = $P1 + $P2
Return $P3
}
$continue = $true
$port = 8471
$IPEndPoint = New-Object System.Net.IPEndPoint([IPAddress]::Any,$port)
$listener = New-Object System.Net.Sockets.TcpListener $IPEndPoint
$listener.Start()
[Byte] $B1 = 0x02
[Byte] $B2 = 0x03
while ($continue -eq $true) {
$data = $Null
$client = $listener.AcceptTcpClient()
$rEndpoint = $client.client.RemoteEndPoint
$stream = $Client.GetStream()
$client.NoDelay = $True
#Stream Prep
$reader = New-Object System.IO.StreamReader $stream
$writer = New-Object System.IO.StreamWriter $Stream
$writer.AutoFlush = $true
If($client.Connected -eq $false) {
"Not Connected"
}
$buffer = new-object System.byte[] 1024
$EncodedText = new-object System.Text.AsciiEncoding
If ($client.Connected -and $Count -lt 1) {
$R1 = "LS$(Current)"
$Ret = 0x02 + [string]"LS$(Current)" + 0x03
$writer.Write($Ret)
$writer.Flush()
Write-Host $Ret
$Count++
}
while ($client.Connected -and $stream.DataAvailable -and ($i = $stream.Read($buffer, 0, $buffer.Length)) -ne 0) {
$data += $EncodedText.GetString($buffer, 0, $i)
$stream.Flush()
}
If($Data -eq $null) {
$Data = $Reader.ReadLine()
}
Write-Host "$rEndpoint`: $data"
# Get-Variable -Name data
If($data -eq "StopIt") {
$stream.Close()
$client.Close()
$continue = $false
$listener.stop()
$Client.Close()
$stream.Close()
$listener.stop()
$reader.Close()
$writer.Close()
} ElseIf($data -match "LS") {
$Ret = "'\x02LS$(Current)\x03'"
$writer.WriteLine($Ret)
Write-Host $Ret
} ElseIf($data -match "x02LA") {
$Ret = "\x02LA$(Current)\x03"
$writer.Write($Ret)
Write-Host $Ret
} ElseIf($Data -eq $Null) {
$Data
"Not"
# $Ret = "'\x02LS$(Current)\x03'"
# $writer.WriteLine($Ret)
# Write-Host $Ret
} ElseIf($Data -match "DR") {
$Data
Write-host "DR Request"
# Resenc query to database
} ElseIf($Data -match "PS") {
$Data
Write-host "PS Request"
# Room paid query
} Else {
$Data
Write-host "Other Request"
}
$Reader.Close()
$writer.Close()
$stream.Close()
$client.Close()
start-sleep -Milliseconds 500
}
In Python it looks like this:
s = 'LS|DA220601|TI195645|'
m = b'\x02' + s.encode('ascii') + b'\x03'
Need to send the same thing over a TCP connection with PowerShell.
Thanks
Peter
$port=8471
$IPEndPoint=New-Object System.Net.IPEndPoint([IPAddress]::Any,$port)
$listener = New-Object System.Net.Sockets.TcpListener $IPEndPoint
$listener.Start()
$data = $Null
$client = $listener.AcceptTcpClient()
$rEndpoint = $client.client.RemoteEndPoint
$client.NoDelay = $True
$PS=$Client.GetStream()
#((0x02) -as [byte[]];$([System.Text.Encoding]::ASCII.GetBytes($Ret));(0x03) -as
byte[]])
$PS.Write($data1,0,$data1.Length)
$PS.Flush()
The above fixed it. Thanksall.
Peter

PowerShell To Retreive HTML from an open tab

I am trying to retrieve links from a dynamic JavaScript-rendered web page using PowerShell.
This is the code I have so far:
# Create IE object and load URL
$ie = New-Object -comobject "InternetExplorer.Application"
$ie.visible = $true
$ie.navigate($url)
# Wait for the page to load
while ($ie.Busy -eq $true -Or $ie.ReadyState -ne 4) {Start-Sleep 2}
$Doc = $ie.Document
$divs = $Doc.getElementsByTagName('a')
foreach ($div in $divs){
write-host $div['id'].value
write-host $div['tagName'].value
write-host $div['parentElement'].value
write-host $div['style'].value
write-host $div['document'].value
write-host $div['sourceIndex'].value
write-host $div['offsetLeft'].value
write-host $div['offsetTop'].value
write-host $div['offsetWidth'].value
write-host $div['offsetHeight'].value
write-host $div['offsetParent'].value
write-host $div['innerHTML'].value
write-host $div['innerText'].value
write-host $div['outerHTML'].value
write-host $div['outerText'].value
write-host $div['parentTextEdit'].value
}
However, all of the output is blank lines.
(FYI - if I output just the $div, then I get System.__ComObject
Can anyone explain what I need to do to get the information?
Thank you.
First of all, I would change the names of $divs to $tags and use $tag rather then $div in the foreach loop, because you are not really looking for divs, but <a> tags.
Each $tag has a getAttribute method you should use instead of $tag['attribName'].value.
Your code could then look like this
$ie = New-Object -comobject "InternetExplorer.Application"
$ie.visible = $true
$ie.navigate($url)
# Wait for the page to load
while ($ie.Busy -eq $true -Or $ie.ReadyState -ne 4) {Start-Sleep 2}
$Doc = $ie.Document
$tags = $Doc.getElementsByTagName("a")
foreach ($tag in $tags){
Write-Host $tag.getAttribute("id")
Write-Host $tag.getAttribute("tagName")
Write-Host $tag.getAttribute("parentElement")
Write-Host $tag.getAttribute("style")
Write-Host $tag.getAttribute("document")
Write-Host $tag.getAttribute("sourceIndex")
Write-Host $tag.getAttribute("offsetLeft")
Write-Host $tag.getAttribute("offsetTop")
Write-Host $tag.getAttribute("offsetWidth")
Write-Host $tag.getAttribute("offsetHeight")
Write-Host $tag.getAttribute("offsetParent")
Write-Host $tag.getAttribute("innerHTML")
Write-Host $tag.getAttribute("innerText")
Write-Host $tag.getAttribute("outerHTML")
Write-Host $tag.getAttribute("outerText")
Write-Host $tag.getAttribute("parentTextEdit")
}
Also, when finished you should cleanup the COM object you have created:
[System.Runtime.InteropServices.Marshal]::ReleaseComObject($ie) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
Edit
to make better output i suggest this:
$Doc = $ie.Document
$tags = $Doc.getElementsByTagName("a")
#$tags = $doc.all.tags("a")
$attribs = #("id", "tagName", "parentElement", "style", "document", "sourceIndex",
"offsetLeft", "offsetTop", "offsetWidth", "offsetHeight", "offsetParent",
"innerHTML", "innerText", "outerHTML", "outerText", "parentTextEdit")
foreach ($tag in $tags){
$attribs | ForEach-Object {
#{ $_ = $tag.getAttribute($_) }
}
}

Register-EventObject does not update console while listening to process

I am currently in the process of writing a process wrapper and i am trying to redirect the stdout, and stderr channels to the powershell console
the following code is the function I am using to invoke my process but the problem i seem to be having is that i don't get any output from the event handlers to update the console
the output and err output fine at the end but will not update as it happens
function Invoke-Executable($ExePath, $ExeArgs)
{
#Setup ProcessInfo
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $ExePath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $ExeArgs
#Setup Process
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
#Setup Error Listener
$errEvent = Register-ObjectEvent -InputObj $process `
-Event "ErrorDataReceived" `
-Action `
{
param
(
[System.Object] $sender,
[System.Diagnostics.DataReceivedEventArgs] $e
)
Write-Error $e.Data
}
#Setup Out Listener
$outEvent = Register-ObjectEvent -InputObj $process `
-Event "OutputDataReceived" `
-Action `
{
param
(
[System.Object] $sender,
[System.Diagnostics.DataReceivedEventArgs] $e
)
Write-Host $e.Data
}
# Start the process
[Void] $process.Start()
# Begin async read events
# $process.BeginOutputReadLine()
# $process.BeginErrorReadLine()
while (!$process.HasExited)
{
Start-Sleep -Milliseconds 250
Write-Host "ping"
}
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
# if ($stdout) {Write-Host "$stdout"}
# if ($stderr) { Write-Error "$stderr" }
}
Uncomment these lines in your code and it should start working:
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
I modified Write-Error to Write-Host for reasons similar to what is discussed here: Write-Error does not print to console before script exits
Example used for output redir testing :
Invoke-Executable ping "127.0.0.1"
Example used for testing error redir :
Invoke-Executable powershell 'import-module nonexistant -ea continue;exit'
Full code :
function Invoke-Executable($ExePath, $ExeArgs)
{
#Setup ProcessInfo
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $ExePath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $ExeArgs
#Setup Process
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
#Setup Error Listener
$errEvent = Register-ObjectEvent -InputObj $process `
-Event "ErrorDataReceived" `
-Action `
{
param
(
[System.Object] $sender,
[System.Diagnostics.DataReceivedEventArgs] $e
)
Write-host $e.Data
}
#Setup Out Listener
$outEvent = Register-ObjectEvent -InputObj $process `
-Event "OutputDataReceived" `
-Action `
{
param
(
[System.Object] $sender,
[System.Diagnostics.DataReceivedEventArgs] $e
)
Write-Host $e.Data
}
# Start the process
[Void] $process.Start()
# Begin async read events
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
while (!$process.HasExited)
{
Start-Sleep -Milliseconds 250
Write-Host "ping"
}
# if ($stdout) {Write-Host "$stdout"}
# if ($stderr) { Write-Error "$stderr" }
}

How do I time out a REGQUERY command in Powershell?

I am trying to find a way (maybe with a job?) to timeout system jobs. It seems my regquery will normally time out after 42 seconds if the computer is not online, and the code can't reach the registry. I am looking to limit the query to around 5 seconds as I have a tonne of computers in our environment. I have tried to play around with creating jobs, a stopwatch, etc. but no luck :( Please help!
$File = Import-Csv 'c:\temp\regcomplist.txt'
$Results=""
$text="Machine Name,Regkey Value, Runtime"
$fileout = "C:\Temp\regquery.csv"
Write-host $text
Out-File -FilePath $fileout -InputObject $text -Force
$timeout = new-timespan -Seconds 5
$swtotal = [Diagnostics.Stopwatch]::StartNew()
foreach ($line in $file)
{
TRY{
$regkey = ""
$keyValue = ""
$machinename = $line.machinename
#trap [Exception] {continue}
$key = "SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\Windows x64\\Drivers\\Version-3\\Lexmark Universal v2 XL"
$sw = [Diagnostics.Stopwatch]::StartNew()
#while ($sw.elapsed -lt $timeout){
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$MachineName)
$regkey = $reg.opensubkey($key)
$keyValue = $regKey.GetValue('Help File')
# return
#}
#start-sleep -seconds 5
#if ($ok -ne "OK"){$keyValue = "TIMED OUT: "+$sw.elapsed}
}
Catch{
$keyValue = "Error Opening Registry"
}
$text = $machinename+","+$keyValue+","+$sw.elapsed
$Results += $text
#Output Below Here:
Write-host $text
Out-File -InputObject $text -FilePath $fileout -Append
}
Write-host "Total time run:"$swtotal.elapsed
Thanks! That was exactly what I needed. I set the count to 1 ping, and it's MUCH faster than 42 second timeouts. Here is the code for anyone this might help...
$File = Import-Csv 'c:\temp\powershell\regcomplist.txt'
$Results=""
$text="Machine Name,Regkey Value, Runtime"
$fileout = "C:\Temp\powershell\regquery.csv"
Write-host $text
Out-File -FilePath $fileout -InputObject $text -Force
$timeout = new-timespan -Seconds 5
$swtotal = [Diagnostics.Stopwatch]::StartNew()
foreach ($line in $file){
$regkey = ""
$keyValue = ""
$machinename = $line.machinename
#trap [Exception] {continue}
$key = "SYSTEM\\CurrentControlSet\\Control\\Print\\Environments\\Windows x64\\Drivers\\Version-3\\Lexmark Universal v2 XL"
$sw = [Diagnostics.Stopwatch]::StartNew()
$pingtest=Test-Connection -ComputerName $machinename -Quiet -Count 1
if ($pingtest -eq $true ){
TRY{
#while ($sw.elapsed -lt $timeout){
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$MachineName)
$regkey = $reg.opensubkey($key)
$keyValue = $regKey.GetValue('Help File')
# return
#}
#start-sleep -seconds 5
#if ($ok -ne "OK"){$keyValue = "TIMED OUT: "+$sw.elapsed}
}
Catch{
$keyValue = "Error Opening Registry"
}
$text = $machinename+","+$keyValue+","+$sw.elapsed
$Results += $text
#Output Below Here:
Write-host $text
Out-File -InputObject $text -FilePath $fileout -Append
}
else {write-host $machinename",doesn't ping!"}
}
Write-host "Total time run:"$swtotal.elapsed
I use a Tcp Test before querying registry:
if((Test-ComputerPort $ComputerName 445)) {
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $ComputerName).OpenSubKey("SYSTEM\CurrentControlSet\Control\Session Manager\Environment").GetValue('TEMP')
}
with Test-ComputerPort() :
function Test-ComputerPort($ComputerName,$port){
try {
$ip = [System.Net.Dns]::GetHostAddresses($ComputerName).IPAddressToString # confirm DNS resolving
if([system.Net.Sockets.TcpClient]::new().BeginConnect($ComputerName, $port, $null, $null).AsyncWaitHandle.WaitOne(40, $false) -or [system.Net.Sockets.TcpClient]::new().BeginConnect($ComputerName, $port, $null, $null).AsyncWaitHandle.WaitOne(80, $false)){ # with retry if down
return $ComputerName
}
return $false # tcp-port is down !
} catch {
return $null # conputername not resolved !
}
}

Powershell to manipulate host file

I am looking at to see if I can create powershell script to update the contents in the host file.
Anybody know if there are any examples that manipulate the host file using powershell or any other scripting lanaguages?
Thanks.
All of these answers are pretty elaborate. This is all you need to add a hosts file entry:
Add-Content -Path $env:windir\System32\drivers\etc\hosts -Value "`n127.0.0.1`tlocalhost" -Force
IP address and hostname are separated by `t which is the PowerShell notation for a tab character.
`n is the PowerShell notation for a newline.
First up, if you're on Vista or Windows 7 make sure you run these commands from an elevated prompt:
# Uncomment lines with localhost on them:
$hostsPath = "$env:windir\System32\drivers\etc\hosts"
$hosts = get-content $hostsPath
$hosts = $hosts | Foreach {if ($_ -match '^\s*#\s*(.*?\d{1,3}.*?localhost.*)')
{$matches[1]} else {$_}}
$hosts | Out-File $hostsPath -enc ascii
# Comment lines with localhost on them:
$hosts = get-content $hostsPath
$hosts | Foreach {if ($_ -match '^\s*([^#].*?\d{1,3}.*?localhost.*)')
{"# " + $matches[1]} else {$_}} |
Out-File $hostsPath -enc ascii
Given this I think you can see how to use a regex to manipulate entries as necessary.
The Carbon module has a Set-HostsEntry function for setting a hosts entry:
Set-HostsEntry -IPAddress 10.2.3.4 -HostName 'myserver' -Description "myserver's IP address"
If anyone is looking for a more advanced example, I've always been particularly fond of this gist: https://gist.github.com/markembling/173887
#
# Powershell script for adding/removing/showing entries to the hosts file.
#
# Known limitations:
# - does not handle entries with comments afterwards ("<ip> <host> # comment")
#
$file = "C:\Windows\System32\drivers\etc\hosts"
function add-host([string]$filename, [string]$ip, [string]$hostname) {
remove-host $filename $hostname
$ip + "`t`t" + $hostname | Out-File -encoding ASCII -append $filename
}
function remove-host([string]$filename, [string]$hostname) {
$c = Get-Content $filename
$newLines = #()
foreach ($line in $c) {
$bits = [regex]::Split($line, "\t+")
if ($bits.count -eq 2) {
if ($bits[1] -ne $hostname) {
$newLines += $line
}
} else {
$newLines += $line
}
}
# Write file
Clear-Content $filename
foreach ($line in $newLines) {
$line | Out-File -encoding ASCII -append $filename
}
}
function print-hosts([string]$filename) {
$c = Get-Content $filename
foreach ($line in $c) {
$bits = [regex]::Split($line, "\t+")
if ($bits.count -eq 2) {
Write-Host $bits[0] `t`t $bits[1]
}
}
}
try {
if ($args[0] -eq "add") {
if ($args.count -lt 3) {
throw "Not enough arguments for add."
} else {
add-host $file $args[1] $args[2]
}
} elseif ($args[0] -eq "remove") {
if ($args.count -lt 2) {
throw "Not enough arguments for remove."
} else {
remove-host $file $args[1]
}
} elseif ($args[0] -eq "show") {
print-hosts $file
} else {
throw "Invalid operation '" + $args[0] + "' - must be one of 'add', 'remove', 'show'."
}
} catch {
Write-Host $error[0]
Write-Host "`nUsage: hosts add <ip> <hostname>`n hosts remove <hostname>`n hosts show"
}
Starting with Kevin Remisoski's excellent answer above, I came up with this which lets me add/update multiple entries at once. I also changed the regex in the split to look for any white space, not just tab.
function setHostEntries([hashtable] $entries) {
$hostsFile = "$env:windir\System32\drivers\etc\hosts"
$newLines = #()
$c = Get-Content -Path $hostsFile
foreach ($line in $c) {
$bits = [regex]::Split($line, "\s+")
if ($bits.count -eq 2) {
$match = $NULL
ForEach($entry in $entries.GetEnumerator()) {
if($bits[1] -eq $entry.Key) {
$newLines += ($entry.Value + ' ' + $entry.Key)
Write-Host Replacing HOSTS entry for $entry.Key
$match = $entry.Key
break
}
}
if($match -eq $NULL) {
$newLines += $line
} else {
$entries.Remove($match)
}
} else {
$newLines += $line
}
}
foreach($entry in $entries.GetEnumerator()) {
Write-Host Adding HOSTS entry for $entry.Key
$newLines += $entry.Value + ' ' + $entry.Key
}
Write-Host Saving $hostsFile
Clear-Content $hostsFile
foreach ($line in $newLines) {
$line | Out-File -encoding ASCII -append $hostsFile
}
}
$entries = #{
'aaa.foo.local' = "127.0.0.1"
'bbb.foo.local' = "127.0.0.1"
'ccc.foo.local' = "127.0.0.1"
};
setHostEntries($entries)
I have written a code to delete entries from host. You can easily change the code to add entries to it from the code.
$domainName = "www.abc.com"
$rplaceStr = ""
$rHost = "C:\Windows\System32\drivers\etc\hosts"
$items = Get-Content $rHost | Select-String $domainName
Write-host $items
foreach( $item in $items)
{
(Get-Content $rHost) -replace $item, $rplaceStr| Set-Content $rHost
}
For more information see
http://nisanthkv.blog.com/2012/06/13/remove-host-entries-using-powershell/
99% of the time admin rights are needed to modify a host record. Try adding this code at the top of your Powershell script.
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
For me the biggest pain in dealing with the hosts file is remembering where it is. I set a variable that points to my hosts file in my PowerShell profile, which makes it easy to edit in a text editor.
In PowerShell, type the following to open your profile:
C:\> Notepad $profile
Add this:
$hosts = "$env:windir\System32\drivers\etc\hosts"
Save the file, then close and re-open PowerShell, running as administrator. You can't edit the hosts file without elevated permissions.
Now you can edit your hosts file the same way you'd edit your profile:
C:\> Notepad $hosts
I wrote a quick script that creates a simple GUI for adding new records to the HOSTS file. It will open a window, ask for hostname and IP, then append your input to the HOSTS file.
I'm sure it could be simplified and look cleaner... but works fine for my use case.
Enjoy!
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$hostsfilelocation = "$env:windir\System32\drivers\etc\hosts"
$readhostsfile = Get-Content $hostsfilelocation
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Update HOSTS File'
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'
$AddHosts = New-Object System.Windows.Forms.Button
$AddHosts.Location = New-Object System.Drawing.Point(55,120)
$AddHosts.Size = New-Object System.Drawing.Size(90,25)
$AddHosts.Text = 'Add Record'
$AddHosts.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $AddHosts
$form.Controls.Add($AddHosts)
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(170,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,25)
$CancelButton.Text = 'Cancel'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
$Hostslabel = New-Object System.Windows.Forms.Label
$Hostslabel.Location = New-Object System.Drawing.Point(10,20)
$Hostslabel.Size = New-Object System.Drawing.Size(280,20)
$Hostslabel.Text = 'Enter New HOSTNAME/FQDN:'
$form.Controls.Add($Hostslabel)
$HoststextBox = New-Object System.Windows.Forms.TextBox
$HoststextBox.Location = New-Object System.Drawing.Point(10,40)
$HoststextBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($HoststextBox)
$IPlabel = New-Object System.Windows.Forms.Label
$IPlabel.Location = New-Object System.Drawing.Point(10,60)
$IPlabel.Size = New-Object System.Drawing.Size(280,20)
$IPlabel.Text = 'Enter IP:'
$form.Controls.Add($IPlabel)
$IPtextBox = New-Object System.Windows.Forms.TextBox
$IPtextBox.Location = New-Object System.Drawing.Point(10,80)
$IPtextBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($IPtextBox)
$form.Topmost = $true
$form.Add_Shown({($HoststextBox,$IPtextbox).Select()})
$result = $form.ShowDialog()
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$inputhosts = $HoststextBox.Text
$inputip = $IPtextBox.Text
$newrecord = "$inputip $inputhosts"
Add-Content -Path $hostsfilelocation -Value $newrecord
}
This is the one I ended up using. I made an Active Directory group policy to run on every update, so if one of the entries is missing it is added, if it already exists you don't get a double entry:
function Test-FileLock {
param (
[parameter(Mandatory=$true)][string]$Path
)
$oFile = New-Object System.IO.FileInfo $Path
if ((Test-Path -Path $Path) -eq $false) {
return $false
}
try {
$oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
if ($oStream) {
$oStream.Close()
}
return $false
} catch {
# file is locked by a process.
return $true
}
}
$hostsFile = "$($env:windir)\system32\Drivers\etc\hosts"
$hostsEntry = #('192.168.223.223 w2012', '192.168.223.224 w2019', '192.168.223.5 tbstorage', '192.168.223.7 tgcstorage','192.168.223.202 paneladmin.local.com')
foreach ($HostFileEntry in $hostsEntry)
{
While(Test-FileLock($hostsFile)) {
Write-Host "File locked! waiting 1 seconds."
Start-Sleep -s 1
}
# split the entry into separate variables
$ipAddress, $hostName = $HostFileEntry -split '\s+',2
# prepare the regex
$re = '(?m)^{0}[ ]+{1}' -f [Regex]::Escape($ipAddress), [Regex]::Escape($hostName)
# Write-Host $re
If ((Get-Content $hostsFile -Raw) -notmatch $re) {
Add-Content -Path $hostsFile -Value $HostFileEntry
Write-Host "Writing $HostFileEntry"
}
}
I have checked the entry if exists or not before write-in the host file.
$domainCheck = "installer.example.com"
$rHost = "C:\Windows\System32\drivers\etc\hosts"
$domainName = "`n8.8.8.8`tinstaller.example.com"
if((Get-Content $rHost | Select-String $domainCheck ).length -eq 0){
Add-Content -Path $rHost -Value $domainName -Force
}
#Kevin Remisoski's answer was nice but I wanted to be able to Add, Remove and Find (so I could determine if the host was in the set before trying to remove or add)
Simplified hosts.ps1:
# Original file - https://gist.github.com/markembling/173887
$DefaultHostsFilePath = "C:\Windows\System32\drivers\etc\hosts"
Function Add-Host([string]$Ip, [string]$HostName, [string]$HostsFilePath = $DefaultHostsFilePath) {
Remove-Host $HostsFilePath $HostsFilePath
$Ip + "`t`t" + $HostName | Out-File -Encoding ASCII -Append $HostsFilePath
}
Function Remove-Host([string]$HostName, [string]$HostsFilePath = $DefaultHostsFilePath) {
$Content = Get-Content $HostsFilePath
$NewLines = #()
foreach ($Line in $Content) {
$Bits = [regex]::Split($Line, "\t+")
if ($Bits.Count -eq 2) {
if ($Bits[1] -ne $HostName) {
$NewLines += $Line
}
} else {
$NewLines += $Line
}
}
# Write file
Clear-Content $HostsFilePath
foreach ($Line in $NewLines) {
$Line | Out-File -Encoding ASCII -Append $HostsFilePath
}
}
Function Get-Hosts([string]$HostsFilePath = $DefaultHostsFilePath) {
$Content = Get-Content $HostsFilePath
$Result = #()
foreach ($Line in $Content) {
$Bits = [regex]::Split($Line, "\t+")
if ($Bits.Count -eq 2) {
$Result += "$Bits[0] `t`t $Bits[1]"
}
}
return $Result;
}
Function Find-Host([string]$Pattern, [string]$HostsFilePath = $DefaultHostsFilePath) {
$Hosts = Get-Hosts $HostsFilePath;
$Filtered = ($Hosts | Select-String $Pattern)
return $Filtered
}
Example usage script:
. ".\hosts.ps1"
if ((Find-Host "MyNewEntry").Count -eq 0)
{
Add-Host "127.0.0.1:5000" "MyNewEntry"
Write-Host "Added Entry"
}
else
{
Remove-Host "MyNewEntry"
Write-Host "Removed Entry"
}
Write-Host "-- Begin Printing Hosts --"
Get-Hosts | Write-Host
Write-Host "-- End Printing Hosts --"
Best Solution!
if(!((Select-String -Path $env:windir\System32\drivers\etc\hosts -Pattern "localhost") -ne $null)){
Add-Content -Path $env:windir\System32\drivers\etc\hosts -Value "`n127.0.0.1`tlocalhost" -Force
}