编写用户自定义的DSC Resource

编写一个用户自定义的DSC Resource

  一个用户自定义的Resource至少包含两个部分:

  • 一个根模块,实际上并不包含任何代码。它只是PowerShell把Resource加载到内存的方式。你可以把根模块看做为DSC Resource的一个“包”。
  • 一个或者多个Resource,有些像根模块下的子模块。这些确实执行了Resource的代码。   例如,微软提供的xNetworking模块(在它的DSC Resource套件里)在磁盘上看起来像这样:
     xNetworking
     xNetworking.psd1
     DSCResources
     MSFT_xDNSServerAddress
         MSFT_xDNSServerAddress.psm1
         MSFT_xDNSServerAddress.schema.mof
     MSFT_xIPAddress
         MSFT_xIPAddress.psm1
         MSFT_xIPAddress.schema.mof
     MSFT_xIPAddress
         MSFT_xIPAddress.psm1
         MSFT_xIPAddress.schema.mof
    
      那么让我来回顾一下这些都表明了什么:
  • 这个根模块的名字是xNetworking。它在名叫xNetworking的文件夹之下,包含一个名叫xNetworking.psd1的模块数据单。这个数据单包含如下有用的信息(原文也有其他的信息,但这是重要的元素):
    @{
    GUID = 'e6647cc3-ce9c-4c86-9eb8-2ee8919bf358'
    PowerShellVersion = '4.0'}
    
  • DSCResource这个文件夹包含所有的Resource。
  • 每个Resource都在它自身文件夹下,并且这个文件夹是这个Resource的全名(通常比这个Resource的“friendly names”还要长)。
  • 在每个Resource文件夹内有个脚本模块(.psm1)和一个概要(.schema.mof)文件。两个文件的文件名都和Resource的全名一样。
  •   把多个Resource捆绑到一个根模块,或者让它们每个都单独存在,是最多的做法。你需要部署整个根目录,并且根目录的版本(根据它的概要)定义了每个Resource的版本号。因此,如果你有三个相互协调的Resource,那把它们放在单个根模块下或许才有意义。

定制你的Resource

  你要问自己的第一个问题是,“我的Resource需要给它自己配置什么信息?”例如,如果你正在创建一个可以从业务线应用中增减用户的Resource,然后这个Resource就需要知道这个用户的名字,或许他们的角色或者工作标题,以及一些其他信息。每块信息都会变成这个Resource的一个属性,或设置。

  再以内置的File这个Resource为例。它的属性包含针对一个文件的源路径(source path)以及目标路径(destination path)。至少需要这两个信息,来确定目标节点上是否存在一个文件,以及是否有必要获取这个文件。

  作为一种执行范例,我们打算为一个虚构的业务线应用创建一个Resource样本。我们的Resource将会被叫做adatumOrdersApp。命名其实是很重要的:我们假装我们为Adatum(微软的虚构公司名字之一)工作。给Resource名加上公司名的前缀能够确保它不和与定制应用有关系的的其他任何Resource想冲突。

  我们决定让这个Resource负责维护这个应用的用户列表。用户由一个登陆名,一个全名,以及一个角色组成。而这个角色必须是“Agent”,“Manager”或者“Auditor”。因此我们这个Resource必须显示三个属性。除此之外,它也必须要有个“Ensure”属性(可以是“Present”或者“Absent”,比如,确认一个用户在不在系统内)。

开始你自己的Resource

  我们推荐用DSC Resource Designer来开始。你可以从http://gallery.technet.microsoft.com/scriptcenter/xDscResourceDesigne-Module-22eddb29 (最好搜索一下以确认是否有更新版本;但编写原文时,它的版本是v1.0)获取它。这要解压到你的模块文件夹下(下载页有相应说明),而且使用PowerShell指令搭建你的Resource的框架。

我们以定义属性来开始:

$logon    = New-DscResourceProperty –Name LogonName –Type String –Attribute Key
$ensure   = New-DscResourceProperty –Name Ensure –Type String –Attribute Write
                                    –ValidateSet "Present","Absent"
$fullname = New-DscResourceProperty –Name FullName –Type String –Attribute Write
$role     = New-DscResourceProperty –Name Role –Type String –Attribute Write
                                    –ValidateSet "Manager","Agent","Auditor"

  注意到,我们这儿为了更加便于阅读,就以这种格式的代码来作为样本。如果你像我们展示的这样键入它们是没有用的,因为它们是为了控制一个虚构的应用,它们不是用来运行什么。

  这就给了我们可以定义我们的Resource属性或者设定的四个变量。现在你可以为这个Resource搭建这样的框架:

New-DscResource –Name adatumOrdersApp
                -Properties $logon,$ensure,$fullname,$role
                -Path 'C:\program files\WindowsPowerShell\modules\adatumOrderApp'
                -FriendlyName adatumOrderApp

  这条指令就可以在我们的根模块下创建一个文件夹,它叫做adatumOrderApp。它也会为实际的Resource创建相应的子文件夹,叫做adatumOrderApp。它还会创建一个空的名为adatumOrderApp.psm1的脚本模块,和用于定义这个Resource结构的adatumOrder.schema.mof文件。

编写Resource

  我们剩下的工作将会在adatumOrderApp.psm1文件中进行。这个文件将会包含DSC需要的一下三个函数--我们只要把它们填上就好:

  • Get-TargetResource. 这个函数可以接受被定义为关键(key)的属性作为参数。如果你往回看,你会发现LogonName属性是代表了我们系统中独一无二的用户。Get-TargetResource需要检查特定的LogonName是否已经存在。该函数期望返回一个包含关键属性LogonName, Ensure, Role, 以及FullName的哈希表。如果这个用户存在,那么明显它们将会填入正确的信息,并且Ensure会被设为“Present”。如果这个用户不存在,那Ensure就会是“Absent”,LogonName将会是任何我们将要登陆的用户,并且FullName和Role将会是空的。
  • Set-TargetResource. 这个函数的参数可以是LogonName,Ensure,Role,以及FullName。它期望去检查这个用户是否存在,且根据具体情况,采取一下行动中的一个:
    • 如果Ensure是“Absent”并且这个用户存在,该函数应该移除这个用户。
    • 如果Ensure是“Present”并且这个用户存在,该函数什么也不做。
    • 如果Ensure是“Absent”并且这个用户不存在,该函数应该还是什么也不做。
    • 如果Ensure是“Present”并且这个用户不存在,该函数应该创建这个用户。

  如果这个函数需要系统重启目标节点,那可以设$global:DSCMachineStatus =1。Write-Verbose和Write-Debug可以用作返回状态或调试信息。

  • Test-TargetResource. 这个函数接受LogonName,Ensure,Role以及FullName作为参数。

  它需要去检查这些信息是否已经存在,并且同时$True或者$False。比如你定义了一个用户名,并且Ensure值设为“Absent”,如果这个用户确实存在,那么它应当返回$False,如果这个用户不存在,就应该返回$True。换句话说,这个函数就是在起初给一个期望的配置状态,然后判断现在的系统状态是否满足这个期望。这个函数看上去什么也没做。

  至于函数里面的功能实现,还是必须得有你自己的代码的。很明显,这里的代码取决于你想要做的。但是一般会依照以下要点:

  • Get函数返回当前状态
  • Set函数设置系统状态
  • Test函数比较当前状态与理想状态。

  我们将会在之后这份指导的更新版本中介绍一个更具体更详细的例子。在此期间,你可以在http://github.com/PowerShellorg/DSC. 找到很多实例。

模块化思考

  当你设计一个DSC Resource时,尝试思考模块化的编程实践。例如,你或许会在你的Set-TargetResource和Test-TargetResource函数里发现很多重复的地方,因为它们都要检查某些事物是否被配置为了理想的状态。但它实际不是重复的代码,因为在脚本模块里把“检查这些的”代码移到独立的“实际有用的”的函数内是有意义的。你之后或许只要导出三个TargetResource函数,并保持你的有用函数私有。

  这儿有一段伪代码模型的例子用于阐述这个想法:

Function Get-TargetResource {
  # ...
}

Function Test-TargetResource {
  (_CheckConfigA –and _CheckConfigB)
}

Function Set-TargetResource {
  If (-not _CheckConfigA) {
    # set item A
  }
  If (-not _CheckConfigB) {
    # set item B
}

Function _CheckConfigA {
  # ...
}

Function _CheckConfigB {
  # ...
}

Export-ModuleMember –Function *-TargetResource

  在这个例子中,“实际起作用的”函数_CheckConfigA和_CheckConfigB将会返回True或者False。它们都被假定用作检查电脑上一些配置信息。Test-Target-Resource会调用它们两个,并使用逻辑与,而后Test-TargetResoource函数只会在_CheckConfigA和_CheckConfigB都输出True时才会输出True。Set-TargetResoource函数也用了这两个“实际起作用的”函数,并执行了一些针对设置不正确的配置的改变。同时,这个下划线也不是你的“实际起作用的”函数名开头所必须使用的;这只是一些程序员在表明一个元素只是在内部使用的约定习惯而已。

编写混合的DSC Resource

  DSC的一个限制就是一个给定的节点只能被指向单个MOF配置文件。是否你正在推送MOF文件,或者已经配置了一个节点去从Pull Server拉回一个MOF文件,而每一台电脑只能获得一个MOF文件。这就意味着你的配置脚本可能很快就失去控制了。例如,假设你有一些被域控、网页服务器以及你的SQL 服务器电脑共享的相同配置设定。那就不得不把这些同样的配置内容进行很多的拷贝和传送,对吧?那么问题就来了,如果你要改变这一部分共同的配置,那你就不得不在多个地方重复做这个。一点都不开心呐。

  所以,混合的Resource就被用来解决这个需求。一开始,你要为你常用配置的东西创建一个配置脚本。然后你保存它,让它看起来像一个用户定制的DSC Resource。之后你就可以在其他配置中调用这个Resource。因此,你需要处理一个主配置文件,一个用于域控制器,一个用于SQL服务器,还有一个用于网页服务器。很明显,它们每一个都要包含自己独一无二的东西,但是它们也都要引用你的这个”常用配置(common Confirguration)”。要明白这个常用配置不是真正意义上的DSC Resource,它只是被做的很像而已。这就更容易实现了!

  例如,假设你创建这个配置脚本:

Configuration CommonConfig {
    Param([string]$IPAddress)

    Import-DscResource –ModuleName xNetworking

    WindowsFeature Backup {
        Ensure = 'Present'
        Name   = 'Windows-Server-Backup'
    }

    xIPAddress IPAddress {
        IPAddress = $IPAddress
        InterfaceAlias = "Ethernet"
        DefaultGateway = "192.168.10.2"
        SubnetMask = 24
        AddressFamily = "IPv4"
    }

    xDNSServerAddress DNSServer {
        Address = "192.168.12.8"
        InterfaceAlias = "Ethernet"
        AddressFamily = "IPv4"
    }

}

  注意这个配置里没有Node的段落!这是因为这并不是为了指向一个特定的节点。我们使用了xNetWorking模块(从 http://gallery.technet.microsoft.com/scriptcenter/xNetworking-Module-818b3583),而且我们提供了一个输入参数,那样就可以传入目标节点的IP地址。我们已经把IP地址和DNS服务器设置作为硬代码写入了,这是基于我们已经知道我们的环境怎么搭建起来的前提知识而假设的。显然你可以参数化它们中任何一个----我们只是举例说明如何混搭使用带硬编码的参数信息。

  这个方法是展示如何保存你的文件。我们打算把握它命名为CommonConfig.schema.psm1---注意这个“schema.psm1”扩展文件名,因为那是让它变得神奇的根源。然后你需要把它保存在你存放Resource的同样路径下。比如,我们将会把它按这样保存:

\Program Files\WindowsPowerShell\Modules\CommonConfigModule\DSCResources\CommonConfig\CommonConfig.schema.psm1

  现在你需要在同一个文件夹内为它创建一个表单(manifest)。这个表单的名字必须为CommonConfig.psd1:

New-ModuleManifest –Path "\Program Files\WindowsPowerShell\Modules\CommonConfigModule
\DSCResources\CommonConfig\CommonConfig.psd1" –RootModule "CommonConfig.schema.psm1"

  如果你还没有,那你就需要在模快的根目录下有一个表单,如下:

New-ModuleManifest –Path "\Program Files\WindowsPowerShell\Modules\CommonConfigModule\CommonConfigModule.psd1"

  完成后,现在你就可以在一个配置里使用它了:

Configuration DomainController {

    Import-DscResource –ModuleName CommonConfigModule

    Node DC1 {
        CommonConfig Common {
            IPAddress = "192.168.10.202"
        }
    }
}

  看看我们是如何把$IPAddress参数从常见配置中拿出来,并且把它用作配置脚本里的设置的。同时注意我们在配置的顶部导入了这个DSC Resource,然后就可以引用这个配置的名字CommonConfig,作为配置属性来设定。

  在这里,我们不是真的必须做实际的编写代码:下层的WindowsFeature,xIPAddress,以及xDNSServerAddress这些Resource就已经完成了所有工作。我们只是把这些设置捆绑在一起放在了可复用的配置里,而这个配置看起来像是一个Resource。

  你可以在http://blogs.msdn.com/b/PowerShell/archive/2014/02/25/reusing-existing-configuration-scripts-in-PowerShell-desired-state-configuration.aspx. 里找到一个关于混合Resource的更长以及更加详细的讨论。

results matching ""

    No results matching ""