PowerShell · 28.06.2024

Адрес и имя одной строкой

Разнести файл hosts по удалённым системам групповыми политиками — дело не хитрое. Да и отредактировать сам файл проще простого. А что если нужно работать с файлом из консоли, адресно и (желательно) на удалённой машине?

Как раз для этих целей и можно использовать PowerShell-модуль PsHosts, не изобретая очередное колесо. Всю документацию по модулю можно найти в репозитории на котоосьминоге. Там даже есть классная гифка с демонстрацией работы модуля:

Модуль умеет работать с нестандартными расположениями файла hosts прямо из коробки. Это и позволит добираться до файла hosts на удалённых машинах при условии, что разрешена работа в админской шаре. Кроме того авторами проекта заявлена мультиплатформенность и поддержка директивы whatif.

К сожалению, до мультиплатформы я ещё не добрался, а вот на whatif обратил пристальное внимание во время тестирования командлетов. Теперь к теме поста.

Устанавливается модуль вполне стандартно:

# Установка для текущего пользователя #
Install-Module PsHosts -Scope CurrentUser

# Установка для всех пользователей #
Install-Module PsHosts -Scope AllUsers

Потестировав командлеты и определив для себя необходимый минимум (Get-HostEntry, Test-HostEntry, Add-HostEntry и Remove-HostEntry), я принялся за написание функций-кирпичиков, которые мог бы использовать в финальном скрипте.

Начну с сопутствующего функционала. Прежде всего мне потребовалась вот эта функция, с помощью которой можно устанавливать или импортировать модули. Обращаю внимание, что для работы GetModule потребуется доступ к репозиториям PowerShell.

Первой я написал функцию проверки доступности удалённого хоста и самого сетевого пути до заветного файла:

function GetRemoteHostsPath
{
    Param([Parameter(Mandatory=$true)]$ComputerName)
    $ping = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
    if ($ping)
    {
        $path = Test-Path -Path "\\$ComputerName\c$\windows\system32\drivers\etc\hosts" -PathType Leaf
        if ($path) { return $true }
        else { return $false }
    }
    else
    {
       return $false
    }
}

GetRemoteHostsPath принимает в качестве аргумента имя удалённого компьютера, проверяет его доступность и пытается обратиться к файлу hosts. Если хоть одно из указанных действий завершается неудачей — функция возвращает False.

Затем мне потребовалась функция, возвращающая все правила, описанные в файле hosts:

function GetRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $content = Get-HostEntry -HostsPath $remotehostspath 
    if ($content -eq $null) 
    { return "Записей в файле hosts на $ComputerName не найдено!"}
    else { return $content }
}

Всю основную работу берёт на себя командлет Get-HostEntry, а функция лишь передаёт ему путь до файла и обрабатывает пустой вывод. Данные, которые возвращаются в консоль уложены в таблицу и обращаться с ними можно соответствующе. Разумеется, ничего кроме вывода в консоль содержимого файла hosts функция не делает.

Приступим к основному функционалу. Логическая функция AddRemoteHosts для добавления записи в файл hosts:

function AddRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName,
          [Parameter(Mandatory=$true)]$IP,
          [Parameter(Mandatory=$true)]$Name)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $test = Test-HostEntry -HostsPath $remotehostspath -Name $Name
    if (!$test)
    { 
        Add-HostEntry -HostsPath $remotehostspath -Name $Name -Address $IP | Out-Null
        return $true
    }
    else { return $false }
}

Принимает в качестве параметров имя удалённого компьютера, а также IP-адрес и DNS-имя для сопоставления.

Конечно, добавить запись в файл — полдела. Необходимо иметь функционал и удаления записи. Для этого написал функцию RemoveRemoteHosts:

function RemoveRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName,
          [Parameter(Mandatory=$true)]$Name)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $test = Test-HostEntry -HostsPath $remotehostspath -Name $Name
    if ($test)
    { 
        Remove-HostEntry -HostsPath $remotehostspath -Name $Name | Out-Null
        return $true
    }
    else { return $false }
}

Прежде чем удалить запись, функция RemoveRemoteHosts проверяет её наличие с помощью командлета Test-HostEntry.

Теперь объединяем весь этот набор функций в один скрипт с менюшкой и дружим с модулем ActiveDirectoryObjectPicker для понятного обращения к Active Directory:

## Функция для поиска модулей ##
function GetModule
{
   Param([Parameter(Mandatory=$true)]$ModuleName)
   $module = Get-Module -ListAvailable -Name $ModuleName
   if ($module)
   {
      Import-Module $ModuleName
   }
   else
   {
      Install-Module $ModuleName -Scope CurrentUser
      Import-Module $ModuleName -Force
   }
}

## Проверка сетевого пути ##
function GetRemoteHostsPath
{
    Param([Parameter(Mandatory=$true)]$ComputerName)
    $ping = Test-Connection -ComputerName $ComputerName -Count 1 -Quiet
    if ($ping)
    {
        $path = Test-Path -Path "\\$ComputerName\c$\windows\system32\drivers\etc\hosts" -PathType Leaf
        if ($path) { return $true }
        else { return $false }
    }
    else
    {
       return $false
    }
}

## Получить записи из hosts ##
function GetRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $content = Get-HostEntry -HostsPath $remotehostspath 
    if ($content -eq $null) 
    { return "Записей в файле hosts на $ComputerName не найдено!"}
    else { return $content }
}

## Добавить записи в hosts ##
function AddRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName,
          [Parameter(Mandatory=$true)]$IP,
          [Parameter(Mandatory=$true)]$Name)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $test = Test-HostEntry -HostsPath $remotehostspath -Name $Name
    if (!$test)
    { 
        Add-HostEntry -HostsPath $remotehostspath -Name $Name -Address $IP | Out-Null
        return $true
    }
    else { return $false }
}

## Удалить записи из hosts ##
function RemoveRemoteHosts
{
    Param([Parameter(Mandatory=$true)]$ComputerName,
          [Parameter(Mandatory=$true)]$Name)
    $remotehostspath = "\\$ComputerName\c$\windows\system32\drivers\etc\hosts"
    $test = Test-HostEntry -HostsPath $remotehostspath -Name $Name
    if ($test)
    { 
        Remove-HostEntry -HostsPath $remotehostspath -Name $Name | Out-Null
        return $true
    }
    else { return $false }
}

# Импорт и/или установка модулей #
GetModule -ModuleName PSHosts
GetModule -ModuleName ActiveDirectoryObjectPicker

# Получение имени удалённого компьютера #
[System.Console]::Title = "RemoteHostsFile"
Write-Host "Ожидание данных..." -ForegroundColor Cyan
$remotehost = (Show-ActiveDirectoryObjectPicker -AllowedLocations EnterpriseDomain -DefaultObjectTypes Computers -DefaultLocations EnterpriseDomain).Name
Clear
[System.Console]::Title = "RemoteHostsFile on $remotehost"

# Добавление/удаление записей #
$getpath = GetRemoteHostsPath -ComputerName $remotehost
if ($getpath)
{
    while ($true)
    {
        Write-Host
        Write-Host "Получаю содержимое файла hosts на $remotehost..."
        GetRemoteHosts -ComputerName $remotehost
        Write-Host
        Write-Host "1. Добавить запись в файл hosts" -ForegroundColor Yellow
        Write-Host "2. Удалить запись из файла hosts" -ForegroundColor Yellow
        Write-Host
        $choice = Read-Host "Выберите вариант"
        Write-Host 
        switch($choice)
        {
           1 # Добавить запись в файл hosts
           {
              $name = Read-Host "Введите DNS имя нового правила"
              $ip = Read-Host "Введите IP-адрес нового правила"
              $add = AddRemoteHosts -ComputerName $remotehost -Name $name -IP $ip
              Clear
              if ($add) { Write-Host "Правило для $name успешно добавлено!" -ForegroundColor Green }
              else { Write-Host "Что-то пошло не так!" -ForegroundColor Red }
              
           }
           
           2 # Удалить запись из файла hosts
           {  
              $name = Read-Host "Введите DNS имя правила"
              Clear
              $remove = RemoveRemoteHosts -ComputerName $remotehost -Name $name
              if ($remove) { Write-Host "Правило $name успешно удалено!" -ForegroundColor Green }
              else { Write-Host "Что-то пошло не так!" -ForegroundColor Red }
           }
           
           default # Любой другой ввод
           {
              Clear 
           }        
        }      
    }
}
else
{
	Write-Host "Что-то пошло не так!" -ForegroundColor Red
	Sleep(15)
}

Скрипт можно смело компилировать в exe с помощью PS2EXE. Главное при сборке не забыть убрать параметр -noConsole и поставить галку с повышением прав запуска:

В следующей публикации покажу простейший скрипт для редактирования файла hosts на удалённой машине без установки и импорта дополнительных модулей.

Напоминаю: для любых манипуляций с файлом hosts нужны права администратора!