about_Classes_and_DSC
Краткое описание
Описывает, как использовать классы для разработки в PowerShell с помощью требуемой конфигурации состояния (DSC).
Длинное описание
Начиная с Windows PowerShell 5.0, язык был добавлен для определения классов и других определяемых пользователем типов с помощью формального синтаксиса и семантики, аналогичных другим объектно-ориентированным языкам программирования. Цель заключается в том, чтобы разработчики и ИТ-специалисты могли использовать PowerShell для более широкого спектра вариантов использования, упростить разработку артефактов PowerShell, таких как ресурсы DSC, и ускорить охват поверхностей управления.
Поддерживаемые сценарии
Поддерживаются следующие сценарии:
- Определите ресурсы DSC и связанные с ними типы с помощью языка PowerShell.
- Определите пользовательские типы в PowerShell с помощью знакомых объектно-ориентированных конструкций программирования, таких как классы, свойства, методы и наследование.
- Типы отладки с помощью языка PowerShell.
- Создание и обработка исключений с помощью формальных механизмов и на правильном уровне.
Определение ресурсов DSC с помощью классов
Помимо изменений синтаксиса, основные различия между определенным классом ресурса DSC и поставщиком ресурсов DSC командлета являются следующими элементами:
- Файл формата объекта управления (MOF) не требуется.
- Подпапка DSCResource в папке модуля не требуется.
- Файл модуля PowerShell может содержать несколько классов ресурсов DSC.
Создание поставщика ресурсов DSC, определяемого классом
Следующий пример — это поставщик ресурсов DSC, который сохраняется в виде модуля MyDSCResource.psm1. Необходимо всегда включать свойство ключа в определяемый классом поставщик ресурсов DSC.
enum Ensure
{
Absent
Present
}
<#
This resource manages the file in a specific path.
[DscResource()] indicates the class is a DSC resource
#>
[DscResource()]
class FileResource
{
<#
This property is the fully qualified path to the file that is
expected to be present or absent.
The [DscProperty(Key)] attribute indicates the property is a
key and its value uniquely identifies a resource instance.
Defining this attribute also means the property is required
and DSC will ensure a value is set before calling the resource.
A DSC resource must define at least one key property.
#>
[DscProperty(Key)]
[string]$Path
<#
This property indicates if the settings should be present or absent
on the system. For present, the resource ensures the file pointed
to by $Path exists. For absent, it ensures the file point to by
$Path does not exist.
The [DscProperty(Mandatory)] attribute indicates the property is
required and DSC will guarantee it is set.
If Mandatory is not specified or if it is defined as
Mandatory=$false, the value is not guaranteed to be set when DSC
calls the resource. This is appropriate for optional properties.
#>
[DscProperty(Mandatory)]
[Ensure] $Ensure
<#
This property defines the fully qualified path to a file that will
be placed on the system if $Ensure = Present and $Path does not
exist.
NOTE: This property is required because [DscProperty(Mandatory)] is
set.
#>
[DscProperty(Mandatory)]
[string] $SourcePath
<#
This property reports the file's create timestamp.
[DscProperty(NotConfigurable)] attribute indicates the property is
not configurable in DSC configuration. Properties marked this way
are populated by the Get() method to report additional details
about the resource when it is present.
#>
[DscProperty(NotConfigurable)]
[Nullable[datetime]] $CreationTime
<#
This method is equivalent of the Set-TargetResource script function.
It sets the resource to the desired state.
#>
[void] Set()
{
$fileExists = $this.TestFilePath($this.Path)
if($this.ensure -eq [Ensure]::Present)
{
if(-not $fileExists)
{
$this.CopyFile()
}
}
else
{
if($fileExists)
{
Write-Verbose -Message "Deleting the file $($this.Path)"
Remove-Item -LiteralPath $this.Path -Force
}
}
}
<#
This method is equivalent of the Test-TargetResource script
function. It should return True or False, showing whether the
resource is in a desired state.
#>
[bool] Test()
{
$present = $this.TestFilePath($this.Path)
if($this.Ensure -eq [Ensure]::Present)
{
return $present
}
else
{
return -not $present
}
}
<#
This method is equivalent of the Get-TargetResource script function.
The implementation should use the keys to find appropriate
resources. This method returns an instance of this class with the
updated key properties.
#>
[FileResource] Get()
{
$present = $this.TestFilePath($this.Path)
if ($present)
{
$file = Get-ChildItem -LiteralPath $this.Path
$this.CreationTime = $file.CreationTime
$this.Ensure = [Ensure]::Present
}
else
{
$this.CreationTime = $null
$this.Ensure = [Ensure]::Absent
}
return $this
}
<#
Helper method to check if the file exists and it is correct file
#>
[bool] TestFilePath([string] $location)
{
$present = $true
$item = Get-ChildItem -LiteralPath $location -ea Ignore
if ($null -eq $item)
{
$present = $false
}
elseif( $item.PSProvider.Name -ne "FileSystem")
{
throw "Path $($location) is not a file path."
}
elseif($item.PSIsContainer)
{
throw "Path $($location) is a directory path."
}
return $present
}
<#
Helper method to copy file from source to path
#>
[void] CopyFile()
{
if(-not $this.TestFilePath($this.SourcePath))
{
throw "SourcePath $($this.SourcePath) is not found."
}
[System.IO.FileInfo]
$destFileInfo = new-object System.IO.FileInfo($this.Path)
if (-not $destFileInfo.Directory.Exists)
{
$FullName = $destFileInfo.Directory.FullName
$Message = "Creating directory $FullName"
Write-Verbose -Message $Message
#use CreateDirectory instead of New-Item to avoid code
# to handle the non-terminating error
[System.IO.Directory]::CreateDirectory($FullName)
}
if(Test-Path -LiteralPath $this.Path -PathType Container)
{
throw "Path $($this.Path) is a directory path"
}
Write-Verbose -Message "Copying $this.SourcePath to $this.Path"
#DSC engine catches and reports any error that occurs
Copy-Item -Path $this.SourcePath -Destination $this.Path -Force
}
}
Создание манифеста модуля
Создав определяемый классом поставщик ресурсов DSC и сохранив его в виде модуля, создайте манифест модуля для модуля. Чтобы сделать ресурс на основе класса доступным для ядра DSC, необходимо включить инструкцию DscResourcesToExport
в файл манифеста, который указывает модулю экспортировать ресурс. В этом примере следующий манифест модуля сохраняется как MyDscResource.psd1.
@{
# Script module or binary module file associated with this manifest.
RootModule = 'MyDscResource.psm1'
DscResourcesToExport = 'FileResource'
# Version number of this module.
ModuleVersion = '1.0'
# ID used to uniquely identify this module
GUID = '81624038-5e71-40f8-8905-b1a87afe22d7'
# Author of this module
Author = 'Microsoft Corporation'
# Company or vendor of this module
CompanyName = 'Microsoft Corporation'
# Copyright statement for this module
Copyright = '(c) 2014 Microsoft. All rights reserved.'
# Description of the functionality provided by this module
# Description = ''
# Minimum version of the PowerShell engine required by this module
PowerShellVersion = '5.0'
# Name of the PowerShell host required by this module
# PowerShellHostName = ''
}
Развертывание поставщика ресурсов DSC
Разверните новый поставщик ресурсов DSC, создав папку MyDscResource в $pshome\Modules
или $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
.
Не нужно создавать вложенную папку DSCResource. Скопируйте файлы манифеста модуля и модуля (MyDscResource.psm1 и MyDscResource.psd1) в папку MyDscResource.
На этом этапе вы создадите и запустите скрипт конфигурации, как и любой ресурс DSC.
Создание скрипта конфигурации DSC
После сохранения файлов класса и манифеста в структуре папок, как описано ранее, можно создать конфигурацию, которая использует новый ресурс. Следующая конфигурация ссылается на модуль MyDSCResource. Сохраните конфигурацию в виде скрипта, MyResource.ps1.
Сведения о том, как запустить конфигурацию DSC, см. в обзоре конфигурации требуемого состояния Windows PowerShell.
Перед запуском конфигурации создайте C:\test.txt
. Конфигурация проверяет, существует ли файл в c:\test\test.txt
. Если файл не существует, конфигурация копирует файл из C:\test.txt
.
Configuration Test
{
Import-DSCResource -ModuleName MyDscResource
FileResource file
{
Path = "C:\test\test.txt"
SourcePath = "C:\test.txt"
Ensure = "Present"
}
}
Test
Start-DscConfiguration -Wait -Force Test
Запустите этот скрипт, как и любой скрипт конфигурации DSC. Чтобы запустить конфигурацию, в консоли PowerShell с повышенными привилегиями выполните следующие действия:
PS C:\test> .\MyResource.ps1
Наследование в классах PowerShell
Объявление базовых классов для классов PowerShell
Класс PowerShell можно объявить в качестве базового типа для другого класса PowerShell, как показано в следующем примере, в котором фруктовые является базовым типом для apple.
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
Объявление реализованных интерфейсов для классов PowerShell
Вы можете объявить реализованные интерфейсы после базовых типов или сразу после двоеточия (:
) в случае отсутствия базового типа. Разделите все имена типов с помощью запятых. Это похоже на синтаксис C#.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Вызов конструкторов базового класса
Чтобы вызвать конструктор базового класса из подкласса, добавьте ключевое слово base
, как показано в следующем примере:
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
Если базовый класс имеет конструктор по умолчанию (без параметров), можно пропустить явный вызов конструктора, как показано ниже.
class C : B
{
C([int]$c) {}
}
Вызов методов базового класса
Можно переопределить существующие методы в подклассах. Для переопределения объявите методы с использованием того же имени и подписи.
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
Чтобы вызвать методы базового класса из переопределенных реализаций, приведение к базовому классу ([baseclass]$this)
при вызове.
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
Все методы PowerShell являются виртуальными. Методы , отличные от виртуальных .NET, можно скрыть в подклассе с помощью того же синтаксиса, что и для переопределения: объявлять методы с тем же именем и сигнатурой.
class MyIntList : system.collections.generic.list[int]
{
# Add is final in system.collections.generic.list
[void] Add([int]$arg)
{
([system.collections.generic.list[int]]$this).Add($arg * 2)
}
}
$list = [MyIntList]::new()
$list.Add(100)
$list[0] # return 200
Текущие ограничения с наследованием классов
Ограничение наследования классов заключается в отсутствии синтаксиса для объявления интерфейсов в PowerShell.
Определение пользовательских типов в PowerShell
Windows PowerShell 5.0 представила несколько языковых элементов.
Ключевое слово класса
Определяет новый класс.
Ключевое слово class
— это истинный тип .NET Framework.
Члены класса являются общедоступными.
class MyClass
{
}
Ключевое слово перечисления и перечисления
Добавлена поддержка ключевого слова enum
и является критическим изменением. В настоящее время разделитель enum
является новой строкой. Решение для тех, кто уже использует enum
, заключается в вставке амперсанда (&
) перед словом. Текущие ограничения: нельзя определить перечислитель с точки зрения самой себя, но можно инициализировать enum
с точки зрения другого enum
, как показано в следующем примере:
Базовый тип в настоящее время не может быть указан. Базовый тип всегда является [int].
enum Color2
{
Yellow = [Color]::Blue
}
Значение перечислителя должно быть константой времени синтаксического анализа. Значение перечислителя не может быть задано в качестве результата вызываемой команды.
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
Enum
поддерживает арифметические операции, как показано в следующем примере:
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
Скрытое ключевое слово
Ключевое слово hidden
, представленное в Windows PowerShell 5.0, скрывает членов класса от результатов по умолчанию Get-Member
. Укажите скрытое свойство, как показано в следующей строке:
hidden [type] $classmember = <value>
Скрытые элементы не отображаются с помощью завершения вкладки или IntelliSense, если только не выполняется завершение в классе, определяющем скрытый элемент.
Добавлен новый атрибут, System.Management.Automation.HiddenAttribute, чтобы код C# может иметь ту же семантику в PowerShell.
Дополнительные сведения см. в разделе [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).
Import-DscResource
Import-DscResource
теперь является истинным динамическим ключевым словом. PowerShell анализирует корневой модуль указанного модуля, выполняя поиск классов, содержащих атрибут DscResource.
Свойства
В ModuleInfo
добавлено новое поле ImplementingAssembly
. Если скрипт определяет классы или загруженную сборку для двоичных модулей ImplementingAssembly
устанавливается в динамическую сборку, созданную для модуля скрипта. Не задано, если ModuleType = Manifest.
Отражение поля ImplementingAssembly
обнаруживает ресурсы в модуле. Это означает, что вы можете обнаруживать ресурсы, написанные на powerShell или на других управляемых языках.
Поля с инициализаторами.
[int] $i = 5
Статический поддерживается и работает как атрибут, аналогичный ограничениям типа, поэтому он может быть указан в любом порядке.
static [int] $count = 0
Тип является необязательным.
$s = "hello"
Все члены являются общедоступными. Для свойств требуется новая линия или точка с запятой. Если тип объекта не указан, тип свойства Object.
Конструкторы и экземпляры
Классы PowerShell могут иметь конструкторы, имеющие то же имя, что и класс. Конструкторы могут быть перегружены. Поддерживаются статические конструкторы.
Свойства с выражениями инициализации инициализированы перед выполнением любого кода в конструкторе. Статические свойства инициализированы перед телом статического конструктора, а свойства экземпляра инициализированы перед телом нестатического конструктора. В настоящее время нет синтаксиса для вызова конструктора из другого конструктора, например синтаксиса C#: ": this()")
. Решение заключается в определении общего метода Init.
Ниже приведены способы создания экземпляров классов:
Создание экземпляра с помощью конструктора по умолчанию. Обратите внимание, что
New-Object
не поддерживается в этом выпуске.$a = [MyClass]::new()
Вызов конструктора с параметром.
$b = [MyClass]::new(42)
Передача массива конструктору с несколькими параметрами
$c = [MyClass]::new(@(42,43,44), "Hello")
В этом выпуске имя типа отображается только лексически, т. е. оно не отображается за пределами модуля или скрипта, определяющего класс. Функции могут возвращать экземпляры класса, определенного в PowerShell, и экземпляры работают хорошо вне модуля или скрипта.
Get-Member
статических параметр перечисляет конструкторы, чтобы просматривать перегрузки, как и любой другой метод. Производительность этого синтаксиса также значительно быстрее, чем New-Object
.
Псевдостатический метод с именем новых работает с типами .NET, как показано в следующем примере. [hashtable]::new()
Теперь можно увидеть перегрузки конструктора с Get-Member
или как показано в следующем примере:
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
Методика
Метод класса PowerShell реализуется как ScriptBlock с только конечным блоком. Все методы являются общедоступными. В следующем примере показано определение метода с именем DoSomething.
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
Вызов метода
Перегруженные методы поддерживаются. Перегруженные методы называются тем же, что и существующий метод, но отличаются по указанным значениям.
$b = [MyClass]::new()
$b.DoSomething(42)
Вызов
См. вызов метода.
Атрибуты
Добавлены три новых атрибута: DscResource
, DscResourceKey
и DscResourceMandatory
.
Возвращаемые типы
Тип возвращаемого значения — это контракт. Возвращаемое значение преобразуется в ожидаемый тип. Если возвращаемый тип не указан, возвращаемый тип является void. Потоковая передача объектов и объектов не может быть записана в конвейер намеренно или случайно.
Лексическая области переменных
Ниже показан пример работы лексической области в этом выпуске.
$d = 42 # Script scope
function bar
{
$d = 0 # Function scope
[MyClass]::DoSomething()
}
class MyClass
{
static [object] DoSomething()
{
return $d # error, not found dynamically
return $script:d # no error
$d = $script:d
return $d # no error, found lexically
}
}
$v = bar
$v -eq $d # true
Пример. Создание пользовательских классов
В следующем примере создается несколько новых пользовательских классов для реализации языка динамической таблицы стилей HTML (DSL). В примере добавляются вспомогательные функции для создания определенных типов элементов в рамках класса элементов, таких как стили заголовков и таблицы, так как типы не могут использоваться вне области модуля.
# Classes that define the structure of the document
#
class Html
{
[string] $docType
[HtmlHead] $Head
[Element[]] $Body
[string] Render()
{
$text = "<html>`n<head>`n"
$text += $Head
$text += "`n</head>`n<body>`n"
$text += $Body -join "`n" # Render all of the body elements
$text += "</body>`n</html>"
return $text
}
[string] ToString() { return $this.Render() }
}
class HtmlHead
{
$Title
$Base
$Link
$Style
$Meta
$Script
[string] Render() { return "<title>$Title</title>" }
[string] ToString() { return $this.Render() }
}
class Element
{
[string] $Tag
[string] $Text
[hashtable] $Attributes
[string] Render() {
$attributesText= ""
if ($Attributes)
{
foreach ($attr in $Attributes.Keys)
{
$attributesText = " $attr=`"$($Attributes[$attr])`""
}
}
return "<${tag}${attributesText}>$text</$tag>`n"
}
[string] ToString() { return $this.Render() }
}
#
# Helper functions for creating specific element types on top of the classes.
# These are required because types aren't visible outside of the module.
#
function H1 {[Element] @{Tag = "H1"; Text = $args.foreach{$_} -join " "}}
function H2 {[Element] @{Tag = "H2"; Text = $args.foreach{$_} -join " "}}
function H3 {[Element] @{Tag = "H3"; Text = $args.foreach{$_} -join " "}}
function P {[Element] @{Tag = "P" ; Text = $args.foreach{$_} -join " "}}
function B {[Element] @{Tag = "B" ; Text = $args.foreach{$_} -join " "}}
function I {[Element] @{Tag = "I" ; Text = $args.foreach{$_} -join " "}}
function HREF
{
param (
$Name,
$Link
)
return [Element] @{
Tag = "A"
Attributes = @{ HREF = $link }
Text = $name
}
}
function Table
{
param (
[Parameter(Mandatory)]
[object[]]
$Data,
[Parameter()]
[string[]]
$Properties = "*",
[Parameter()]
[hashtable]
$Attributes = @{ border=2; cellpadding=2; cellspacing=2 }
)
$bodyText = ""
# Add the header tags
$bodyText += $Properties.foreach{TH $_}
# Add the rows
$bodyText += foreach ($row in $Data)
{
TR (-join $Properties.Foreach{ TD ($row.$_) } )
}
$table = [Element] @{
Tag = "Table"
Attributes = $Attributes
Text = $bodyText
}
$table
}
function TH {([Element] @{Tag="TH"; Text=$args.foreach{$_} -join " "})}
function TR {([Element] @{Tag="TR"; Text=$args.foreach{$_} -join " "})}
function TD {([Element] @{Tag="TD"; Text=$args.foreach{$_} -join " "})}
function Style
{
return [Element] @{
Tag = "style"
Text = "$args"
}
}
# Takes a hash table, casts it to and HTML document
# and then returns the resulting type.
#
function Html ([HTML] $doc) { return $doc }
См. также
сборка настраиваемы х ресурсов конфигурации состояния PowerShell