Как работать в Powershell с модулями и профилями и повторно использовать данные


18 марта 2021


Работа с профилями и модулями Powershell и повторное использование данных

Работая в Powershell можно столкнутся с неудобствами, когда вам всегда нужна какая-то переменная или функция (созданная вами), но после закрытия консоли (сессии) эти данные всегда исчезают. При открытии консоли вы либо копируете старый код либо печатаете все заново. Обойти эти проблемы можно использовав встроенные методы работы в Powershell: профили, модули и переменные окружения. Каждый из этих методов имеет свои отличия, но все они решают проблему с излишними действиями.

 

Профили

У каждого пользователя Powershell есть свой профиль. Профиль выглядит как обычный файл со скриптом формата ps1. Открывая консоль Powershell вы автоматически запускаете этот файл и все значения, перечисленные в файле профиля, становятся видимыми для вас. Кроме этого профиль не ограничивается одним пользователем и может использоваться для всех.

Создание

Каждый профиль пользователя хранится в файле ".ps1" и путь до него можно посмотреть используя встроенную переменную:

$Profile

Расположение профиля пользователя в Powershell

Вы можете открыть этот файл и вписать в него любую команду. Я использую команду, которая проверит соединение с DNS сервером Google:

Test-NetConnection -ComputerName 8.8.8.8 -Hops 1

Скрипт срабатывающий при открытии консоли Powershell

Теперь, каждый запуск консоли будет выводить следующее, дополнительное, сообщение:

Скрипт срабатывающий при открытии консоли Powershell

Отмечу, что это не самый удачный пример т.к. эта команда занимает существенное время на выполнение. Вы вряд ли захотите ждать 4-5 секунд для запуска консоли.

Вам может не будет требоваться отображать подобный результат при каждом запуске. Вы можете поместить эти данные в переменную и затем вызывать ее по мере работы в консоли:

$result = Test-NetConnection -ComputerName 8.8.8.8 -Hops 1

Загрузка Powershell с переменной

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

function MyJob(){
    Test-NetConnection -ComputerName 8.8.8.8 -Hops 1
}

Использование функции при загрузке консоли Powershell

Типы профилей

Если посмотреть содержание переменной $Profile более детально, то мы увидим 4 типа профилей:

$Profile | select *

Типы профилей для работы в Powershell

В выведенной информации можно увидеть понятие 'host'. Под "хостом" подразумевается программа, которая хостит Powershell. Это может быть стандартная консоль, редактор ISE, VisualStudio и т.д. 

Мы так же увидим следующие профили:

  • AllUsersAllHosts - профиль для всех пользователей и для всех хостов;
  • AllUsersCurrentHost - профиль для всех пользователей и только для текущего хоста;
  • CurrentUserAllHosts - профиль для текущего пользователя и для всех хостов;
  • CurrentUserCurrentHost - профиль для текущего пользователя и только для этого хоста.

Указанный путь - это место, где уже есть или может быть создан файл. При каждом запуске Powershell проверяет эти пути и читает данные.

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

echo "AllUsersAllHosts"
echo "AllUsersCurrentHost"
echo "CurrentUserAllHosts"
echo "CurrentUserCurrentHost"

Запуск средства разработки Powershell ISE и обычной консоли был следующий:

Разница профилей в Powershell

Как видно, в ISE, отображаются не все профили. Связано это с тем, что консоль Powershell и редактор ISE - это разные хосты. Мы можем увидеть немного отличающиеся профили если запустить эту команду в ISE:

$Profile | select *

Профили редактора ISE в Powershell

Так же вы можете увидеть дополнительные профили если используете Powershell Core 6/7, VisualStudio и т.д..

Профиль при удаленном подключении

Прямых способов использования профилей при удаленном подключении нет. Есть косвенный способ, который заключается в возможности использования файлов со скриптами в команде "Invoke-Command". Следующий пример демонстрирует такую возможность:

Invoke-Command -ComputerName "localhost" -FilePath $Profile

На самом деле эта команда выглядит следующим образом:

$path = "C:\Users\%username%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
Invoke-Command -ComputerName "localhost" -FilePath $path

 

Модули

Модуль Powershell - это файл формата "*.psm1" объединяющий в себе одну или несколько функций (команд). Powershell проверяет папки с модулями и выводит основную информацию о них. Когда вам нужно использовать какую-то команду, то Powershell повторно обращается к файлу модуля для ее выполнения.

Отличие модуля от профиля в том, что профиль загружает все в оперативную память сразу, а модуль по требованию (обращению).

Каждая команда (функция) Powershell относится к какому-то модулю. Увидеть модули можно с помощью нескольких команд, одна из них следующая:

Get-Command

Команды и модули Powershell

Эти модули находятся в определенных директориях. Пути до этих директорий описаны в переменной глобального окружения и вывести ее можно так:

$env:PSModulePath -split ';'

Список путей для создания модулей Powershell

Вы можете поместить свой модуль в любую директорию, но в основном они делятся на следующие:

  • В папке "Windows" - хранятся модули, которые относятся к самому Powershell. Они появляются при установке Windows и могут быть написанные на других языках;
  • В "Program Files" - лежат модули, которые устанавливаются отдельно (например из какого-то репозитория);
  • "Users" - обычно пустой и подразумевается, что в нем лежат ваши личные модули.

Какие модули и где находятся можно увидеть с помощью следующей команды:

Get-Module -ListAvailable

Список модулей и их расположения Powershell

Создание

Что бы создать модуль вам нужно создать папку и файл по одному из путей полученных раннее. Папка и файл должны иметь одинаковые имена. Для примера я создал файл модуля и папку с названием "MyModule" по следующему пути:

C:\Users\1\Documents\WindowsPowerShell\Modules\MyModule\MyModule.psm1

В этот файл я поместил следующую функцию:

function Test-Me(){
    echo "hello world"
}

При открытии новой консоли Powershell мы сможем сразу использовать эту команду:

Test-Me

Использование созданного модуля Powershell

Мы не можем помещать в файл модуля переменные, но их может возвращать сама функция. Так же модули лучше подходят для случаев, когда вам нужно выполнять код удаленно.

Манифесты и справки

Если у вас большой модуль и если им пользуетесь не только вы, то стоит его описать. В программировании, для таких вещей, чаще всего используются комментарии. В Powershell, кроме комментариев, есть еще два понятия:

  • манифест - описывает автора модуля, дату его создания, ссылку на модуль и т.д.;
  • справка - описывает принцип работы с функцией/командой.

Пример манифеста можно увидеть на примере "AppLocker" и следующей команды:

Get-Module -ListAvailable | where name -eq AppLocker | select *

Вывод манифеста модуля Powershell

Манифест подразумевает создание дополнительного файла по тому же пути что и сам модуль. Название модуля, папки и файла манифеста должны совпадать. Расширение манифеста должно быть psd1. Т.е. используя предыдущий пример мой файл манифеста и модуль будут иметь следующие пути:

# модуль
C:\Users\1\Documents\WindowsPowerShell\Modules\MyModule\MyModule.psm1
# манифест
C:\Users\1\Documents\WindowsPowerShell\Modules\MyModule\MyModule.psd1

В этом файл нужно поместить массив с вашими значениями следующего формата:

@{
Author = "Alexander Melnikov"
ModuleVersion = "0.0.1"
}

Ключи заранее определены и вы не можете использовать произвольные. Манифест можно создать скопировав его у существующего модуля (путь указан в ModuleBase со скриншота выше) либо использовать команду New-ModuleManifest. Эту команду можно использовать как генератор шаблона, так и используя ключи:

New-ModuleManifest -Path "C:\Users\1\Documents\WindowsPowerShell\Modules\MyModule\MyModule.psd1" -PassThru

Создание шаблона манифеста пользователя Powershell

Манифест это так же важная часть, которая должна быть создана, если вы планируете опубликовать свой модуль в публичный репозиторий Powershell. В этой статье не описано, но для публикации в репозиторий Powershell ваши функции так же должны иметь определенную логику в названиях.

Справка в Powershell описывает работу не модуля, а функции/команды. Справку можно увидеть в следующем примере:

Get-Help Get-Command

Создание справки Powershell

Самой важной частью справки, на мой взгляд, является раздел с примерами:

Get-Help Get-Command

Создание справки Powershell и описания

Сама справка описывается внутри модуля у каждой функции отдельно. Обычно используется следующий шаблон:

function Some-Funct(){
<#
	    .SYNOPSIS
	    Returns registry key using provided path.
	    .DESCRIPTION
	    The function uses the Get-Item command to return the information for a provided registry key.
	    .PARAMETER Path
	    The path that will be searched for a registry key.
	    .EXAMPLE
	    Get-RegistryKey -Path 'HKLM:\HARDWARE\DESCRIPTION\System'
	    .INPUTS
	    System.String
	    .OUTPUTS
	    Microsoft.Win32.RegistryKey
	    .NOTES
	    This module is an example of what a well documented function could look.
	    .LINK
	https://fixmypc.ru
	#>
   return "Pass"
}

Удаленное выполнение модулей

Самый тривиальный способ передать команду - это использование Invoke-Command:

Invoke-Command -ComputerName "localhost" -Scriptblock {Some-Funct}

Минус этого способа в том, что у вас может быть с 10-ок модулей, которые вы захотите использовать. В Powershell есть интересный способ импортировать модули с удаленного компьютера на локальный. Для этого мы должны создать сессию с New-PSSession, а затем импортировать с этой сессии модули. В примере ниже я импортировал модуль "ActiveDirectory" не имея его на локальном компьютере:

$remote_pc = New-PSSession -ComputerName $servcer -Credential (Get-Credential)
Import-Module -Name ActiveDirectory -PSSession $AdminServer -Prefix "From AD1"

Импорт модуля с удаленного хоста в Powershell

Изменение PSModulePath и добавление сетевого хранилища модулей

Еще одна не очевидная возможность - это создание сетевого пути для хранения модулей. Это может пригодится, если вы создаете свои модули в команде или используете их на множестве компьютерах. Что бы вы их могли читать - вам нужно будет изменить переменную окружения "PSModulePath" добавив в нее путь формата:

path = "\\remotepath\Modules"

Этот сетевой каталог должен будет быть доступен для чтения и без дополнительной аутентификации (случай, если вы подключаетесь к файловым серверам от другого пользователя). 

Этот путь можно занести в переменные окружения обычным способом через GUI либо с помощью следующих команд Powerhsell:

# сетевой путь
$new_path = $env:PSModulePath + ";\\remotepath\Modules"
# добавление в окружение
[System.Environment]::SetEnvironmentVariable('PSModulePath', $new_path,[System.EnvironmentVariableTarget]::Machine)

Использование модуля по сетевому пути Powershell

Новое значение в PSModulePath появится после перезапуска консоли.

 

Переменные окружения

Через Powershell можно устанавливать и читать переменные окружения. Обычные методы позволяют изменять значения окружения только на текущую сессию, но вы можете использовать методы .NET, которые могут создать постоянные значения.

Значения, которые вы можете установить в переменных окружения, могут быть только строками. Создать массивы, числа и функции вы не сможете. Плюсы этого метода в том, что они будут доступны для любого интерпретатора и программы.

Чтение

Для получения переменных окружения вы можете использовать 2 способа. Первый - через встроенную переменную $env. Так это будет выглядеть на примере переменной Path:

$env:path

# более читаемый вид
$env:path -split ';'

Вывод переменных окружения Powershell

Еще один способ заключается в том, что в Powershell переменные окружения располагаются как отдельный раздел в который вы можете перейти и вывести все значения:

cd env:\
ls

Вывод переменных окружения Powershell

Открыть значение любой переменно можно так же как и любой другой файл:

cat env:\path

Переменные окружения делятся на несколько типов: machine -> user -> process, .

  • machine - относится к ОС и эти переменные может прочитать любой пользователь;
  • user - относятся к конкретному пользователю. Эти значения перезаписывают значения machine;
  • process - динамические переменные, которые создаются при запуске процесса. Они являются совмещением user + machine.

Используя способы выше вы видите только динамические переменные. Если у вас есть 2 одинаковых переменных на пользовательском и системном уровне вы можете использовать метод .NET для получения конкретной переменной:

# Получение значения системной переменной
[System.Environment]::GetEnvironmentVariable('PATH','machine')

# Получение значения пользовательской переменной
[System.Environment]::GetEnvironmentVariable('PATH','user')

Вывод системных переменных окружения Powershell используя метод .NET

Либо вы можете все переменные окружения конкретного типа:

# Получение всех переменных конкретного типа
[System.Environment]::GetEnvironmentVariables('User')
[System.Environment]::GetEnvironmentVariables('Process')
[System.Environment]::GetEnvironmentVariables('Machine')

Создание

Переменную окружения, которая будет жить в рамках одной сессии Powershell, можно создать следующей командой:

New-Item -Name test1 -Value value1 -Path env:\

Создание временной переменной окружения Powershell

При закрытии консоли, переменна созданная выше, будет удалена. Исправить это можно использовав методы .NET. В следующем примере мы создадим переменную типа "Machine", но уже постоянную:

[System.Environment]::SetEnvironmentVariable('test1', 'value1',[System.EnvironmentVariableTarget]::Machine)

Создание постоянной системной переменной окружения в Powershell используя .NET

Аналогичное действие, но для пользовательских переменных:

[System.Environment]::SetEnvironmentVariable('test2', 'value2',[System.EnvironmentVariableTarget]::User)

Создание постоянной пользовательской переменной окружения в Powershell используя .NET

Изменение и удаление

Изменить переменную окружения, на текущую сессию, можно следующими способом:

# вариант 1
$env:testvar += ";C:\folder2\"

# вариант 2
Set-Item -Path "Env:\testvar" -Value ($env:testvar +"C:\folder3\;")

Удаление переменной окружения с Powershell на текущий сеанс

Что бы сделать изменения постоянными нужно использовать метод .NET:

# Изменим старое значение
$new_path = $env:testvar + "E:\;"
[System.Environment]::SetEnvironmentVariable('testvar', $new_path, 'Process')

# Удалим переменную и значение
[System.Environment]::SetEnvironmentVariable('testvar', '', 'Process')

Удаление переменной окружения с Powershell используя .NET

...

Теги: #powershell


Каналы
Telegram FixMyPc Telegram Лента FixMyPC RSS Rss
Популярные тэги
О блоге
Этот блог представляет собой конспекты выученного материала, приобретённого опыта и лучшие практики в системном администрировании и программировании.