Как powershell взаимодействует с windows api
Перейти к содержимому

Как powershell взаимодействует с windows api

  • автор:

Introduction

The content is available in English and Russian. You can use the language switch in the top toolbar of this website (to the left from the search box) to change language.

This documentation project is based on the blog post series by Matt Graeber:

Here you can find tutorials and examples on how to access the Windows API (formerly called the Win32 API) from PowerShell scripts. Tutorials consider three alternative ways of calling the CopyFile function, and demonstrate the creation of a PowerShell module with the Copy-RawItem cmdlet. The custom cmdlet differs from the standard Copy-Item, as it can handle special device object paths, such as paths to files backed up by the Volume Shadow Copy Service:

Handling of errors thrown by the called Windows API method is also demonstrated.

Copy-RawItem result

Accessing CopyFile is considered as a simple basic example here. You can use a similar approach to access other Windows APIs

Contents

Several sections are not completed yet, the content is under development.

Code Examples

Examples demonstrated in this documentation are available an GitHub: powershell-winapi-tutorial/examples/.

Easily Calling Windows APIs from PowerShell

After my previous post of some snippets from my PowerShell profile; I received another email from the person that prompted me to write that post, asking some questions about calling Windows APIs from PowerShell. It turns out that this isn’t so straight-forward, despite PowerShell’s pitch as a system automation language!

The confusion came from various varying samples online of how to do this in PowerShell; and they all used blocks of C#, which many PowerShell users aren’t particularly familiar with.

Although I couldn’t find a way to do this without the C# wrapper, I did think it was worthwhile extracting some parts out of the C# code to avoid having to manipulate so much C# code in a quoted string inside PowerShell to add new Windows API methods. Unfortunately the argument types still need to be in C#, because I don’t know how to map them from the C++ examples easily.

It’s very basic; and can probably be improved by anyone that has some knowledge of calling Windows APIs; but hopefully it’s a little simpler if you’re less comfortable with C#.

In PowerShell profile:

And to use them:

Hopefully this helps a little. If anyone can suggest improvements (being able to paste method signatures directly from the documentation would be good), I’ll update the post.

Call Native Win32 API in PowerShell

Windows built-in DLLs exposes a lot of APIs for many low-level functionalities. It is quite useful to call native Windows API in PowerShell to accomplish specific tasks which are not being wrapped in any tools to commands. For example, if you want to set a system parameter of Windows, you will find that there is no commands or cmdlet to do that. But SystemParametersInfo function in user32.dll could help you. But writing a native Win32 application to just call this API for a specific task is too heavy. In PowerShell, everything become easy.

Methodology

Matt published an excellent blog on three ways of interacting with Windows API:

  • Use the Add-Type cmdlet to compile C# code. This is the officially documented method.
  • Get a reference to a private type in the .NET Framework that calls the method.
  • Use reflection to dynamically define a method that calls the Windows API function.
    In this blog, I will choose first one.

Background

My task is to set a Windows system parameters. Although you can play tricks with registry key if you can hack it, in some scenario it is hard to manipulate the key because the registry could be a combination of binary code.
After some research, we found a native Windows API, SystemParametersInfo, for this task:

Add-Type

Add-Type is a cmdlet allow us to extend PowerShell session with .NET Framework type (a class).
There is an example about how to call native Windows APIs in the document:

Solution

First, use Add-Type to introduce the specific function into PowerShell session.
Second, call the API in your code. You can pass the parameters either in native code block or PowerShell code block.

Используем PowerShell для работы с REST API

Многие из вас наверняка работают с разнообразными инфраструктурами, используя REST API. А поскольку все более широкие слои населения для автоматизации рутинных задач осваивают PowerShell, то почему бы и не начать применять его для работы с REST API?

Сегодня вашему вниманию предлагается перевод статьи Адама Бертрама, большого поклонника автоматизации, автора сайта adamtheautomator.com, в которой он показывает на примере, как это можно сделать.

Итак, добро пожаловать под кат.

В качестве примера API у нас будет выступать swagger petstore API.

Начну, как и положено в приличных руководствах, с требований к системе. Данный пример создавался с Windows PowerShell v5.1 — проверьте, что у вас он есть. Возможно, что пример будет работать также на v6 и выше, возможно, что на Mac или на Linux ОС — но это не точно (не тестировалось).

Готовим папку и файлы для нового модуля

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

  • В файле манифеста PowerShell (.psd1) будет храниться информация о новом модуле.
  • В файле собственно модуля PowerShell (.psm1) будут храниться все наши командлеты.
  • Ну и папка, в которой будут лежать оба эти файла.

Затем создадим файл манифеста для нашего модуля — это файл .psd1 с метаданными, где вы сможете прописать все требования к модулю и всю информацию о нём. Вообще-то никто вам не запретит создать такой файл вручную, но можно сделать это и с помощью командлета New-ModuleManifest. Само собой, нужно будет указать необходимые вам параметры.
Создаем файл манифеста под названием Petstore.psd1 в нашей новой папке на диске C:

Файл собственно модуля с расширением .psm1 как раз и будет содержать все нужные нам функции. В любом PowerShell-редакторе создадим новый файл и положим его на диск C.

Прописываем URI для API-эндпойнтов

Строго говоря, это необязательная операция, но будет хорошим тоном в файле .psm1 прописать основной идентификатор ресурса (base URI) для API-эндпойнтов как переменную — это многое упростит, в том числе и в будущем (переход от версии к версии). Ну а в качестве формата данных будем использовать JSON.

Получится вот так:

Затем мы сможем использовать эту переменную в любой функции с вызовом
Invoke-RestMethod:

Создаем первую функцию

Для начала создадим функцию, которая будет получать от эндпойнта https://petstore.swagger.io/v2/pet/ данные о питомцах. Назовем ее Get-PetstorePet:

Мы определили параметр PetId так, чтобы он соответствовал определенному параметру REST API для этого эндпойнта.

Примечание: Полный перечень параметров для данного примера эндпойнта можно найти тут.

Используя ValueFromPipeline и ValueFromPipelineByPropertyName в нашей функции, можно передавать ей параметры из пайплайна:

Импортируем модуль

Для этого выполняем команду Import-Module:

После чего можно будет запускать выполнение нашей функции Get-PetstorePet:

Добавляем новую запись

Как разъясняется в документации для API, чтобы добавить нового питомца, нужно написать тело запроса в формате JSON. Ну и логично, что для добавления нового питомца мы будем использовать функцию Add-PetstorePet.

Поскольку PowerShell умеет в объекты, мы не будем валить всё в кучу, а создадим хэш-таблицу, которую затем преобразуем в JSON:

Это куда проще и короче, чем создавать всеобъемлющий JSON. На основе нашей хэш-таблички определим параметры, которые понадобятся нам для команды:

Поскольку блок Begin ровно такой же, как и у предыдущей команды, мы можем его переиспользовать, слегка изменив URI (т.к. параметр PetID тут уже не нужен):

Затем определим блок Process, который создаст post-запрос, используя формат JSON, и отправит его к REST API-эндпойнту, задействуя Invoke-RestMethod.

Заметьте, что мы удалили у $BodyObject свойства со значением null — ибо некоторые REST API не любят, когда им пытаются скормить такие значения.

Наша функция добавления питомца будет выглядеть в итоге так:

Снова импортируем модуль и запускаем нашу функцию:

Пишем новые функции

Теперь мы освоились с функциями и можем на основе этого подхода создавать функции для других методов, работающих с REST API.

Например, на базе функции Add-PetstorePet можно создать функцию удаления Remove-PetstorePet — для этого надо дать ей соответствующее название и заменить метод, который вызывается командой Invoke-RestMethod, на метод DELETE , как показано ниже:

Поскольку параметры были настроены на прием pipeline input, вы можете спокойно организовать пайп питомцев в функцию Remove-PetstorePet:

В заключение

Как вы наверняка заметили, при создании модуля PowerShell для REST API многое завязано на повторное использование. Поскольку многие REST API весьма схожи с swagger API, то вполне можно переиспользовать модуль для работы с разными API. Поддержка же пайплайна еще более упрощает работу.

Ну а если у вас тоже есть чем поделиться из своей практики автоматизации, добро пожаловать в комментарии.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *