Using powershell to modify notes.ini - powershell

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

Related

having trouble with powershell using Out-File to append results to existing file

so I m having this issue with getting my results to write out to an Out-file.
The problem I m having is, if new file item does exist, the error message I m getting is saying the path is null.
Please review the script and let me know your thoughts
thanks
$sScriptVersion = "1.0"
$dir = $PSScriptRoot
$svrlist += $dir + '\' + 'serverlist.txt'
$folder = 'search'
$computername = Get-Content $svrlist
$drv_array = #()
$new_drv_array =#()
$SearchStr = "error"
$log_pth = 'ProgramData\something\something\something\logs'
$file_logs = #()
$log_pth = #()
$log_nme = #()
$srchpath = #()
foreach($server in $computername)
{
$disks = Get-WmiObject -ComputerName $server -Class Win32_LogicalDisk -Filter "DriveType = 3";
foreach($disk in $disks)
{
$drv_array += $disk.DeviceID;
}
$driv =Foreach-Object {$drv_array -replace ":", "$"}
foreach($drv in $driv)
{
$new_drv_array += '\\'+ $server + '\'+ $drv +'\'+ $log_pth
}
}
Foreach($tst_pth in $new_drv_array )
{
$dvr_testpath = test-path $tst_pth
$srchpath += $tst_pth + '\'+ "*.*"
if ($dvr_testpath -eq $false)
{
write-host "This path does not exist " $tst_pth
}
else
{
write-host "This path does exist" $tst_pth
$Sel = Select-String -pattern $SearchStr -Context 2, 3 -path $srchpath
$out_file = New-item -ErrorAction ignore -itemtype File (join-path $dir1 $log_nme)
This is the part I'm having the issues with
$tst = Test-Path $out_file
if($tst -eq $false)
{
write-host "writing out parsed log file"
if($Sel -ne $null)
{
$Sel| Out-File $out_file
}
}
else
{
write-host " parsed log file does exist"
if($Sel -ne $null)
{
$Sel| Out-File -Append $out_file
}
}
}
}

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 ....

My script runs in powershell ISE but not powershell console?

function Get-LoggedOnUsers ($server) {
if($server -eq $null){
$server = "localhost"
}
$users = #()
# Query using quser, 2>$null to hide "No users exists...", then skip to the next server
$quser = quser /server:$server 2>$null
if(!($quser)){
Continue
}
#Remove column headers
$quser = $quser[1..$($quser.Count)]
foreach($user in $quser){
$usersObj = [PSCustomObject]#{Server=$null;Username=$null;SessionName=$null;SessionId=$Null;SessionState=$null;LogonTime=$null;IdleTime=$null}
$quserData = $user -split "\s+"
#We have to splice the array if the session is disconnected (as the SESSIONNAME column quserData[2] is empty)
if(($user | select-string "Disc") -ne $null){
#User is disconnected
$quserData = ($quserData[0..1],"null",$quserData[2..($quserData.Length -1)]) -split "\s+"
}
# Server
$usersObj.Server = $server
# Username
$usersObj.Username = $quserData[1]
# SessionName
$usersObj.SessionName = $quserData[2]
# SessionID
$usersObj.SessionID = $quserData[3]
# SessionState
$usersObj.SessionState = $quserData[4]
# IdleTime
$quserData[5] = $quserData[5] -replace "\+",":" -replace "\.","0:0" -replace "Disc","0:0"
if($quserData[5] -like "*:*"){
$usersObj.IdleTime = [timespan]"$($quserData[5])"
}elseif($quserData[5] -eq "." -or $quserData[5] -eq "none"){
$usersObj.idleTime = [timespan]"0:0"
}else{
$usersObj.IdleTime = [timespan]"0:$($quserData[5])"
}
# LogonTime
$usersObj.LogonTime = (Get-Date "$($quserData[6]) $($quserData[7]) $($quserData[8] )")
$users += $usersObj
}
return $users
}
function LogOutUser {
$test = Get-LoggedOnUsers
$count = 0
$nameArray = #()
$sessionArray = #()
$logonTimeArray = #()
foreach($line in $test){
$field1 = $line.Username
$field2 = $line.SessionID
$field3 = $line.LogonTime
$nameArray += $field1
$sessionArray += $field2
$logonTimeArray += $field3
$count+=1
}
$session1 = [INT]$sessionArray[0]
$session2 = [INT]$sessionArray[1]
Write-Host $nameArray
Write-Host $sessionArray
Write-Host $logonTimeArray
Write-Host $count
Write-Host $session1
Write-Host $session2
if(($count -gt 1) -and ($session2 -gt $session1 )){
logoff $session1
}
}
Get-LoggedOnUsers
LogOutUser
this code works in the ISE but it does not work when running from the console, what is happening? It does exactly what i want it to do through the ISE but when I try running as a scheduled task it doesn't work also it doesnt work when trying to run with console. i have tried powershell.exe -noexit -file c:\logofftest1.ps1 but still no success

PowerShell calling function for telnet shows strange behavior

I have a telnet script as function, that behaves well, if it is called directly.
If I try to call it in a loop, it starts to misbehave.
This code is working fine for me:
$HostList = Import-CSV ".\list.csv" -Header IP,Script,SNMP -Delimiter (";")
ForEach ($item in $HostList)
{
Write-Host "Connecting to: $item" -NoNewline
$Script = Get-Content (".\Scripts\" + $item.Script + ".txt")
Get-Telnet -RemoteHost $item.IP -SNMPLoc $item.SNMP -Login $login -PassWord $password -Commands $Script
}
The following changes seems to break the script, as I want to make the contril file more dynamic:
$login = Read-Host 'Please provide the login? '
$password = Read-Host -AsSecureString 'Please provide the password? '
Import-CSV ".\list.csv" -Delimiter (";") | Foreach-Object {
[String[]] $VarValues = ""
foreach ($item in $_.PSObject.Properties)
{
if ($item.Name.Contains("IP"))
{
$HostName = $item.Value
}
if ($item.Name.Contains("Script"))
{
$Script = Get-Content (".\Scripts\" + $item.Value + ".txt")
}
if (!$item.Name.Contains("IP") -and !$item.Name.Contains("Script"))
{
$VarValues += $item.Name + "#" + $item.Value
}
}
Get-Telnet -RemoteHost $HostName -Login $login -PassWord $password -Commands $Script -Vars $VarValues
}
If I call Get-Telnet the function on is own, it's still working.
As I don't known, how many possible columns might be in the control file (.\list.csv), is there any other way, to create $VarValues list from the file?
I changed it to >
$data = Import-CSV $ControlFile -Delimiter (";")
$OutputPath = $OutputPath + "%{0}.txt"
Clear-Host
foreach ($obj in $data)
{
[String[]] $VarValues = ""
foreach ($item in $obj.psobject.properties)
{
$Key = $item.name
$Value = $item.value
switch ($Key)
{
"IP" { $HostName = $Value }
"Script" { $Script = Get-Content ($ScriptPath + $Value + ".txt") }
default { $VarValues += $Key + "#" + $Value }
}
}
Get-Telnet -RemoteHost $HostName -Login $login -PassWord $password -Commands $Script -Vars $VarValues
}
That seems to do the trick and the idea to use switch makes things even easier.
Something might be off with the W2K itself - as soon as the pipe is gone - all goes well.

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
}