How do I time out a REGQUERY command in Powershell? - 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 !
}
}

Related

Powershell export output variable to file not found

I have the following script and as much as I try to send the output result, it does not go to the file and therefore the txt file is empty, if you can tell me where I am failing I would appreciate it.
Clear-Content .\telnet.txt
Write-host "Prueba de conexión de puertos"
'-'*30
' '*25
$test = #('WEB:google.com:443','WEBSERVER_HTTP:www.noticias3d.com:80')
Foreach ($t in $test)
{
$description = $t.Split(':')[0]
$source = $t.Split(':')[1]
$port = $t.Split(':')[2]
Write-Host "Conectando a $description host $source puerto $port"
try
{
$socket = New-Object System.Net.Sockets.TcpClient($source, $port)
$_.Message
}
catch [Exception]
{
Write-Host $_.Exception.GetType().FullName
Write-Host $_.Exception.Message
}
Out-File -FilePath telnet.txt
Write-Host "Revisado`n"
}
#$wsh = New-Object -ComObject Wscript.Shell
#$wsh.Popup("Finalizado checklist de Plataforma")
As per the out-file help:
The Out-File cmdlet sends output to a file
In your example you haven't provided any output so you are just getting an empty file. Try
"my file content" | Out-File -FilePath telnet.txt
or just use the redirection operator:
"my file content" > telnet.txt
You are not giving anything to Out-File to send. A foreach(...) statement does not produce pipeline output. Even if it did, you aren't actually chaining them together. Assign the output of the foreach loop to a variable, then pipe that variable to Out-File. I'd actually recommend you use Set-Content instead.
Clear-Content .\telnet.txt
Write-host "Prueba de conexión de puertos"
'-'*30
' '*25
$test = #('WEB:google.com:443','WEBSERVER_HTTP:www.noticias3d.com:80')
$result = Foreach ($t in $test)
{
$description = $t.Split(':')[0]
$source = $t.Split(':')[1]
$port = $t.Split(':')[2]
Write-Host "Conectando a $description host $source puerto $port"
try
{
$socket = New-Object System.Net.Sockets.TcpClient($source, $port)
$_.Message
}
catch [Exception]
{
Write-Host $_.Exception.GetType().FullName
Write-Host $_.Exception.Message
}
$result | Set-Content -FilePath telnet.txt
Write-Host "Revisado`n"
}
As an alternative you could use the pipeline instead.
Clear-Content .\telnet.txt
Write-host "Prueba de conexión de puertos"
'-'*30
' '*25
$test = #('WEB:google.com:443','WEBSERVER_HTTP:www.noticias3d.com:80')
$test | ForEach-Object {
$description = $_.Split(':')[0]
$source = $_.Split(':')[1]
$port = $_.Split(':')[2]
Write-Host "Conectando a $description host $source puerto $port"
try
{
$socket = New-Object System.Net.Sockets.TcpClient($source, $port)
$_.Message
}
catch [Exception]
{
Write-Host $_.Exception.GetType().FullName
Write-Host $_.Exception.Message
}
} | Set-Content -FilePath telnet.txt
Write-Host "Revisado`n"
You also reference automatic variable $_ in the line $_.Message which is referencing nothing.

my script suddenly stops after working as intended for 1 week

Screenshot
I decided to try and create a Powershell script. To remove printers that are not in the $StandardPrinterListe. This has been working for about a week and then yesterday it doesn't work anymore. It stops after checking a couple of computers. I would appreciate any help on this matter. If you feel like you also could give input on how the script could have been done better, please let me know.
$Publikum = New-Object Collections.Generic.List[string]
$PublikumOnline = New-Object Collections.Generic.List[string]
$PublikumOffline = New-Object Collections.Generic.List[string]
$PrintListe = New-Object Collections.Generic.List[string]
$PrinterLokal = New-Object Collections.Generic.List[string]
$PrinterLokalFjernes = New-Object Collections.Generic.List[string]
$StandardPrinterListe = New-Object Collections.Generic.List[string]
$StandardPrinterListe = #('Microsoft Print to PDF', 'Send til OneNote 16', 'Microsoft XPS Document Writer', 'Fax','Send to OneNote 16')
$i = 0
foreach($c in Get-ADComputer -Filter {Name -like "*Name*"} -searchbase "Some SearchBase"){
$Publikum.Add($c.Name)
}
foreach($Element in $Publikum){
Write-Progress -Activity "Sjekker offline status" -Status "Progress:" -PercentComplete ($i/$Publikum.count*100)
Write-Host $Element
if (Test-Connection -ComputerName $Element -Quiet)
{
$PublikumOnline.Add($Element)
}
else
{
$PublikumOffline.Add($Element)
}
$i = $i+1
Write-Host $PublikumOffline.Count "Offline" -BackgroundColor “Red”
Write-Host $PublikumOnline.Count "Online" -ForegroundColor “Green”
}
$Left = $PublikumOnline.Count
$i = 0
foreach($Element in $PublikumOnline){
$PrintResultat = Invoke-Command -ComputerName $Element -ScriptBlock {
$Print = #{}
$Print.LokalPrint = Get-Printer
New-Object psobject -property $Print
}
foreach ($Printer in $PrintResultat.LokalPrint) {
if($Printer.Name -notin $StandardPrinterListe){
$PrinterLokalFjernes.Add($Printer.Name)
}
$PrinterLokal.Add($Printer.Name)
}
Write-Output $Element $PrinterLokal
if ($PrinterLokalFjernes)
{
foreach ($FjernePrinter in $PrinterLokalFjernes){
Write-Host $Element $FjernePrinter -BackgroundColor “Red”
}
. 'C:\Users\jonhstor-drift\Documents\Fjerne Lokale skrivere.ps1'
Fjern-Skriver -Session ([ref]$Element) -Target ([ref]$PrinterLokalFjernes)
}
else
{
Write-Host $Element "Har ikke pushprint" -ForegroundColor “Green”
}
$PrinterLokalFjernes.Clear()
$PrinterLokal.Clear()
$i = $i+1
$Left = $Left - 1
Write-Host $Left "igjen å sjekke"
Write-Progress -Activity "Progresjon" -Status "Progress:" -PercentComplete ($i/$PublikumOnline.count*100)
}
Read-Host -Prompt "Press Enter to exit"
function Fjern-Skriver([ref]$Session, [ref]$Target) {
foreach($Element in $Target.Value){
Invoke-Command -ComputerName $Session.Value -ScriptBlock {
C:\Windows\SysWOW64\rundll32.exe printui.dll,PrintUIEntry /n $using:Element /dl
Gpupdate /force
}
Write-Host "Har fjernet" $Element "fra maskin" $Session.Value -ForegroundColor “Green”
}
}

Check if computer is online, if so, echo "$Computername is online" script [duplicate]

I have a large list of hostnames I need to ping to see if they are up or down. I'm not really that great at scripting but I managed to figure this much out:
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name is up" -ForegroundColor Green
}
else{
Write-Host "$name is down" -ForegroundColor Red
}
}
This gets me what I need but i now need to write out these results to a csv file and i have no idea how to do that.
Please Help!
You can use the following code instead (I simply altered the write-host calls to CSV formatting) and execute it with "PowerShell.exe script.ps > output.csv"
Note that you must execute it from the folder that contains hnames.txt, or simply change the "hnames.txt" to a full path.
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name,up"
}
else{
Write-Host "$name,down"
}
}
P.S. You can also use the Out-File Cmdlet to create the CSV file
I am a complete newbie to Powershell, so I took this on as a learning task, as I needed a quick and simple way to check a list of PC's for up/down status. These tweaks were needed to get it to output cleanly to the screen and to a txt file
$Output= #()
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
$Output+= "$name,up"
Write-Host "$Name,up"
}
else{
$Output+= "$name,down"
Write-Host "$Name,down"
}
}
$Output | Out-file "C:\support\result.csv"
$Output= #()
$names = Get-Content ".\input\Servers.txt"
foreach ($name in $names){
if (Test-Connection -Delay 15 -ComputerName $name -Count 1 -ErrorAction SilentlyContinue -quiet){
$Output+= "$name,up"
Write-Host "$Name,up" -ForegroundColor Green
}
else{
$Output+= "$name,down"
Write-Host "$Name,down" -ForegroundColor Red
}
}
$Output | Out-file ".\output\result.csv"
This is a tad cleaner, and includes the original foreground options but, BTW, the 'delay' switch seems to be ignored -PB
I would do it this way. Using a list of computers and -asjob works very well. The Responsetime property (confusingly the header is "Time(ms)") will be non-null if the host is up.
$names = Get-content hnames.txt
test-connection $names -asjob -count 1 | receive-job -wait -auto
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
COMP001 yahoo.com 74.6.231.21 32 39
COMP001 microsoft.com 40.113.200.201 32
Lately I do it this way. It requires threadjobs installed in powershell 5.1. Or just use get-port. I stick it in a mymod\mymod.psm1 module file somewhere in $env:psmodulepath. I can check a classroom in under 10 seconds.
function get-pport { # multi-threaded
param($list)
$list |
% { $_ | start-threadjob { get-port $input } -throttlelimit 20 } |
receive-job -wait -auto
}
function Get-Port {
Param (
[parameter(ValueFromPipeline)]
[string[]]$Hostname='yahoo.com'
)
begin {
$ports = 22,5988,3389,5985
$ping = New-Object System.Net.Networkinformation.ping
$Timeout = 200 # ms
}
process {
$hostname | foreach {
$openPorts = #()
foreach ($port in $ports) {
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($_,$port,$null,$null)
Start-Sleep -Milli $TimeOut
if($client.Connected) { $openPorts += $port }
$client.Close()
}
$result = $Ping.Send($_, $timeout)
if (! $result) { write-error "hostname $_ not found" }
$pingstatus = ($result.status -eq 'Success')
New-Object -typename PSObject -Property #{
HostName = $_
Port = $openPorts
Ping = $pingstatus
} | select hostname,port,ping
} # end foreach
} # end process
}
Example:
$avid = cat afile.txt
pport $avid
HostName Port Ping
-------- ---- ----
A006 {3389, 5985} True
A011 {3389, 5985} True
A015 {3389} True

Why does PowerShell script fail to close mspaint when the image's filename contains brackets?

Here's the whole code:
$kansio = ($args[0]).replace("'","")
$tiedostot = 0
Get-ChildItem $kansio | foreach {if ($_.Extension -eq ".jpg"){$_.FullName}} |
foreach {
$id = (Start-process mspaint $_ -ea 0 -PassThru).Id
$vikaTallennus = (Get-ChildItem $_).LastWriteTime
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
do
{
try
{
if ((Get-Process -Id $id -ea 'stop').WaitForInputIdle())
{
[Microsoft.VisualBasic.Interaction]::AppActivate($id)
[System.Windows.Forms.SendKeys]::SendWait("^s")
[System.Windows.Forms.SendKeys]::SendWait("{esc}")
for ($i=1; $i -le 10; $i++)
{
if ((Get-ChildItem $_).LastWriteTime -gt $vikaTallennus)
{
try
{
Stop-Process -Id $id -force -ea 'stop'
Write-Host "Done: $_" -foregroundcolor Green
break
}
catch
{
Write-Host "Failed to close the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
}
}
}
}
catch
{
Write-Host "Failed to catch the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
} until ((Get-Process -Id $id -ea 0) -eq $null -or $id -eq $null)
$tiedostot++
}
Write-Host "Files saved: $tiedostot" -foregroundcolor Green
If the filename of a JPG picture contains "[" or "]" PowerShell fails to close MSPaint. Why?
What workarounds are there? Can I use some literalname thing in here?
Yes, if you want to use Get-ChildItem to return files/folders with brackets (and some other weird characters), you should use the -LiteralPath parameter. Here's a decent writeup.
So if you expect such characters in the filenames, and you don't need wildcard support, the pertinent line should be:
if ((Get-ChildItem -LiteralPath $_).LastWriteTime -gt $vikaTallennus)

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
}