开始创作基于类的 DSC 资源来管理配置文件。 完成本教程后,你将在可用于进一步学习和自定义的模块中提供基于功能类的 DSC 资源。
在本教程中,你将了解如何执行以下操作:
- 搭建 DSC 资源模块基架
- 添加基于类的 DSC 资源
- 定义 DSC 资源属性
- 实现 DSC 资源方法
- 导出模块清单中的 DSC 资源
- 手动测试 DSC 资源
注意
本教程中的示例输出与 Windows 计算机上的 PowerShell 7.2 匹配。 本教程适用于 Linux 或 macOS 计算机上的 Windows PowerShell 和 PowerShell。 只有输出特定于在 Windows 计算机上在 PowerShell 中运行命令。
先决条件
- PowerShell 或 Windows PowerShell 5.1
- 使用 PowerShell 扩展的 VS Code
1 - 搭建 DSC 资源模块的基架
必须在 PowerShell 模块中定义 DSC 资源。
创建模块文件夹
创建名为 ExampleResources
的新文件夹。 此文件夹用作本教程中模块和所有代码的根文件夹。
New-Item -Path './ExampleResources' -ItemType Directory
Directory: C:\code\dsc
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 9/8/2022 12:54 PM ExampleResources
使用 VS Code 创作模块
在 VS Code 中打开 ExampleResources
文件夹。 在 VS Code 中打开集成终端。 确保终端正在运行 PowerShell 或 Windows PowerShell。
重要
对于本教程的其余部分,请在模块文件夹根目录中的集成终端中运行指定的命令。 这是 VS Code 中的默认工作目录。
创建模块文件
使用 New-ModuleManifest
cmdlet 创建模块清单。 用作./ExampleResources.psd1
路径。 将 RootModule 指定为 ExampleResources.psm1
, 将 DscResourcesToExport 指定为 Tailspin
。
$ModuleSettings = @{
RootModule = 'ExampleResources.psm1'
DscResourcesToExport = 'Tailspin'
}
New-ModuleManifest -Path ./ExampleResources.psd1 @ModuleSettings
Get-Module -ListAvailable -Name ./ExampleResources.psd1 | Format-List
Name : ExampleResources
Path : C:\code\dsc\ExampleResources\ExampleResources.psd1
Description :
ModuleType : Script
Version : 0.0.1
PreRelease :
NestedModules : {}
ExportedFunctions :
ExportedCmdlets :
ExportedVariables :
ExportedAliases :
将根模块文件创建为 ExampleResources.psm1
。
New-Item -Path ./ExampleResources.psm1
Directory: C:\code\dsc\ExampleResources
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/8/2022 1:57 PM 0 ExampleResources.psm1
创建名为 的 Helpers.ps1
脚本文件。
New-Item -Path ./Helpers.ps1
Directory: C:\code\dsc\ExampleResources
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/8/2022 1:58 PM 0 Helpers.ps1
在 VS Code 中打开 Helpers.ps1
。 添加以下行,如果使用的是 Windows,:
并且使用的是 Linux 或 macOS,请将 替换为 。<Separator>
;
$env:PSModulePath += "<Separator>$pwd"
在 VS Code 中打开 ExampleResources.psm1
。 模块现已搭建基架,可供你创作 DSC 资源。
2 - 添加基于类的 DSC 资源
为了定义基于类的 DSC 资源,我们在模块文件中编写一个 PowerShell 类,并向其添加 DscResource 属性。
定义类
在 ExampleResources.psm1
中添加以下代码:
[DscResource()]
class Tailspin {
}
此代码将作为基于类的 DSC 资源添加到 Tailspin
ExampleResources 模块。
将鼠标悬停在上 [DscResource()]
并读取警告。
将鼠标悬停在 DSCResource 属性上会显示四条警告。 1. DSC 资源“Tailspin”缺少返回“[void]”且不接受任何参数的“Set () ”方法。 2. DSC 资源“Tailspin”缺少返回“[Tailspin]”且不接受任何参数的“Get () ”方法。 3. DSC 资源“Tailspin”缺少返回“[bool]”且不接受任何参数的“Test () ”方法。 4. DSC 资源“Tailspin”必须使用语法“[DscProperty (Key) ]” (至少一个键属性。)
这些警告列出了类为有效 DSC 资源的要求。
最小实现所需的方法
将 、 Test()
和 Set()
方法的Get()
最小实现添加到 类。
class Tailspin {
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
return $CurrentState
}
[bool] Test() {
return $true
}
[void] Set() {}
}
添加方法后, DscResource 属性仅警告类没有 Key 属性。
3 - 定义 DSC 资源属性
应在方法之前定义 DSC 资源的属性。 属性定义 DSC 资源的可管理设置。 它们在 方法中使用。
了解 TSToy 应用程序
在定义 DSC 资源的属性之前,需要了解要管理的设置。
在本教程中,我们将定义一个 DSC 资源,用于通过其配置文件管理虚构 TSToy 应用程序的设置。 TSToy 是一个在用户和计算机级别具有配置的应用程序。 DSC 资源应能够配置任一文件。
DSC 资源应使用户能够配置:
- 他们正在管理的配置的范围,或
Machine
User
- 配置文件是否应存在
- TSToy 是否应自动更新
- TSToy 应检查更新的频率(介于 1 到 90 天之间)
添加 ConfigurationScope 属性
若要管理 Machine
或 User
配置文件,需要定义 DSC 资源的 属性。 若要在资源中定义 $ConfigurationScope
属性,请在 方法前的 类中添加以下代码:
[DscProperty(Key)] [TailspinScope]
$ConfigurationScope
此代码将 $ConfigurationScope
定义为 DSC 资源的 Key 属性。
Key 属性用于唯一标识 DSC 资源的实例。 添加此属性满足 DscResource 属性在搭建类基架时警告的要求之一。
它还指定 $ConfigurationScope
的类型为 TailspinScope。 若要定义 TailspinScope 类型,请在 中的ExampleResources.psm1
类定义之后添加以下 TailspinScope 枚举:
enum TailspinScope {
Machine
User
}
此枚举使 Machine
和 User
DSC 资源的 属性的唯一有效值 $ConfigurationScope
。
添加 Ensure 属性
最佳做法是定义属性 $Ensure
来控制是否存在 DSC 资源的实例。 属性通常有两个 $Ensure
有效值, Absent
即 Present
。
- 如果
$Ensure
指定为Present
,则 DSC 资源将创建项(如果不存在)。 - 如果
$Ensure
为Absent
,则 DSC 资源会删除该项(如果存在)。
对于 Tailspin DSC 资源,要创建或删除的项是指定 $ConfigurationScope
的配置文件。
将 TailspinEnsure 定义为 TailspinScope 后面的枚举。 它应具有 值 Absent
和 Present
。
enum TailspinEnsure {
Absent
Present
}
接下来,在 $Ensure
类中将 属性添加到 属性的 $ConfigurationScope
后面。 它应具有空 的 DscProperty 属性,其类型应为 TailspinEnsure。 它应默认为 Present
。
[DscProperty()] [TailspinEnsure]
$Ensure = [TailspinEnsure]::Present
添加 UpdateAutomatically 属性
若要管理自动更新,请在 $UpdateAutomatically
类中定义 属性后的 $Ensure
属性。 其 DscProperty 属性应指示它是必需的,其类型应为 布尔值。
[DscProperty(Mandatory)] [bool]
$UpdateAutomatically
添加 UpdateFrequency 属性
若要管理 TSToy 应检查更新的频率,请在 $UpdateFrequency
类中的 属性后面 $UpdateAutomatically
添加 属性。 它应具有空 的 DscProperty 属性,其类型应为 int。使用 ValidateRange 属性将 的有效 $UpdateFrequency
值限制在 1 到 90 之间。
[DscProperty()] [int] [ValidateRange(1, 90)]
$UpdateFrequency
添加隐藏的缓存属性
接下来,添加两个用于缓存资源的当前状态的隐藏属性: $CachedCurrentState
和 $CachedData
。 将 的类型 $CachedCurrentState
设置为 Tailspin,与 方法的 类和返回类型 Get()
相同。 将 类型 $CachedData
设置为 PSCustomObject。 为两个属性添加 关键字前缀 hidden
。 不要为两者指定 DscProperty 属性。
hidden [Tailspin] $CachedCurrentState
hidden [PSCustomObject] $CachedData
这些隐藏属性将用在稍后定义的 和 Set()
方法中Get()
。
查看模块文件
此时, ExampleResources.psm1
应定义:
- 具有属性
$ConfigurationScope
、$Ensure
、$UpdateAutomatically
和 的 Tailspin 类$UpdateFrequency
-
TailspinScope 枚举,其值为
Machine
和User
-
TailspinEnsure 枚举,其值为
Present
和Absent
- 、
Test()
和Set()
方法的Get()
最小实现。
[DscResource()]
class Tailspin {
[DscProperty(Key)] [TailspinScope]
$ConfigurationScope
[DscProperty()] [TailspinEnsure]
$Ensure = [TailspinEnsure]::Present
[DscProperty(Mandatory)] [bool]
$UpdateAutomatically
[DscProperty()] [int] [ValidateRange(1,90)]
$UpdateFrequency
hidden [Tailspin] $CachedCurrentState
hidden [PSCustomObject] $CachedData
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
return $CurrentState
}
[bool] Test() {
$InDesiredState = $true
return $InDesiredState
}
[void] Set() {}
}
enum TailspinScope {
Machine
User
}
enum TailspinEnsure {
Absent
Present
}
现在,DSC 资源满足要求,可以使用 Get-DscResource
来查看它。 在 VS Code 中,打开新的 PowerShell 终端。
. ./Helpers.ps1
Get-DscResource -Name Tailspin -Module ExampleResources | Format-List
Get-DscResource -Name Tailspin -Module ExampleResources -Syntax
ImplementationDetail : ClassBased
ResourceType : Tailspin
Name : Tailspin
FriendlyName :
Module : ExampleResources
ModuleName : ExampleResources
Version : 0.0.1
Path : C:\code\dsc\ExampleResources\ExampleResources.psd1
ParentPath : C:\code\dsc\ExampleResources
ImplementedAs : PowerShell
CompanyName : Unknown
Properties : {ConfigurationScope, UpdateAutomatically, DependsOn, Ensure…}
Tailspin [String] #ResourceName
{
ConfigurationScope = [string]{ Machine | User }
UpdateAutomatically = [bool]
[DependsOn = [string[]]]
[Ensure = [string]{ Absent | Present }]
[PsDscRunAsCredential = [PSCredential]]
[UpdateFrequency = [Int32]]
}
4 - 实现 DSC 资源方法
DSC 资源的方法定义如何检索 DSC 资源的当前状态,根据所需状态对其进行验证,并强制实施所需状态。
Get 方法
方法 Get()
检索 DSC 资源的当前状态。 它用于手动检查 DSC 资源,并由 Test()
方法调用。
方法 Get()
没有参数,并返回 类的实例作为其输出。
Tailspin
对于 DSC 资源,最小实现如下所示:
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
return $CurrentState
}
此实现执行的唯一操作是创建 Tailspin 类的实例并返回它。 可以使用 调用 方法 Invoke-DscResource
以查看此行为。
Invoke-DscResource -Name Tailspin -Module ExampleResources -Method Get -Property @{
ConfigurationScope = 'User'
UpdateAutomatically = $true
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
Machine Present False 0
返回对象的属性全部设置为其默认值。 的值 $ConfigurationScope
应始终为用户提供的值。 若要使 Get()
该方法有用,它必须返回 DSC 资源的实际状态。
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
$CurrentState.ConfigurationScope = $this.ConfigurationScope
$this.CachedCurrentState = $CurrentState
return $CurrentState
}
变量 $this
引用 DSC 资源的工作实例。 现在,如果再次使用 Invoke-DscResource
, $ConfigurationScope
具有正确的值。
Invoke-DscResource -Name Tailspin -Module ExampleResources -Method Get -Property @{
ConfigurationScope = 'User'
UpdateAutomatically = $true
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present False 0
接下来,DSC 资源需要确定配置文件是否存在。 如果是, $Ensure
应为 Present
。 否则, $Ensure
应为 Absent
。
TSToy 的配置文件的位置取决于操作系统和配置范围:
- 对于 Windows 计算机:
- 配置文件
Machine
为%PROGRAMDATA%\TailSpinToys\tstoy\tstoy.config.json
- 配置文件
User
为%APPDATA%\TailSpinToys\tstoy\tstoy.config.json
- 配置文件
- 对于 Linux 计算机:
- 配置文件
Machine
为/etc/xdg/TailSpinToys/tstoy/tstoy.config.json
- 配置文件
User
为~/.config/TailSpinToys/tstoy/tstoy.config.json
- 配置文件
- 对于 macOS 计算机:
- 配置文件
Machine
为/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json
- 配置文件
User
为~/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json
- 配置文件
若要处理这些路径,需要创建帮助程序方法 GetConfigurationFile()
。
[string] GetConfigurationFile() {
$FilePaths = @{
Linux = @{
Machine = '/etc/xdg/TailSpinToys/tstoy/tstoy.config.json'
User = '~/.config/TailSpinToys/tstoy/tstoy.config.json'
}
MacOS = @{
Machine = '/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json'
User = '~/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json'
}
Windows = @{
Machine = "$env:ProgramData\TailSpinToys\tstoy\tstoy.config.json"
User = "$env:APPDATA\TailSpinToys\tstoy\tstoy.config.json"
}
}
$Scope = $this.ConfigurationScope.ToString()
if ($Global:PSVersionTable.PSVersion.Major -lt 6 -or $Global:IsWindows) {
return $FilePaths.Windows.$Scope
} elseif ($Global:IsLinux) {
return $FilePaths.Linux.$Scope
} else {
return $FilePaths.MacOS.$Scope
}
}
若要测试此新方法,请执行 using
语句,将 ExampleResources 模块的类和枚举加载到当前会话中。
using module ./ExampleResources.psd1
$Example = [Tailspin]::new()
$Example
$Example.GetConfigurationFile()
$Example.ConfigurationScope = 'User'
$Example.GetConfigurationFile()
Ensure ConfigurationScope UpdateAutomatically UpdateFrequency
------- ------------------ ------------------- ---------------
Present Machine False 0
C:\ProgramData\TailSpinToys\tstoy\tstoy.config.json
C:\Users\mikey\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json
在 VS Code 中打开 Helpers.ps1
。 将配置文件的路径复制并粘贴到脚本中,并将其 $TSToyMachinePath
分配给 和 $TSToyUserPath
。 该文件应如下所示:
$env:PSModulePath += "<separator>$pwd"
$TSToyMachinePath = '<machine configuration file path>'
$TSToyUserPath = '<user configuration file path>'
退出 VS Code 中的终端并打开新的终端。 点源 Helpers.ps1
。
. ./Helpers.ps1
现在可以编写方法的 Get()
其余部分。
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
$CurrentState.ConfigurationScope = $this.ConfigurationScope
$FilePath = $this.GetConfigurationFile()
if (!(Test-Path -Path $FilePath)) {
$CurrentState.Ensure = [TailspinEnsure]::Absent
return $CurrentState
}
$Data = Get-Content -Raw -Path $FilePath |
ConvertFrom-Json -ErrorAction Stop
$this.CachedData = $Data
if ($null -ne $Data.Updates.Automatic) {
$CurrentState.UpdateAutomatically = $Data.Updates.Automatic
}
if ($null -ne $Data.Updates.CheckFrequency) {
$CurrentState.UpdateFrequency = $Data.Updates.CheckFrequency
}
$this.CachedCurrentState = $CurrentState
return $CurrentState
}
设置 $ConfigurationScope
并确定配置文件的路径后, 方法检查文件是否存在。 如果不存在,只需将 设置为 $Ensure
Absent
并返回结果。
如果文件确实存在, 方法需要从 JSON 转换内容,以创建配置的当前状态。 接下来, 方法在将键分配给当前状态的属性之前检查它们是否具有任何值。 如果未指定它们,DSC 资源必须将其视为未设置且处于默认状态。
此时,DSC 资源缓存数据。 缓存数据可以在开发期间检查数据,并在实现 Set()
方法时非常有用。
可以在本地验证此行为。
$GetParameters = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Method = 'Get'
Property = @{
ConfigurationScope = 'User'
}
}
Invoke-DscResource @GetParameters
New-Item -Path $UserPath -Force
Invoke-DscResource @GetParameters
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Absent False 0
Directory: C:\Users\mikey\AppData\Roaming\TailSpinToys\tstoy
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/15/2022 3:43 PM 0 tstoy.config.json
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present False 0
在 User
VS Code 中打开范围配置文件。
code $UserPath
将此 JSON 配置复制到 文件中并保存。
{
"unmanaged_key": true,
"updates": {
"automatic": true,
"checkFrequency": 30
}
}
再次调用 Invoke-DscResource
并查看结果中反映的值。
Invoke-DscResource @GetParameters
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
方法 Get()
现在返回有关 DSC 资源的当前状态的准确信息。
Test 方法
实现 方法后 Get()
,可以验证当前状态是否符合所需状态。
最小 Test()
实现方法始终返回 $true
。
[bool] Test() {
return $true
}
你可以通过运行 Invoke-DscResource
来进行验证。
$SharedParameters = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $false
}
}
Invoke-DscResource -Method Get @SharedParameters
Invoke-DscResource -Method Test @SharedParameters
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
InDesiredState
--------------
True
需要使 Test()
方法准确反映 DSC 资源是否处于所需状态。 方法 Test()
应始终调用 方法, Get()
使当前状态与所需状态进行比较。 然后检查属性 $Ensure
是否正确。 如果不是,请立即返回 $false
。 如果 $Ensure
属性处于所需状态,则无需进一步检查。
[bool] Test() {
$CurrentState = $this.Get()
if ($CurrentState.Ensure -ne $this.Ensure) {
return $false
}
return $true
}
现在可以验证更新后的行为。
$TestParameters = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $false
Ensure = 'Absent'
}
}
Invoke-DscResource -Method Test @TestParameters
$TestParameters.Property.Ensure = 'Present'
Invoke-DscResource -Method Test @TestParameters
InDesiredState
--------------
False
InDesiredState
--------------
True
接下来,检查 的值 $Ensure
是否为 Absent
。 如果配置文件不存在且不应存在,则没有理由检查其余属性。
[bool] Test() {
$CurrentState = $this.Get()
if ($CurrentState.Ensure -ne $this.Ensure) {
return $false
}
if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
return $true
}
return $true
}
接下来, 方法需要比较管理 TSToy 更新行为的属性的当前状态。 首先,检查属性是否 $UpdateAutomatically
处于正确的状态。 如果不是,则返回 $false
。
[bool] Test() {
$CurrentState = $this.Get()
if ($CurrentState.Ensure -ne $this.Ensure) {
return $false
}
if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
return $true
}
if ($CurrentState.UpdateAutomatically -ne $this.UpdateAutomatically) {
return $false
}
return $true
}
若要比较 $UpdateFrequency
,需要确定用户是否指定了 值。 由于 $UpdateFrequency
初始化为 0
,并且 属性的 ValidateRange 属性指定它必须介于 和 90
之间1
,我们知道 值 指示0
未指定属性。
有了这些信息, Test()
方法应:
- 如果用户未指定,则返回
$true
$UpdateFrequency
- 如果用户指定
$UpdateFrequency
了 ,并且系统的值不等于用户指定的值,则返回$false
- 如果未满足上述两个条件,则返回
$true
[bool] Test() {
$CurrentState = $this.Get()
if ($CurrentState.Ensure -ne $this.Ensure) {
return $false
}
if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
return $true
}
if ($CurrentState.UpdateAutomatically -ne $this.UpdateAutomatically) {
return $false
}
if ($this.UpdateFrequency -eq 0) {
return $true
}
if ($CurrentState.UpdateFrequency -ne $this.UpdateFrequency) {
return $false
}
return $true
}
现在, Test()
方法使用以下操作顺序:
- 检索 TSToy 配置的当前状态。
- 如果配置在不应存在时存在,则返回 ;如果配置应不存在,则返回
$false
。 - 如果配置不存在且不应存在,则返回
$true
。 - 如果配置的自动更新设置与所需设置不匹配,则返回
$false
。 - 如果用户未为更新频率设置指定值,则返回
$true
。 - 如果用户为更新频率设置指定的值与配置的设置不匹配,则返回
$false
。 - 如果未满足上述任何条件,则返回
$true
。
可以在本地验证 Test()
方法:
$SharedParameters = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
Ensure = 'Present'
UpdateAutomatically = $false
}
}
Invoke-DscResource -Method Get @SharedParameters
Invoke-DscResource -Method Test @SharedParameters
$SharedParameters.Property.UpdateAutomatically = $true
Invoke-DscResource -Method Test @SharedParameters
$SharedParameters.Property.UpdateFrequency = 1
Invoke-DscResource -Method Test @SharedParameters
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
InDesiredState
--------------
False
InDesiredState
--------------
True
InDesiredState
--------------
False
使用此代码, Test()
方法能够准确地确定配置文件是否处于所需状态。
Set 方法
Get()
既然 和 Test()
方法可靠工作,可以定义 Set()
方法以实际强制实施所需状态。
在最小实现中 Set()
, 方法不执行任何作用。
[void] Set() {}
首先, Set()
需要确定是否需要创建、更新或删除 DSC 资源。
[void] Set() {
if ($this.Test()) {
return
}
$CurrentState = $this.CachedCurrentState
$IsAbsent = $CurrentState.Ensure -eq [TailspinEnsure]::Absent
$ShouldBeAbsent = $this.Ensure -eq [TailspinEnsure]::Absent
if ($IsAbsent) {
# Create
} elseif ($ShouldBeAbsent) {
# Remove
} else {
# Update
}
}
Set()
首先调用 Test()
方法以确定是否需要执行任何操作。 某些工具(例如 Azure Automanage 的计算机配置功能)确保 Set()
方法仅在 方法之后 Test()
调用。 但是,使用 Invoke-DscResource
cmdlet 时没有这样的保证。
Test()
由于 方法调用 Get()
缓存当前状态的 ,因此 DSC 资源可以访问缓存的当前状态,而无需再次调用 Get()
方法。
接下来,DSC 资源需要区分配置文件的创建、删除和更新行为。 如果配置文件不存在,我们知道应该创建它。 如果配置文件确实存在且不应存在,则我们知道需要将其删除。 如果配置文件确实存在并且应该存在,我们知道需要更新它。
创建三个新方法来处理这些操作,并根据需要在 方法中 Set()
调用它们。
所有三个的返回类型都应为 void。
[void] Set() {
if ($this.Test()) {
return
}
$CurrentState = $this.CachedCurrentState
$IsAbsent = $CurrentState.Ensure -eq [TailspinEnsure]::Absent
$ShouldBeAbsent = $this.Ensure -eq [TailspinEnsure]::Absent
if ($IsAbsent) {
$this.Create()
} elseif ($ShouldBeAbsent) {
$this.Remove()
} else {
$this.Update()
}
}
[void] Create() {}
[void] Remove() {}
[void] Update() {}
此外,创建一个名为 ToConfigJson()
的新方法。 其返回类型应为 字符串。 此方法将 DSC 资源转换为配置文件所需的 JSON。 可以从以下最小实现开始:
[string] ToConfigJson() {
$config = @{}
return ($config | ConvertTo-Json)
}
ToConfigJson 方法
最小实现将空 JSON 对象作为字符串返回。 若要使其有用,它需要返回 TSToy 配置文件中设置的实际 JSON 表示形式。
首先,使用强制自动更新设置预填充 $config
哈希表, updates
方法是将键的值添加为 哈希表。 哈希表应具有 automatic
键。 将类的 $UpdateAutomatically
属性的值分配给 automatic
键。
[string] ToConfigJson() {
$config = @{
updates = @{
automatic = $this.UpdateAutomatically
}
}
return ($config | ConvertTo-Json)
}
此代码将 TSToy 设置的 DSC 资源表示形式转换为 TSToy 的配置文件所需的结构。
接下来,方法需要检查 类是否缓存了现有配置文件中的数据。 缓存的数据允许 DSC 资源管理定义的设置,而不会覆盖或删除非托管设置。
[string] ToConfigJson() {
$config = @{
updates = @{
automatic = $this.UpdateAutomatically
}
}
if ($this.CachedData) {
# Copy unmanaged settings without changing the cached values
$this.CachedData |
Get-Member -MemberType NoteProperty |
Where-Object -Property Name -NE -Value 'updates' |
ForEach-Object -Process {
$setting = $_.Name
$config.$setting = $this.CachedData.$setting
}
# Add the checkFrequency to the hashtable if it is set in the cache
if ($frequency = $this.CachedData.updates.checkFrequency) {
$config.updates.checkFrequency = $frequency
}
}
# If the user specified an UpdateFrequency, use that value
if ($this.UpdateFrequency -ne 0) {
$config.updates.checkFrequency = $this.UpdateFrequency
}
return ($config | ConvertTo-Json)
}
如果类缓存了现有配置中的设置,则它:
检查缓存数据的属性,查找 DSC 资源未管理的任何属性。 如果找到任何属性,方法会将这些非托管属性插入到
$config
哈希表中。由于 DSC 资源仅管理更新设置,因此插入除 以外的
updates
每个设置。检查是否
checkFrequency
设置了 中的updates
设置。 如果设置了该值,方法会将此值插入到$config
哈希表中。如果用户未指定属性,
$UpdateFrequency
则此操作允许 DSC 资源忽略该属性。最后,方法需要检查用户是否指定了 属性,
$UpdateFrequency
并在指定时将其插入哈希$config
表中。
使用此代码,方法 ToConfigJson()
:
- 返回 TSToy 应用程序在其配置文件中所需的所需状态的准确 JSON 表示形式
- 遵循 DSC 资源未显式管理的任何 TSToy 设置
- 如果用户未指定,则遵循 TSToy 更新频率的现有值,包括在配置文件中保留它未定义
若要测试此新方法,请关闭 VS Code 终端并打开一个新方法。
using
执行 语句将 ExampleResources 模块的类和枚举加载到当前会话中,并点源脚本helpers.ps1
。
using module ./ExampleResources.psd1
. ./Helpers.ps1
$Example = [Tailspin]::new()
Get-Content -Path $UserPath
$Example.ConfigurationScope = 'User'
$Example.ToConfigJson()
Get()
在调用 方法之前,ToJsonConfig 方法输出中的唯一值是 属性的$UpdateAutomatically
转换值。
{
"unmanaged_key": true,
"updates": {
"automatic": true,
"checkFrequency": 30
}
}
{
"updates": {
"automatic": false
}
}
$Example.Get()
$Example.ToConfigJson()
调用 Get()
后,输出包括非托管顶级键 unmanaged_key
。 它还包括 配置文件中的现有设置, $UpdateFrequency
因为它未在 DSC 资源上显式设置。
Ensure ConfigurationScope UpdateAutomatically UpdateFrequency
------ ------------------ ------------------- ---------------
Present User True 30
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": false
}
}
$Example.UpdateFrequency = 7
$Example.ToConfigJson()
设置 后 $UpdateFrequency
,输出将反映指定的值。
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 7,
"automatic": false
}
}
Create 方法
若要实现 Create()
方法,我们需要将 DSC 资源的用户指定属性转换为 TSToy 在其配置文件中预期的 JSON,并将其写入该文件。
[void] Create() {
$ErrorActionPreference = 'Stop'
$Json = $this.ToConfigJson()
$FilePath = $this.GetConfigurationFile()
$FolderPath = Split-Path -Path $FilePath
if (!(Test-Path -Path $FolderPath)) {
New-Item -Path $FolderPath -ItemType Directory -Force
}
Set-Content -Path $FilePath -Value $Json -Encoding utf8 -Force
}
方法使用 ToConfigJson()
方法获取配置文件的 JSON。 它会检查配置文件的文件夹是否存在,并在必要时创建它。 最后,它会创建配置文件,并将 JSON 写入其中。
Remove 方法
方法 Remove()
具有最简单的行为。 如果配置文件存在,请将其删除。
[void] Remove() {
Remove-Item -Path $this.GetConfigurationFile() -Force -ErrorAction Stop
}
Update 方法
方法 Update()
的实现类似于 Create 方法。 它需要将 DSC 资源的用户指定属性转换为 TSToy 在其配置文件中预期的 JSON,并替换该文件中的设置。
[void] Update() {
$ErrorActionPreference = 'Stop'
$Json = $this.ToConfigJson()
$FilePath = $this.GetConfigurationFile()
Set-Content -Path $FilePath -Value $Json -Encoding utf8 -Force
}
5 - 手动测试 DSC 资源
完全实现 DSC 资源后,现在可以测试其行为。
在测试之前,请关闭 VS Code 终端并打开一个新终端。 点源 Helpers.ps1
脚本。
对于每个测试方案,创建 $DesiredState
包含共享参数的哈希表,并按以下顺序调用方法:
-
Get()
,用于检索 DSC 资源的初始状态 -
Test()
,查看 DSC 资源是否认为它处于所需状态 -
Set()
,以强制实施所需状态 -
Test()
,查看 DSC 资源是否认为它已正确设置 -
Get()
,确认 DSC 资源的最终状态
场景:TSToy 不应在用户范围内自动更新
在这种情况下,需要将用户范围中的现有配置配置为不自动更新。 所有其他设置应保持不变。
. ./Helpers.ps1
$DesiredState = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $false
Ensure = 'Present'
}
}
Get-Content -Path $UserPath
Invoke-DscResource @DesiredState -Method Get
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Set
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Get
Get-Content -Path $UserPath
{
"unmanaged_key": true,
"updates": {
"automatic": true,
"checkFrequency": 30
}
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
InDesiredState
--------------
False
RebootRequired
--------------
False
InDesiredState
--------------
True
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present False 30
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": false
}
}
场景:Tailspin 应根据用户范围中的任何计划自动更新
在这种情况下,需要将用户范围中的现有配置配置为自动更新。 所有其他设置应保持不变。
. ./Helpers.ps1
$DesiredState = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $true
Ensure = 'Present'
}
}
Get-Content -Path $UserPath
Invoke-DscResource @DesiredState -Method Get
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Set
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Get
Get-Content -Path $UserPath
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": false
}
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present False 30
InDesiredState
--------------
False
RebootRequired
--------------
False
InDesiredState
--------------
True
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": true
}
}
场景:TSToy 应在用户范围内每天自动更新
在此方案中,需要将用户范围中的现有配置配置为每天自动更新。 所有其他设置应保持不变。
. ./Helpers.ps1
$DesiredState = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $true
UpdateFrequency = 1
Ensure = 'Present'
}
}
Get-Content -Path $UserPath
Invoke-DscResource @DesiredState -Method Get
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Set
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Get
Get-Content -Path $UserPath
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": true
}
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
InDesiredState
--------------
False
RebootRequired
--------------
False
InDesiredState
--------------
True
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 1
{
"unmanaged_key": true,
"updates": {
"automatic": true,
"checkFrequency": 1
}
}
场景:TSToy 不应具有用户范围配置
在此方案中,用户范围内 TSToy 的配置文件不应存在。 如果存在,DSC 资源应删除该文件。
. ./Helpers.ps1
$DesiredState = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'User'
UpdateAutomatically = $true
Ensure = 'Absent'
}
}
Get-Content -Path $UserPath
Invoke-DscResource @DesiredState -Method Get
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Set
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Get
Test-Path -Path $UserPath
{
"unmanaged_key": true,
"updates": {
"checkFrequency": 30,
"automatic": true
}
}
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Present True 30
InDesiredState
--------------
False
RebootRequired
--------------
False
InDesiredState
--------------
True
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
User Absent False 0
False
场景:TSToy 应在计算机范围内每周自动更新
在此方案中,计算机范围中没有定义的配置。 需要将计算机范围配置为每天自动更新。 DSC 资源应根据需要创建文件和任何父文件夹。
. ./Helpers.ps1
$DesiredState = @{
Name = 'Tailspin'
Module = 'ExampleResources'
Property = @{
ConfigurationScope = 'Machine'
UpdateAutomatically = $true
Ensure = 'Present'
}
}
Test-Path -Path $MachinePath, (Split-Path -Path $MachinePath)
Invoke-DscResource @DesiredState -Method Get
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Set
Invoke-DscResource @DesiredState -Method Test
Invoke-DscResource @DesiredState -Method Get
Get-Content -Path $MachinePath
False
False
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
Machine Absent False 0
InDesiredState
--------------
False
RebootRequired
--------------
False
InDesiredState
--------------
True
ConfigurationScope Ensure UpdateAutomatically UpdateFrequency
------------------ ------ ------------------- ---------------
Machine Present True 0
{
"updates": {
"automatic": true
}
}
审阅
本教程介绍以下操作:
- 搭建了 PowerShell 模块并实现了基于类的
Tailspin
DSC 资源 - 定义了 DSC 资源的属性,以通过验证这些属性来管理计算机和用户范围内的 TSToy 应用程序的更新行为
- 为
$Ensure
和$ConfigurationScope
属性实现了枚举 -
GetConfigurationFile()
实现了帮助程序方法,以跨平台可靠地发现 TSToy 的应用程序配置在计算机和用户范围内的位置 -
Get()
实现了 方法以检索 DSC 资源的当前状态,并缓存它以便在 和Set()
方法中使用Test()
- 实现了 方法,
Test()
以根据所需状态验证特定范围内 TSToy 的更新行为的当前状态 - 实现了 ToConfigJson 方法,以将 DSC 资源的所需状态转换为 TSToy 应用程序为其配置文件所需的 JSON 对象,并遵循非托管设置
-
Set()
实现了 方法和 Create、Remove 和 Update 帮助程序方法,以幂等方式在特定范围内强制实施 TSToy 更新行为的所需状态,确保 DSC 资源不会产生不良的副作用 - 手动测试 DSC 资源的常见使用方案
在实现结束时,模块定义如下所示:
[DscResource()]
class Tailspin {
[DscProperty(Key)] [TailspinScope]
$ConfigurationScope
[DscProperty()] [TailspinEnsure]
$Ensure = [TailspinEnsure]::Present
[DscProperty(Mandatory)] [bool]
$UpdateAutomatically
[DscProperty()] [int] [ValidateRange(1, 90)]
$UpdateFrequency
hidden [Tailspin] $CachedCurrentState
hidden [PSCustomObject] $CachedData
[Tailspin] Get() {
$CurrentState = [Tailspin]::new()
$CurrentState.ConfigurationScope = $this.ConfigurationScope
$FilePath = $this.GetConfigurationFile()
if (!(Test-Path -Path $FilePath)) {
$CurrentState.Ensure = [TailspinEnsure]::Absent
$this.CachedCurrentState = $CurrentState
return $CurrentState
}
$CurrentState.Ensure = [TailspinEnsure]::Present
$Data = Get-Content -Raw -Path $FilePath |
ConvertFrom-Json -ErrorAction Stop
$this.CachedData = $Data
if ($null -ne $Data.Updates.Automatic) {
$CurrentState.UpdateAutomatically = $Data.Updates.Automatic
}
if ($null -ne $Data.Updates.CheckFrequency) {
$CurrentState.UpdateFrequency = $Data.Updates.CheckFrequency
}
$this.CachedCurrentState = $CurrentState
return $CurrentState
}
[bool] Test() {
$CurrentState = $this.Get()
if ($CurrentState.Ensure -ne $this.Ensure) {
return $false
}
if ($CurrentState.UpdateAutomatically -ne $this.UpdateAutomatically) {
return $false
}
if ($this.UpdateFrequency -eq 0) {
return $true
}
if ($CurrentState.UpdateFrequency -ne $this.UpdateFrequency) {
return $false
}
return $true
}
[void] Set() {
if ($this.Test()) {
return
}
$CurrentState = $this.CachedCurrentState
$IsAbsent = $CurrentState.Ensure -eq [TailspinEnsure]::Absent
$ShouldBeAbsent = $this.Ensure -eq [TailspinEnsure]::Absent
if ($IsAbsent) {
$this.Create()
} elseif ($ShouldBeAbsent) {
$this.Remove()
} else {
$this.Update()
}
}
[string] GetConfigurationFile() {
$FilePaths = @{
Linux = @{
Machine = '/etc/xdg/TailSpinToys/tstoy/tstoy.config.json'
User = '~/.config/TailSpinToys/tstoy/tstoy.config.json'
}
MacOS = @{
Machine = '/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json'
User = '~/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json'
}
Windows = @{
Machine = "$env:ProgramData\TailSpinToys\tstoy\tstoy.config.json"
User = "$env:APPDATA\TailSpinToys\tstoy\tstoy.config.json"
}
}
$Scope = $this.ConfigurationScope.ToString()
if ($Global:PSVersionTable.PSVersion.Major -lt 6 -or $Global:IsWindows) {
return $FilePaths.Windows.$Scope
}
elseif ($Global:IsLinux) {
return $FilePaths.Linux.$Scope
}
else {
return $FilePaths.MacOS.$Scope
}
}
[void] Create() {
$ErrorActionPreference = 'Stop'
$Json = $this.ToConfigJson()
$FilePath = $this.GetConfigurationFile()
$FolderPath = Split-Path -Path $FilePath
if (!(Test-Path -Path $FolderPath)) {
New-Item -Path $FolderPath -ItemType Directory -Force
}
Set-Content -Path $FilePath -Value $Json -Encoding utf8 -Force
}
[void] Remove() {
Remove-Item -Path $this.GetConfigurationFile() -Force -ErrorAction Stop
}
[void] Update() {
$ErrorActionPreference = 'Stop'
$Json = $this.ToConfigJson()
$FilePath = $this.GetConfigurationFile()
Set-Content -Path $FilePath -Value $Json -Encoding utf8 -Force
}
[string] ToConfigJson() {
$config = @{
updates = @{
automatic = $this.UpdateAutomatically
}
}
if ($this.CachedData) {
$this.CachedData |
Get-Member -MemberType NoteProperty |
Where-Object -Property Name -NE -Value 'updates' |
ForEach-Object -Process {
$setting = $_.Name
$config.$setting = $this.CachedData.$setting
}
if ($frequency = $this.CachedData.updates.CheckFrequency) {
$config.updates.checkFrequency = $frequency
}
}
if ($this.UpdateFrequency -ne 0) {
$config.updates.checkFrequency = $this.UpdateFrequency
}
return ($config | ConvertTo-Json)
}
}
enum TailspinScope {
Machine
User
}
enum TailspinEnsure {
Absent
Present
}
清除
如果不打算继续使用此模块,请删除该 ExampleResources
文件夹和其中的文件。
后续步骤
- 阅读 基于类的 DSC 资源,了解它们的工作原理,并考虑本教程中 DSC 资源以这种方式实现的原因。
- 考虑如何改进此 DSC 资源。 是否有任何边缘案例或功能无法处理? 更新实现以处理它们。