about_Classes_and_DSC
Descrição curta
Descreve como você pode usar classes para desenvolver no PowerShell com a Configuração de Estado Desejado (DSC).
Descrição longa
A partir do Windows PowerShell 5.0, o idioma foi adicionado para definir classes e outros tipos definidos pelo usuário usando sintaxe formal e semântica semelhantes a outras linguagens de programação orientadas a objetos. O objetivo é permitir que desenvolvedores e profissionais de TI adotem o PowerShell para uma gama mais ampla de casos de uso, simplificar o desenvolvimento de artefatos do PowerShell, como recursos de DSC, e acelerar a cobertura de superfícies de gerenciamento.
Cenários com suporte
Há suporte para os seguintes cenários:
- Defina recursos DSC e seus tipos associados usando a linguagem do PowerShell.
- Defina tipos personalizados no PowerShell usando constructos de programação familiares orientados a objetos, como classes, propriedades, métodos e herança.
- Depurar tipos usando o idioma do PowerShell.
- Gere e lide com exceções usando mecanismos formais e no nível certo.
Definir recursos de DSC com classes
Além das alterações de sintaxe, as principais diferenças entre um recurso DSC definido por classe e um provedor de recursos DSC de cmdlet são os seguintes itens:
- Um arquivo MOF (Formato de Objeto de Gerenciamento) não é necessário.
- Uma subpasta DSCResource na pasta do módulo não é necessária.
- Um arquivo de módulo do PowerShell pode conter várias classes de recursos DSC.
Criar um provedor de recursos DSC definido por classe
O exemplo a seguir é um provedor de recursos DSC definido pela classe que é salvo como um módulo, MyDSCResource.psm1. Você sempre deve incluir uma propriedade de chave em um provedor de recursos DSC definido por classe.
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
}
}
Criar um manifesto do módulo
Depois de criar o provedor de recursos DSC definido pela classe e salvá-lo como um módulo, crie um manifesto de módulo para o módulo. Para disponibilizar um recurso baseado em classe para o mecanismo DSC, você deve incluir uma instrução DscResourcesToExport
no arquivo de manifesto que instrui o módulo a exportar o recurso. Neste exemplo, o manifesto do módulo a seguir é salvo como 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 = ''
}
Implantar um provedor de recursos DSC
Implante o novo provedor de recursos DSC criando uma pasta MyDscResource em $pshome\Modules
ou $env:SystemDrive\ProgramFiles\WindowsPowerShell\Modules
.
Você não precisa criar uma subpasta DSCResource. Copie os arquivos de manifesto do módulo e do módulo (MyDscResource.psm1 e MyDscResource.psd1) para a pasta MyDscResource.
Nesse ponto, você cria e executa um script de configuração como faria com qualquer recurso DSC.
Criar um script de configuração de DSC
Depois de salvar os arquivos de classe e manifesto na estrutura de pastas, conforme descrito anteriormente, você pode criar uma configuração que usa o novo recurso. A configuração a seguir faz referência ao módulo MyDSCResource. Salve a configuração como um script, MyResource.ps1.
Para obter informações sobre como executar uma configuração de DSC, consulte Visão geral da configuração de estado desejado do Windows PowerShell.
Antes de executar a configuração, crie C:\test.txt
. A configuração verifica se o arquivo existe em c:\test\test.txt
. Se o arquivo não existir, a configuração copiará o arquivo de 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
Execute esse script como faria com qualquer script de configuração de DSC. Para iniciar a configuração, em um console do PowerShell com privilégios elevados, execute o seguinte:
PS C:\test> .\MyResource.ps1
Herança em classes do PowerShell
Declarar classes base para classes do PowerShell
Você pode declarar uma classe do PowerShell como um tipo base para outra classe do PowerShell, conforme mostrado no exemplo a seguir, na qual de frutas é um tipo base para apple.
class fruit
{
[int]sold() {return 100500}
}
class apple : fruit {}
[apple]::new().sold() # return 100500
Declarar interfaces implementadas para classes do PowerShell
Você pode declarar interfaces implementadas após tipos base ou imediatamente após dois-pontos (:
) se não houver nenhum tipo base especificado. Separe todos os nomes de tipo usando vírgulas. Isso é semelhante à sintaxe C#.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableTest : test, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Chamar construtores de classe base
Para chamar um construtor de classe base de uma subclasse, adicione a palavra-chave base
, conforme mostrado no exemplo a seguir:
class A {
[int]$a
A([int]$a)
{
$this.a = $a
}
}
class B : A
{
B() : base(103) {}
}
[B]::new().a # return 103
Se uma classe base tiver um construtor padrão (sem parâmetros), você poderá omitir uma chamada de construtor explícita, conforme mostrado.
class C : B
{
C([int]$c) {}
}
Chamar métodos de classe base
Você pode substituir os métodos existentes em subclasses. Para fazer a substituição, declare métodos usando o mesmo nome e assinatura.
class baseClass
{
[int]days() {return 100500}
}
class childClass1 : baseClass
{
[int]days () {return 200600}
}
[childClass1]::new().days() # return 200600
Para chamar métodos de classe base de implementações substituídas, converta para a classe base ([baseclass]$this)
na invocação.
class childClass2 : baseClass
{
[int]days()
{
return 3 * ([baseClass]$this).days()
}
}
[childClass2]::new().days() # return 301500
Todos os métodos do PowerShell são virtuais. Você pode ocultar métodos .NET não virtuais em uma subclasse usando a mesma sintaxe que você faz para uma substituição: declarar métodos com o mesmo nome e assinatura.
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
Limitações atuais com herança de classe
Uma limitação com a herança de classe é que não há sintaxe para declarar interfaces no PowerShell.
Definindo tipos personalizados no PowerShell
O Windows PowerShell 5.0 introduziu vários elementos de linguagem.
Palavra-chave de classe
Define uma nova classe.
A palavra-chave class
é um tipo verdadeiro do .NET Framework.
Os membros da classe são públicos.
class MyClass
{
}
Enumerações e palavras-chave
O suporte para a palavra-chave enum
foi adicionado e é uma alteração significativa. O delimitador enum
atualmente é uma nova linha. Uma solução alternativa para aqueles que já estão usando enum
é inserir uma e comercial (&
) antes da palavra. Limitações atuais: você não pode definir um enumerador em termos de si mesmo, mas pode inicializar enum
em termos de outra enum
, conforme mostrado no exemplo a seguir:
O tipo base não pode ser especificado no momento. O tipo base é sempre [int].
enum Color2
{
Yellow = [Color]::Blue
}
Um valor de enumerador deve ser uma constante de tempo de análise. O valor do enumerador não pode ser definido como o resultado de um comando invocado.
enum MyEnum
{
Enum1
Enum2
Enum3 = 42
Enum4 = [int]::MaxValue
}
Enum
dá suporte a operações aritméticas, conforme mostrado no exemplo a seguir:
enum SomeEnum { Max = 42 }
enum OtherEnum { Max = [SomeEnum]::Max + 1 }
Palavra-chave oculta
A palavra-chave hidden
, introduzida no Windows PowerShell 5.0, oculta os membros da classe dos resultados padrão Get-Member
. Especifique a propriedade oculta, conforme mostrado na seguinte linha:
hidden [type] $classmember = <value>
Os membros ocultos não são exibidos usando a conclusão da guia ou o IntelliSense, a menos que a conclusão ocorra na classe que define o membro oculto.
Um novo atributo, System.Management.Automation.HiddenAttribute, foi adicionado, para que o código C# possa ter a mesma semântica no PowerShell.
Para obter mais informações, consulte [about_Hidden[(/powershell/module/microsoft.powershell.core/about/about_hidden).
Import-DscResource
Import-DscResource
agora é uma verdadeira palavra-chave dinâmica. O PowerShell analisa o módulo raiz do módulo especificado, pesquisando classes que contêm o atributo DscResource.
Propriedades
Um novo campo, ImplementingAssembly
, foi adicionado ao ModuleInfo
. Se o script definir classes ou o assembly carregado para módulos binários ImplementingAssembly
será definido como o assembly dinâmico criado para um módulo de script. Ele não é definido quando ModuleType = Manifesto.
A reflexão sobre o campo ImplementingAssembly
descobre recursos em um módulo. Isso significa que você pode descobrir recursos gravados no PowerShell ou em outras linguagens gerenciadas.
Campos com inicializadores.
[int] $i = 5
Há suporte para estático e funciona como um atributo, semelhante às restrições de tipo, para que ele possa ser especificado em qualquer ordem.
static [int] $count = 0
Um tipo é opcional.
$s = "hello"
Todos os membros são públicos. As propriedades exigem uma nova linha ou ponto-e-vírgula. Se nenhum tipo de objeto for especificado, o tipo de propriedade será Object.
Construtores e instanciação
As classes do PowerShell podem ter construtores com o mesmo nome da classe. Construtores podem ser sobrecarregados. Há suporte para construtores estáticos.
As propriedades com expressões de inicialização são inicializadas antes de executar qualquer código em um construtor. As propriedades estáticas são inicializadas antes do corpo de um construtor estático e as propriedades da instância são inicializadas antes do corpo do construtor não estático. Atualmente, não há sintaxe para chamar um construtor de outro construtor, como a sintaxe C#: ": this()")
. A solução alternativa é definir um método Init comum.
Veja a seguir maneiras de instanciar classes:
Instanciando usando o construtor padrão. Observe que não há suporte para
New-Object
nesta versão.$a = [MyClass]::new()
Chamando um construtor com um parâmetro.
$b = [MyClass]::new(42)
Passando uma matriz para um construtor com vários parâmetros
$c = [MyClass]::new(@(42,43,44), "Hello")
Para esta versão, o nome do tipo só é visível lexicamente, o que significa que ele não está visível fora do módulo ou script que define a classe. As funções podem retornar instâncias de uma classe definida no PowerShell e as instâncias funcionam bem fora do módulo ou script.
O parâmetro Get-Member
estático lista construtores, para que você possa exibir sobrecargas como qualquer outro método. O desempenho dessa sintaxe também é consideravelmente mais rápido do que New-Object
.
O método pseudo estático chamado novo funciona com tipos .NET, conforme mostrado no exemplo a seguir. [hashtable]::new()
Agora você pode ver sobrecargas de construtor com Get-Member
ou conforme mostrado neste exemplo:
[hashtable]::new
OverloadDefinitions
-------------------
hashtable new()
hashtable new(int capacity)
hashtable new(int capacity, float loadFactor)
Métodos
Um método de classe do PowerShell é implementado como um ScriptBlock que tem apenas um bloco final. Todos os métodos são públicos. O exemplo a seguir mostra um exemplo de definição de um método chamado DoSomething.
class MyClass
{
DoSomething($x)
{
$this._doSomething($x) # method syntax
}
private _doSomething($a) {}
}
Invocação de método
Há suporte para métodos sobrecarregados. Os métodos sobrecarregados são nomeados da mesma forma que um método existente, mas diferenciados por seus valores especificados.
$b = [MyClass]::new()
$b.DoSomething(42)
Invocação
Consultede invocação do método
Atributos
Três novos atributos foram adicionados: DscResource
, DscResourceKey
e DscResourceMandatory
.
Tipos de retorno
O tipo de retorno é um contrato. O valor retornado é convertido no tipo esperado. Se nenhum tipo de retorno for especificado, o tipo de retorno será nulo. Não há streaming de objetos e objetos não podem ser gravados no pipeline intencionalmente ou por acidente.
Escopo lexical de variáveis
A seguir, mostra um exemplo de como o escopo léxico funciona nesta versão.
$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
Exemplo: criar classes personalizadas
O exemplo a seguir cria várias classes novas e personalizadas para implementar uma linguagem HTML Dynamic Stylesheet (DSL). O exemplo adiciona funções auxiliares para criar tipos de elemento específicos como parte da classe de elemento, como estilos de título e tabelas, porque os tipos não podem ser usados fora do escopo de um módulo.
# 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 }
Consulte também
criar recursos de configuração de estado desejados personalizados do PowerShell