Udostępnij za pomocą


Samouczek: tworzenie opartego na klasie zasobu DSC na potrzeby konfiguracji maszyny

Rozpocznij tworzenie opartego na klasach zasobu DSC, aby zarządzać plikiem konfiguracji za pomocą funkcji konfiguracji maszyny usługi Azure Automanage. Wykonanie czynności opisanych w tym samouczku zapewnia oparty na konfiguracji maszynowej zasób DSC oparty na klasie w module, którego można użyć do dalszej nauki i dostosowywania.

Ten samouczek zawiera informacje na temat wykonywania następujących czynności:

  • Tworzenie szkieletu modułu zasobów DSC
  • Dodawanie zasobu DSC opartego na klasie
  • Definiowanie właściwości zasobu DSC
  • Implementowanie metod zasobów DSC
  • Eksportowanie zasobu DSC w manifeście modułu
  • Ręczne testowanie zasobu DSC

Uwaga

Przykładowe dane wyjściowe w tym samouczku są zgodne z programem PowerShell 7.2 na komputerze z systemem Windows. Samouczek jest prawidłowy w przypadku Windows PowerShell i programu PowerShell na komputerze z systemem Linux lub macOS. Tylko dane wyjściowe są specyficzne dla uruchamiania poleceń w programie PowerShell na komputerze z systemem Windows.

Wymagania wstępne

  • Program PowerShell lub Windows PowerShell 5.1
  • Program VS Code z rozszerzeniem programu PowerShell

1 — Tworzenie szkieletu modułu zasobów DSC

Zasoby DSC muszą być zdefiniowane w module programu PowerShell.

Tworzenie folderu modułu

Utwórz nowy folder o nazwie ExampleResources. Ten folder jest używany jako folder główny modułu i cały kod w tym samouczku.

New-Item -Path './ExampleResources' -ItemType Directory
    Directory: C:\code\dsc

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----            9/8/2022 12:54 PM                ExampleResources

Tworzenie modułu za pomocą programu VS Code

ExampleResources Otwórz folder w programie VS Code. Otwórz zintegrowany terminal w programie VS Code. Upewnij się, że w terminalu jest uruchomiony program PowerShell lub Windows PowerShell.

Ważne

W pozostałej części tego samouczka uruchom określone polecenia w zintegrowanym terminalu w katalogu głównym folderu modułu. Jest to domyślny katalog roboczy w programie VS Code.

Tworzenie plików modułu

Utwórz manifest modułu za New-ModuleManifest pomocą polecenia cmdlet . Użyj ./ExampleResources.psd1 jako ścieżki. Określ parametr RootModule jako ExampleResources.psm1 i dscResourcesToExport jako 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   :

Utwórz plik modułu głównego jako 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

Utwórz plik skryptu o nazwie 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

Otwórz Helpers.ps1 w programie VS Code. Dodaj następujący wiersz.

$env:PSModulePath += "$([System.IO.Path]::PathSeparator)$pwd"

Otwórz ExampleResources.psm1 w programie VS Code. Moduł jest teraz szkieletowany i gotowy do utworzenia zasobu DSC.

2 — Dodawanie zasobu DSC opartego na klasie

Aby zdefiniować zasób DSC oparty na klasie, napiszemy klasę programu PowerShell w pliku modułu i dodamy do niego atrybut DscResource .

Definiowanie klasy

W pliku ExampleResources.psm1 dodaj następujący kod:

[DscResource()]
class Tailspin {

}

Ten kod dodaje Tailspin jako zasób DSC oparty na klasie do modułu ExampleResources .

Zatrzymaj wskaźnik myszy i [DscResource()] odczytaj ostrzeżenia.

Zrzut ekranu przedstawiający ostrzeżenia atrybutu DSCResource w programie VS Code.

Umieszczenie wskaźnika myszy na atrybucie DSCResource powoduje wyświetlenie czterech ostrzeżeń. 1. W zasobie DSC "Tailspin" brakuje metody "Set()", która zwraca wartość "[void]" i nie akceptuje żadnych parametrów. 2. W zasobie DSC "Tailspin" brakuje metody "Get()", która zwraca wartość "[Tailspin]" i nie akceptuje żadnych parametrów. 3. W zasobie DSC "Tailspin" brakuje metody "Test()", która zwraca wartość "[bool]" i nie akceptuje żadnych parametrów. 4. Zasób DSC "Tailspin" musi mieć co najmniej jedną właściwość klucza (przy użyciu składni "[DscProperty(Key)]".

Te ostrzeżenia zawierają listę wymagań dotyczących klasy jako prawidłowego zasobu DSC.

Minimalna implementacja wymaganych metod

Dodaj minimalną implementację Get()metod , Test()i Set() do klasy .

class Tailspin {
    [Tailspin] Get() {
        $CurrentState = [Tailspin]::new()
        return $CurrentState
    }

    [bool] Test() {
        return $true
    }

    [void] Set() {}
}

Po dodaniu metod atrybut DscResource ostrzega tylko o klasie, która nie ma właściwości Key .

3 — Definiowanie właściwości zasobu DSC

Przed metodami należy zdefiniować właściwości zasobu DSC. Właściwości definiują ustawienia, którymi można zarządzać dla zasobu DSC. Są one używane w metodach.

Omówienie aplikacji TSToy

Przed zdefiniowaniem właściwości zasobu DSC należy zrozumieć, jakie ustawienia chcesz zarządzać.

W tym samouczku definiujemy zasób DSC na potrzeby zarządzania ustawieniami fikcyjnej aplikacji TSToy za pomocą pliku konfiguracji. TSToy to aplikacja, która ma konfigurację na poziomie użytkownika i komputera. Zasób DSC powinien mieć możliwość skonfigurowania dowolnego pliku.

Zasób DSC powinien umożliwić użytkownikom definiowanie:

  • Zakres konfiguracji, którą zarządza, Machine lub User
  • Czy plik konfiguracji powinien istnieć
  • Czy TSToy powinien być aktualizowany automatycznie
  • Częstotliwość sprawdzania dostępności aktualizacji w okresie od 1 do 90 dni

Dodawanie właściwości ConfigurationScope

Aby zarządzać plikiem Machine konfiguracji lub User , należy zdefiniować właściwość zasobu DSC. Aby zdefiniować $ConfigurationScope właściwość w zasobie, dodaj następujący kod w klasie przed metodami:

[DscProperty(Key)] [TailspinScope]
$ConfigurationScope

Ten kod definiuje $ConfigurationScope jako właściwość Key zasobu DSC. Właściwość Key służy do unikatowego identyfikowania wystąpienia zasobu DSC. Dodanie tej właściwości spełnia jedno z wymagań atrybutu DscResource ostrzegane podczas tworzenia szkieletu klasy.

Określa również, że $ConfigurationScopetyp to TailspinScope. Aby zdefiniować typ TailspinScope , dodaj następujące wyliczenie TailspinScope po definicji klasy w pliku ExampleResources.psm1:

enum TailspinScope {
    Machine
    User
}

To wyliczenie tworzy Machine i User jedyne prawidłowe wartości właściwości $ConfigurationScope zasobu DSC.

Dodawanie właściwości Ensure

Najlepszym rozwiązaniem $Ensure jest zdefiniowanie właściwości do kontrolowania, czy istnieje wystąpienie zasobu DSC. Właściwość $Ensure zwykle ma dwie prawidłowe wartości i AbsentPresent.

  • Jeśli $Ensure parametr jest określony jako Present, zasób DSC tworzy element, jeśli nie istnieje.
  • Jeśli $Ensure parametr ma Absentwartość , zasób DSC usuwa element, jeśli istnieje.

W przypadku zasobu DSC Tailspin element do utworzenia lub usunięcia jest plikiem konfiguracji dla określonego $ConfigurationScopeelementu .

Zdefiniuj tailspinEnsure jako wyliczenie po TailspinScope. Powinna ona zawierać wartości Absent i Present.

enum TailspinEnsure {
    Absent
    Present
}

Następnie dodaj $Ensure właściwość w klasie po $ConfigurationScope właściwości . Powinien mieć pusty atrybut DscProperty , a jego typ powinien mieć wartość TailspinEnsure. Wartość domyślna powinna mieć wartość Present.

[DscProperty()] [TailspinEnsure]
$Ensure = [TailspinEnsure]::Present

Dodawanie właściwości UpdateAutomatically

Aby zarządzać aktualizacjami automatycznymi, zdefiniuj $UpdateAutomatically właściwość w klasie po $Ensure właściwości . Jego atrybut DscProperty powinien wskazywać, że jest obowiązkowy, a jego typ powinien być wartością logiczną.

[DscProperty(Mandatory)] [bool]
$UpdateAutomatically

Dodawanie właściwości UpdateFrequency

Aby zarządzać częstotliwością sprawdzania dostępności aktualizacji przez TSToy, dodaj $UpdateFrequency właściwość w klasie po $UpdateAutomatically właściwości . Powinien mieć pusty atrybut DscProperty , a jego typ powinien być int. Użyj atrybutu ValidateRange , aby ograniczyć prawidłowe wartości do $UpdateFrequency zakresu od 1 do 90.

[DscProperty()] [int] [ValidateRange(1, 90)]
$UpdateFrequency

Dodawanie właściwości Reasons

Ponieważ ten zasób DSC jest przeznaczony do użycia z funkcją konfiguracji maszyny usługi Azure Automanage, musi mieć właściwość Reasons , która spełnia następujące wymagania:

  • Należy ją zadeklarować za pomocą właściwości NotConfigurable w atrybucie DscProperty .
  • Musi to być tablica obiektów, które mają właściwość String o nazwie Code, właściwość String o nazwie Phrase i żadne inne właściwości.

Konfiguracja maszyny używa właściwości Reasons do standaryzacji sposobu prezentowania informacji o zgodności. Każdy obiekt zwracany przez metodę Get() właściwości Reasons określa, jak i dlaczego wystąpienie zasobu DSC nie jest zgodne.

Konfiguracja maszyny używa właściwości Reasons do standaryzacji sposobu prezentowania informacji o zgodności. Każdy obiekt zwracany przez metodę Get() właściwości Reasons identyfikuje jedną z właściwości zasobu DSC, żądany stan i stan rzeczywisty.

Aby zdefiniować właściwość Reasons , należy zdefiniować dla niej klasę. Zdefiniuj klasę ExampleResourcesReason po wyliczenie TailspinEnsure . Powinna mieć właściwości Code (Kod ) i Phrase (Fraza ) jako ciągi. Obie właściwości powinny mieć atrybut DscProperty .

Aby zwiększyć czytelne przyczyny podczas testowania ręcznego, zdefiniuj metodę ToString() w klasie ExampleResourcesReason , jak pokazano we fragmencie kodu.

class ExampleResourcesReason {
    [DscProperty()]
    [string] $Code

    [DscProperty()]
    [string] $Phrase

    [string] ToString() {
        return "`n$($this.Code):`n`t$($this.Phrase)`n"
    }
}

Następnie dodaj $Reasons właściwość w klasie zasobu DSC po $UpdateFrequency właściwości . Powinien mieć atrybut DscProperty określony z opcją NotConfigurable , a jego typem powinien być ExampleResourcesReason[].

[DscProperty(NotConfigurable)] [ExampleResourcesReason[]]
$Reasons

Dodawanie właściwości ukrytej pamięci podręcznej

Następnie dodaj dwie ukryte właściwości do buforowania bieżącego stanu zasobu: $CachedCurrentState i $CachedData. Ustaw typ $CachedCurrentState na Tailspin, taki sam jak klasa i zwracany typ dla Get() metody. Ustaw typ na $CachedDataPSCustomObject. Prefiks obu właściwości za pomocą słowa kluczowego hidden . Nie należy określać atrybutu DscProperty dla obu.

hidden [Tailspin] $CachedCurrentState
hidden [PSCustomObject] $CachedData

Te ukryte właściwości są używane w Get() metodach i Set() zdefiniowanych później.

Przejrzyj plik modułu

W tym momencie ExampleResources.psm1 należy zdefiniować następujące elementy:

  • Klasa Tailspin z właściwościami $ConfigurationScope, $Ensure, $UpdateAutomaticallyi $UpdateFrequency
  • Wyliczenie TailspinScope z wartościami Machine iUser
  • Wyliczenie TailspinEnsure z wartościami Present iAbsent
  • Minimalne implementacje Get()metod , Test()i Set() .
[DscResource()]
class Tailspin {
    [DscProperty(Key)] [TailspinScope]
    $ConfigurationScope

    [DscProperty()] [TailspinEnsure]
    $Ensure = [TailspinEnsure]::Present

    [DscProperty(Mandatory)] [bool]
    $UpdateAutomatically

    [DscProperty()] [int] [ValidateRange(1,90)]
    $UpdateFrequency

    [DscProperty(NotConfigurable)] [ExampleResourcesReason[]]
    $Reasons

    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
}

class ExampleResourcesReason {
    [DscProperty()]
    [string] $Code

    [DscProperty()]
    [string] $Phrase

    [string] ToString() {
        return "`n$($this.Code):`n`t$($this.Phrase)`n"
    }
}

Teraz, gdy zasób DSC spełnia wymagania, możesz go zobaczyć Get-DscResource . W programie VS Code otwórz nowy terminal programu 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 — Implementowanie metod zasobów DSC

Metody zasobu DSC definiują sposób pobierania bieżącego stanu zasobu DSC, weryfikowania go względem żądanego stanu i wymuszania żądanego stanu.

Metoda Get

Metoda Get() pobiera bieżący stan zasobu DSC. Służy do ręcznego sprawdzania zasobu DSC i jest wywoływany przez metodę Test() .

Metoda Get() nie ma parametrów i zwraca wystąpienie klasy jako dane wyjściowe. Tailspin W przypadku zasobu DSC minimalna implementacja wygląda następująco:

[Tailspin] Get() {
    $CurrentState = [Tailspin]::new()
    return $CurrentState
}

Jedyną rzeczą, którą wykonuje ta implementacja, jest utworzenie wystąpienia klasy Tailspin i zwrócenie go. Aby zobaczyć to zachowanie, możesz wywołać metodę Invoke-DscResource .

Invoke-DscResource -Name Tailspin -Module ExampleResources -Method Get -Property @{
    ConfigurationScope  = 'User'
    UpdateAutomatically = $true
}
ConfigurationScope  Ensure UpdateAutomatically UpdateFrequency
------------------  ------ ------------------- ---------------
           Machine Present               False               0

Właściwości zwróconego obiektu są ustawione na ich wartość domyślną. Wartość parametru $ConfigurationScope powinna zawsze być wartością podaną przez użytkownika. Aby metoda była Get() przydatna, musi zwrócić rzeczywisty stan zasobu DSC.

[Tailspin] Get() {
    $CurrentState = [Tailspin]::new()

    $CurrentState.ConfigurationScope = $this.ConfigurationScope

    $this.CachedCurrentState = $CurrentState

    return $CurrentState
}

Zmienna $this odwołuje się do działającego wystąpienia zasobu DSC. Teraz, jeśli użyjesz Invoke-DscResource ponownie, $ConfigurationScope ma poprawną wartość.

Invoke-DscResource -Name Tailspin -Module ExampleResources -Method Get -Property @{
    ConfigurationScope  = 'User'
    UpdateAutomatically = $true
}
ConfigurationScope  Ensure UpdateAutomatically UpdateFrequency
------------------  ------ ------------------- ---------------
              User Present               False               0

Następnie zasób DSC musi określić, czy plik konfiguracji istnieje. Jeśli tak, $Ensure powinien to być Present. Jeśli tak nie jest, $Ensure powinien mieć wartość Absent.

Lokalizacja plików konfiguracji TSToy zależy od systemu operacyjnego i zakresu konfiguracji:

  • W przypadku komputerów z systemem Windows:
    • Plik Machine konfiguracji to %PROGRAMDATA%\TailSpinToys\tstoy\tstoy.config.json
    • Plik User konfiguracji to %APPDATA%\TailSpinToys\tstoy\tstoy.config.json
  • W przypadku maszyn z systemem Linux:
    • Plik Machine konfiguracji to /etc/xdg/TailSpinToys/tstoy/tstoy.config.json
    • Plik User konfiguracji to ~/.config/TailSpinToys/tstoy/tstoy.config.json
  • W przypadku maszyn z systemem macOS:
    • Plik Machine konfiguracji to /Library/Preferences/TailSpinToys/tstoy/tstoy.config.json
    • Plik User konfiguracji to ~/Library/Preferences/TailSpinToys/tstoy/tstoy.config.json

Aby obsłużyć te ścieżki, należy utworzyć metodę pomocnika , 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
    }
}

Aby przetestować tę nową metodę, wykonaj instrukcję using , aby załadować klasy i wyliczenia modułu ExampleResources do bieżącej sesji.

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

Otwórz Helpers.ps1 w programie VS Code. Skopiuj i wklej ścieżki dla plików konfiguracji do skryptu, przypisując je do $TSToyMachinePath i $TSToyUserPath. Plik powinien wyglądać następująco:

$env:PSModulePath += "<separator>$pwd"
$TSToyMachinePath = '<machine configuration file path>'
$TSToyUserPath = '<user configuration file path>'

Zamknij terminal w programie VS Code i otwórz nowy terminal. Kropka-źródło Helpers.ps1.

. ./Helpers.ps1

Teraz możesz napisać pozostałą część Get() metody.

[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
    }

    $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
}

Po ustawieniu $ConfigurationScope i określeniu ścieżki pliku konfiguracji metoda sprawdza, czy plik istnieje. Jeśli nie istnieje, ustawienie $Ensure wartości Absent i zwrócenie wyniku jest potrzebne.

Jeśli plik istnieje, metoda musi przekonwertować zawartość z formatu JSON, aby utworzyć bieżący stan konfiguracji. Następnie metoda sprawdza, czy klucze mają jakąkolwiek wartość przed przypisaniem ich do właściwości bieżącego stanu. Jeśli nie zostaną określone, zasób DSC musi rozważyć ich usunięcie i stan domyślny.

W tym momencie zasób DSC buforuje dane. Buforowanie danych umożliwia sprawdzenie danych podczas opracowywania i jest przydatne podczas implementowania Set() metody.

To zachowanie można sprawdzić lokalnie.

$GetParameters = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Method   = 'Get'
    Property = @{
        ConfigurationScope = 'User'
    }
}

Invoke-DscResource @GetParameters
New-Item -Path $TSToyUserPath -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 Otwórz plik konfiguracji zakresu w programie VS Code.

code $TSToyUserPath

Skopiuj tę konfigurację JSON do pliku i zapisz ją.

{
    "unmanaged_key": true,
    "updates": {
        "automatic": true,
        "checkFrequency": 30
    }
}

Ponownie wywołaj metodę Invoke-DscResource i zobacz wartości odzwierciedlone w wynikach.

Invoke-DscResource @GetParameters
ConfigurationScope  Ensure UpdateAutomatically UpdateFrequency
------------------  ------ ------------------- ---------------
              User Present                True              30

Metoda Get() zwraca teraz dokładne informacje o bieżącym stanie zasobu DSC.

Przyczyny obsługi

W przypadku zasobów DSC zgodnych z konfiguracją maszyny Get() metoda musi również wypełnić właściwość Reasons . W tym celu utwórz metodę GetReasons() . Powinna zwrócić tablicę obiektów ExampleResourcesReason i przyjąć pojedynczy obiekt Tailspin jako dane wejściowe.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    return $DefinedReasons
}

Następnie metoda musi sprawdzić poprawność każdego konfigurowalnego ustawienia. Dla każdego ustawienia metoda musi zwrócić element ExampleResourcesReason identyfikujący i opisujący stan.

Pierwszym ustawieniem do zweryfikowania $Ensure jest właściwość . Jeśli $Ensure jest poza stanem, wszystkie inne właściwości będą niepoprawne, ponieważ plik konfiguracji istnieje, gdy nie powinien lub nie istnieje, kiedy powinien.

Metoda musi zdefiniować przyczynę z poprawnym kodem i rozsądną frazą. Kod jest zawsze w formacie <ResourceName>.<ResourceName>.<PropertyName>. Fraza to zawsze zdanie opisujące sprawdzanie, a następnie zdania opisujące oczekiwany stan i rzeczywisty stan.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    $FilePath = $this.GetConfigurationFile()

    if ($this.Ensure -eq [TailspinEnsure]::Present) {
        $Expected = "Expected configuration file to exist at '$FilePath'."
    } else {
        $Expected = "Expected configuration file not to exist at '$FilePath'."
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
        $Actual = "The configuration file exists at '$FilePath'."
    } else {
        $Actual = "The configuration file was not found at '$FilePath'."
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.Ensure"
        Phrase = @(
            "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
            $Expected
            $Actual
        ) -join "`n`t"
    }

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $DefinedReasons
    }

    return $DefinedReasons
}

Jeśli $Ensure nie jest w stanie, metoda powinna sprawdzić, czy żądany stan to Absent. Jeśli tak jest, nie ma żadnych innych właściwości, które mogą być poza stanem, ponieważ plik konfiguracji nie istnieje i nie powinien istnieć.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    $FilePath = $this.GetConfigurationFile()

    if ($this.Ensure -eq [TailspinEnsure]::Present) {
        $Expected = "Expected configuration file to exist at '$FilePath'."
    } else {
        $Expected = "Expected configuration file not to exist at '$FilePath'."
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
        $Actual = "The configuration file exists at '$FilePath'."
    } else {
        $Actual = "The configuration file was not found at '$FilePath'."
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.Ensure"
        Phrase = @(
            "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
            $Expected
            $Actual
        ) -join "`n`t"
    }

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $DefinedReasons
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
        return $DefinedReasons
    }

    return $DefinedReasons
}

Jeśli $Ensure element jest Present i plik istnieje, metoda musi sprawdzić pozostałe konfigurowalne właściwości.

$UpdateAutomatically Sprawdzanie właściwości jest proste, ponieważ jest to wartość obowiązkowa i logiczna.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    $FilePath = $this.GetConfigurationFile()

    if ($this.Ensure -eq [TailspinEnsure]::Present) {
        $Expected = "Expected configuration file to exist at '$FilePath'."
    } else {
        $Expected = "Expected configuration file not to exist at '$FilePath'."
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
        $Actual = "The configuration file exists at '$FilePath'."
    } else {
        $Actual = "The configuration file was not found at '$FilePath'."
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.Ensure"
        Phrase = @(
            "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
            $Expected
            $Actual
        ) -join "`n`t"
    }

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $DefinedReasons
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
        return $DefinedReasons
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.UpdateAutomatically"
        Phrase = (@(
                "Checked value of the 'updates.automatic' key in the TSToy configuration file."
                "Expected boolean value of '$($this.UpdateAutomatically)'"
                "Actual boolean value of '$($CurrentState.UpdateAutomatically)'"
            ) -join "`n`t")
    }

    return $DefinedReasons
}

Ostatnia właściwość do sprawdzenia to $UpdateFrequency. To sprawdzenie może być zwarcie, jeśli wartość właściwości to 0. Jeśli właściwość jest określona, zawsze będzie należeć do zakresu od 1 do 90, co oznacza, że wartość 0 wskazuje, że właściwość nie jest zarządzana.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    $FilePath = $this.GetConfigurationFile()

    if ($this.Ensure -eq [TailspinEnsure]::Present) {
        $Expected = "Expected configuration file to exist at '$FilePath'."
    } else {
        $Expected = "Expected configuration file not to exist at '$FilePath'."
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
        $Actual = "The configuration file exists at '$FilePath'."
    } else {
        $Actual = "The configuration file was not found at '$FilePath'."
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.Ensure"
        Phrase = @(
            "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
            $Expected
            $Actual
        ) -join "`n`t"
    }

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $DefinedReasons
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
        return $DefinedReasons
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.UpdateAutomatically"
        Phrase = (@(
                "Checked value of the 'updates.automatic' key in the TSToy configuration file."
                "Expected boolean value of '$($this.UpdateAutomatically)'"
                "Actual boolean value of '$($CurrentState.UpdateAutomatically)'"
            ) -join "`n`t")
    }

    return $DefinedReasons

    # Short-circuit the check; UpdateFrequency isn't defined by caller
    if ($this.UpdateFrequency -eq 0) {
        return $DefinedReasons
    }
}

Na koniec metoda musi porównać żądane i bieżące stany $UpdateFrequency właściwości i zdefiniować przyczynę, jeśli są one poza stanem.

[ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
    [ExampleResourcesReason[]]$DefinedReasons = @()

    $FilePath = $this.GetConfigurationFile()

    if ($this.Ensure -eq [TailspinEnsure]::Present) {
        $Expected = "Expected configuration file to exist at '$FilePath'."
    } else {
        $Expected = "Expected configuration file not to exist at '$FilePath'."
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
        $Actual = "The configuration file exists at '$FilePath'."
    } else {
        $Actual = "The configuration file was not found at '$FilePath'."
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.Ensure"
        Phrase = @(
            "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
            $Expected
            $Actual
        ) -join "`n`t"
    }

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $DefinedReasons
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
        return $DefinedReasons
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.UpdateAutomatically"
        Phrase = (@(
                "Checked value of the 'updates.automatic' key in the TSToy configuration file."
                "Expected boolean value of '$($this.UpdateAutomatically)'"
                "Actual boolean value of '$($CurrentState.UpdateAutomatically)'"
            ) -join "`n`t")
    }

    # Short-circuit the check; UpdateFrequency isn't defined by caller
    if ($this.UpdateFrequency -eq 0) {
        return $DefinedReasons
    }

    $DefinedReasons += [ExampleResourcesReason]@{
        Code   = "Tailspin.Tailspin.UpdateFrequency"
        Phrase = (@(
                "Checked value of the 'updates.checkFrequency' key in the TSToy configuration file."
                "Expected integer value of '$($this.UpdateFrequency)'."
                "Actual integer value of '$($CurrentState.UpdateFrequency)'."
            ) -join "`n`t")
    }

    return $DefinedReasons
}

Po zaimplementowaniu GetReasons() metody należy zaktualizować metodę w Get() celu wywołania jej przed zwróceniem bieżącego stanu.

[Tailspin] Get() {
    $CurrentState = [Tailspin]::new()

    $CurrentState.ConfigurationScope = $this.ConfigurationScope

    $FilePath = $this.GetConfigurationFile()

    if (!(Test-Path -Path $FilePath)) {
        $CurrentState.Ensure = [TailspinEnsure]::Absent
        $CurrentState.Reasons = $this.GetReasons($CurrentState)

        $this.CachedCurrentState = $CurrentState

        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
    }

    $CurrentState.Reasons = $this.GetReasons($CurrentState)

    $this.CachedCurrentState = $CurrentState

    return $CurrentState
}

Po zaktualizowaniu implementacji można sprawdzić zachowanie i sprawdzić, czy przyczyny są zgłaszane:

$SharedParameters = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'User'
        Ensure              = 'Present'
        UpdateAutomatically = $false
    }
}

Invoke-DscResource -Method Get @SharedParameters

$SharedParameters.Property.UpdateAutomatically = $true
Invoke-DscResource -Method Get @SharedParameters

$SharedParameters.Property.UpdateFrequency = 1
Invoke-DscResource -Method Get @SharedParameters
ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 1
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'False'
                        Actual boolean value of 'True'
                      }

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 1
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      }

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 1
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      ,
                      Tailspin.Tailspin.UpdateFrequency:
                        Checked value of the 'updates.checkFrequency' key in
                      the TSToy configuration file.
                        Expected integer value of '1'.
                        Actual integer value of '1'.
                      }

Metoda Test

Za pomocą zaimplementowanej Get() metody można sprawdzić, czy bieżący stan jest zgodny z żądanym stanem.

Metody minimalna implementacja Test() zawsze zwraca wartość $true.

[bool] Test() {
    return $true
}

Możesz to sprawdzić, uruchamiając polecenie 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

Należy dokładnie odzwierciedlić metodę Test() , czy zasób DSC jest w żądanym stanie. Metoda Test() powinna zawsze wywoływać metodę, Get() aby mieć bieżący stan w celu porównania z żądanym stanem. Następnie sprawdź, czy $Ensure właściwość jest poprawna. Jeśli tak nie jest, wróć $false natychmiast. Nie są wymagane żadne dalsze kontrole, jeśli $Ensure właściwość jest poza żądanym stanem.

[bool] Test() {
    $CurrentState = $this.Get()

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $false
    }

    return $true
}

Teraz możesz zweryfikować zaktualizowane zachowanie.

$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

Następnie sprawdź, czy wartość $Ensure to Absent. Jeśli plik konfiguracji nie istnieje i nie powinien istnieć, nie ma powodu, aby sprawdzić pozostałe właściwości.

[bool] Test() {
    $CurrentState = $this.Get()

    if ($CurrentState.Ensure -ne $this.Ensure) {
        return $false
    }

    if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
        return $true
    }

    return $true
}

Następnie metoda musi porównać bieżący stan właściwości, które zarządzają zachowaniem aktualizacji TSToy. Najpierw sprawdź, czy $UpdateAutomatically właściwość jest w prawidłowym stanie. Jeśli tak nie jest, zwróć polecenie $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
}

Aby porównać $UpdateFrequencymetodę , musimy określić, czy użytkownik określił wartość. Ponieważ $UpdateFrequency atrybut ValidateRange właściwości jest inicjowany i 0 określa, że musi być między 1 i 90, wiemy, że wartość 0 wskazuje, że właściwość nie została określona.

Dzięki tym informacjom Test() metoda powinna:

  1. Zwracanie $true , jeśli użytkownik nie określił $UpdateFrequency
  2. Zwracanie $false , jeśli użytkownik określił $UpdateFrequency , a wartość systemu nie jest równa wartości określonej przez użytkownika
  3. Zwracanie $true , jeśli żaden z poprzednich warunków nie został spełniony
[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() Teraz metoda używa następującej kolejności operacji:

  1. Pobierz bieżący stan konfiguracji TSToy.
  2. Zwróć, $false jeśli konfiguracja istnieje, gdy nie powinna istnieć lub nie istnieje, gdy powinna.
  3. Zwróć, $true jeśli konfiguracja nie istnieje i nie powinna istnieć.
  4. Zwróć, $false jeśli ustawienie automatycznej aktualizacji konfiguracji nie jest zgodne z żądanym ustawieniem.
  5. Zwróć, $true jeśli użytkownik nie określił wartości ustawienia częstotliwości aktualizacji.
  6. Zwróć, $false jeśli określona wartość użytkownika dla ustawienia częstotliwości aktualizacji nie jest zgodna z ustawieniem konfiguracji.
  7. Zwróć, $true jeśli żaden z poprzednich warunków nie został spełniony.

Możesz sprawdzić metodę Test() lokalnie:

$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

Dzięki temu kodowi metoda jest w stanie dokładnie określić, Test() czy plik konfiguracji jest w żądanym stanie.

Metoda Set

Teraz, gdy Get() metody i Test() działają niezawodnie, można zdefiniować metodę, aby rzeczywiście wymusić Set() żądany stan.

W minimalnej implementacji Set() metoda nic nie robi.

[void] Set() {}

Najpierw należy określić, Set() czy zasób DSC musi zostać utworzony, zaktualizowany lub usunięty.

[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() najpierw wywołuje metodę , Test() aby określić, czy coś rzeczywiście należy zrobić. Niektóre narzędzia, takie jak funkcja konfiguracji maszyny usługi Azure Automanage, zapewniają, że Set() metoda jest wywoływana tylko po metodzie Test() . Nie ma jednak takiej gwarancji podczas korzystania z Invoke-DscResource polecenia cmdlet.

Test() Ponieważ metoda wywołuje Get()metodę , która buforuje bieżący stan, zasób DSC może uzyskać dostęp do buforowanego bieżącego stanu bez konieczności ponownego Get() wywołania metody.

Następnie zasób DSC musi rozróżniać zachowania tworzenia, usuwania i aktualizacji dla pliku konfiguracji. Jeśli plik konfiguracji nie istnieje, wiemy, że należy go utworzyć. Jeśli plik konfiguracji istnieje i nie powinien, wiemy, że należy go usunąć. Jeśli plik konfiguracji istnieje i powinien istnieć, wiemy, że musi zostać zaktualizowany.

Utwórz trzy nowe metody do obsługi tych operacji i wywołaj je w metodzie zgodnie z Set() potrzebami. Typ zwracany dla wszystkich trzech powinien być unieważniony.

[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() {}

Utwórz również nową metodę o nazwie ToConfigJson(). Jego zwracany typ powinien być ciągiem. Ta metoda konwertuje zasób DSC na kod JSON oczekiwany przez plik konfiguracji. Możesz rozpocząć od następującej minimalnej implementacji:

[string] ToConfigJson() {
    $config = @{}

    return ($config | ConvertTo-Json)
}

Metoda ToConfigJson

Minimalna implementacja zwraca pusty obiekt JSON jako ciąg. Aby było to przydatne, należy zwrócić rzeczywistą reprezentację JSON ustawień w pliku konfiguracji TSToy.

Najpierw wstępnie wypełniaj $config tabelę skrótów z obowiązkowym ustawieniem automatycznych aktualizacji, dodając updates klucz z jego wartością jako tabelę skrótu. Tabela skrótu automatic powinna mieć klucz. Przypisz wartość właściwości klasy $UpdateAutomatically do automatic klucza.

[string] ToConfigJson() {
    $config = @{
        updates = @{
            automatic = $this.UpdateAutomatically
        }
    }

    return ($config | ConvertTo-Json)
}

Ten kod tłumaczy reprezentację zasobu DSC ustawień TSToy na strukturę oczekiwaną przez plik konfiguracji TSToy.

Następnie metoda musi sprawdzić, czy klasa buforowała dane z istniejącego pliku konfiguracji. Buforowane dane umożliwiają zasobowi DSC zarządzanie zdefiniowanymi ustawieniami bez zastępowania lub usuwania niezarządzanych ustawień.

[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)
}

Jeśli klasa buforowała ustawienia z istniejącej konfiguracji, to:

  1. Sprawdza właściwości buforowanych danych, wyszukując jakiekolwiek właściwości, którymi nie zarządza zasób DSC. Jeśli zostanie znalezione, metoda wstawia te niezarządzane właściwości do tabeli skrótu $config .

    Ponieważ zasób DSC zarządza tylko ustawieniami aktualizacji, każde ustawienie z wyjątkiem updates jest wstawiane.

  2. Sprawdza, czy checkFrequency ustawienie jest updates ustawione. Jeśli jest ona ustawiona, metoda wstawia tę wartość do tabeli skrótu $config .

    Ta operacja umożliwia zasobowi DSC ignorowanie $UpdateFrequency właściwości, jeśli użytkownik go nie określi.

  3. Na koniec metoda musi sprawdzić, czy użytkownik określił $UpdateFrequency właściwość i wstaw ją do tabeli skrótowej $config , jeśli tak.

Przy użyciu tego kodu ToConfigJson() metoda:

  1. Zwraca dokładną reprezentację JSON żądanego stanu oczekiwanego przez aplikację TSToy w pliku konfiguracji
  2. Uwzględnia ustawienia usługi TSToy, którymi zasób DSC nie zarządza jawnie
  3. Uwzględnia istniejącą wartość częstotliwości aktualizacji TSToy, jeśli użytkownik nie określił tej wartości, w tym pozostawienie jej niezdefiniowanego w pliku konfiguracji

Aby przetestować tę nową metodę, zamknij terminal programu VS Code i otwórz nowy. Wykonaj instrukcję, using aby załadować klasy i wyliczenia modułu ExampleResources do bieżącej sesji i dot-source skryptu helpers.ps1 .

using module ./ExampleResources.psd1
. ./Helpers.ps1
$Example = [Tailspin]::new()
Get-Content -Path $TSToyUserPath
$Example.ConfigurationScope = 'User'
$Example.ToConfigJson()

Get() Przed wywołaniem metody jedyną wartością w danych wyjściowych metody ToJsonConfig jest przekonwertowana wartość właściwości$UpdateAutomatically.

{
    "unmanaged_key": true,
    "updates": {
        "automatic": false,
        "checkFrequency": 30
    }
}

{
  "updates": {
    "automatic": false
  }
}
$Example.Get()
$Example.ToConfigJson()

Po wywołaniu Get()polecenia dane wyjściowe zawierają niezarządzany klucz najwyższego poziomu: unmanaged_key. Zawiera również istniejące ustawienie w pliku konfiguracji, $UpdateFrequency ponieważ nie zostało jawnie ustawione w zasobie DSC.

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : False
UpdateFrequency     : 30
Reasons             : {}

{
  "unmanaged_key": true,
  "updates": {
    "automatic": false,
    "checkFrequency": 30
  }
}
$Example.UpdateFrequency = 7
$Example.ToConfigJson()

Po $UpdateFrequency ustawieniu dane wyjściowe odzwierciedlają określoną wartość.

{
  "unmanaged_key": true,
  "updates": {
    "automatic": false,
    "checkFrequency": 7
  }
}

Metoda Create

Aby zaimplementować metodę Create() , musimy przekonwertować właściwości określone przez użytkownika dla zasobu DSC na kod JSON oczekiwany przez TSToy w pliku konfiguracji i zapisać go do tego pliku.

[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
}

Metoda używa ToConfigJson() metody , aby pobrać kod JSON dla pliku konfiguracji. Sprawdza, czy folder pliku konfiguracji istnieje i tworzy go w razie potrzeby. Na koniec tworzy plik konfiguracji i zapisuje do niego kod JSON.

Metoda Remove

Metoda Remove() ma najprostsze zachowanie. Jeśli plik konfiguracji istnieje, usuń go.

[void] Remove() {
    Remove-Item -Path $this.GetConfigurationFile() -Force -ErrorAction Stop
}

Metoda Update

Implementacja Update() metody jest podobna do metody Create . Musi przekonwertować właściwości określone przez użytkownika dla zasobu DSC na kod JSON oczekiwany przez TSToy w pliku konfiguracji i zastąpić ustawienia w tym pliku.

[void] Update() {
    $ErrorActionPreference = 'Stop'

    $Json = $this.ToConfigJson()
    $FilePath   = $this.GetConfigurationFile()

    Set-Content -Path $FilePath -Value $Json -Encoding utf8 -Force
}

5 — Ręczne testowanie zasobu DSC

Dzięki w pełni zaimplementowanemu zasobowi DSC można teraz przetestować jego zachowanie.

Przed rozpoczęciem testowania zamknij terminal programu VS Code i otwórz nowy. Kropka źródło skryptu Helpers.ps1 . Dla każdego scenariusza testowego utwórz tabelę $DesiredState skrótu zawierającą parametry udostępnione i wywołaj metody w następującej kolejności:

  1. Get(), aby pobrać początkowy stan zasobu DSC
  2. Test(), aby sprawdzić, czy zasób DSC uważa go za w żądanym stanie
  3. Set(), aby wymusić żądany stan
  4. Test(), aby sprawdzić, czy zasób DSC uważa, że ma zostać poprawnie ustawiony
  5. Get(), aby potwierdzić stan końcowy zasobu DSC

Scenariusz: TSToy nie powinien być aktualizowany automatycznie w zakresie użytkownika

W tym scenariuszu istniejąca konfiguracja w zakresie użytkownika musi być skonfigurowana do automatycznego aktualizowania. Wszystkie inne ustawienia powinny pozostać nietknięte.

. ./Helpers.ps1

$DesiredState = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'User'
        UpdateAutomatically = $false
        Ensure              = 'Present'
    }
}

Get-Content -Path $TSToyUserPath

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 $TSToyUserPath
{
    "unmanaged_key": true,
    "updates": {
        "automatic": true,
        "checkFrequency": 30
    }
}

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 30
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'False'
                        Actual boolean value of 'True'
                      }

InDesiredState : False

RebootRequired : False

InDesiredState : True

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : False
UpdateFrequency     : 30
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'False'
                        Actual boolean value of 'False'
                      }

{
  "unmanaged_key": true,
  "updates": {
    "automatic": false,
    "checkFrequency": 30
  }
}

Scenariusz: Tailspin powinien zostać automatycznie zaktualizowany zgodnie z dowolnym harmonogramem w zakresie użytkownika

W tym scenariuszu istniejąca konfiguracja w zakresie użytkownika musi zostać skonfigurowana do automatycznego aktualizowania. Wszystkie inne ustawienia powinny pozostać nietknięte.

. ./Helpers.ps1

$DesiredState = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'User'
        UpdateAutomatically = $true
        Ensure              = 'Present'
    }
}

Get-Content -Path $TSToyUserPath

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 $TSToyUserPath
{
  "unmanaged_key": true,
  "updates": {
    "automatic": false,
    "checkFrequency": 30
  }
}

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : False
UpdateFrequency     : 30
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'False'
                      }

InDesiredState : False

RebootRequired : False

InDesiredState : True

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 30
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      }

{
  "unmanaged_key": true,
  "updates": {
    "automatic": true,
    "checkFrequency": 30
  }
}

Scenariusz: TSToy powinien być aktualizowany automatycznie codziennie w zakresie użytkownika

W tym scenariuszu istniejąca konfiguracja w zakresie użytkownika musi zostać skonfigurowana do automatycznego i codziennego aktualizowania. Wszystkie inne ustawienia powinny pozostać nietknięte.

. ./Helpers.ps1

$DesiredState = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'User'
        UpdateAutomatically = $true
        UpdateFrequency     = 1
        Ensure              = 'Present'
    }
}

Get-Content -Path $TSToyUserPath

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 $TSToyUserPath
{
  "unmanaged_key": true,
  "updates": {
    "automatic": true,
    "checkFrequency": 30
  }
}

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 30
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      ,
                      Tailspin.Tailspin.UpdateFrequency:
                        Checked value of the 'updates.checkFrequency' key in
                      the TSToy configuration file.
                        Expected integer value of '1'.
                        Actual integer value of '30'.
                      }

InDesiredState : False

RebootRequired : False

InDesiredState : True

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 1
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file to exist at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      ,
                      Tailspin.Tailspin.UpdateFrequency:
                        Checked value of the 'updates.checkFrequency' key in
                      the TSToy configuration file.
                        Expected integer value of '1'.
                        Actual integer value of '1'.
                      }

{
  "unmanaged_key": true,
  "updates": {
    "checkFrequency": 1,
    "automatic": true
  }
}

Scenariusz: TSToy nie powinien mieć konfiguracji zakresu użytkownika

W tym scenariuszu plik konfiguracji TSToy w zakresie użytkownika nie powinien istnieć. Jeśli tak, zasób DSC powinien usunąć plik.

. ./Helpers.ps1

$DesiredState = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'User'
        UpdateAutomatically = $false
        Ensure              = 'Absent'
    }
}

Get-Content -Path $TSToyUserPath

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 $TSToyUserPath
{
  "unmanaged_key": true,
  "updates": {
    "checkFrequency": 1,
    "automatic": true
  }
}

ConfigurationScope  : User
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 1
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file not to exist at 'C:\Users\ml
                      ombardi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.j
                      son'.
                        The configuration file exists at 'C:\Users\mlombardi\App
                      Data\Roaming\TailSpinToys\tstoy\tstoy.config.json'.
                      }

InDesiredState : False

RebootRequired : False

InDesiredState : True

ConfigurationScope  : User
Ensure              : Absent
UpdateAutomatically : False
UpdateFrequency     : 0
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the User scope.
                        Expected configuration file not to exist at 'C:\Users\ml
                      ombardi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.j
                      son'.
                        The configuration file was not found at 'C:\Users\mlomba
                      rdi\AppData\Roaming\TailSpinToys\tstoy\tstoy.config.json'
                      .
                      }

False

Scenariusz: TSToy powinien być aktualizowany automatycznie co tydzień w zakresie maszyny

W tym scenariuszu nie ma zdefiniowanej konfiguracji w zakresie maszyny. Zakres maszyny należy skonfigurować do automatycznego i codziennego aktualizowania. Zasób DSC powinien utworzyć plik i wszystkie foldery nadrzędne zgodnie z wymaganiami.

. ./Helpers.ps1

$DesiredState = @{
    Name     = 'Tailspin'
    Module   = 'ExampleResources'
    Property = @{
        ConfigurationScope  = 'Machine'
        UpdateAutomatically = $true
        Ensure              = 'Present'
    }
}

Test-Path -Path $TSToyMachinePath, (Split-Path -Path $TSToyMachinePath)

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 $TSToyMachinePath
False
False

ConfigurationScope  : Machine
Ensure              : Absent
UpdateAutomatically : False
UpdateFrequency     : 0
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the Machine scope.
                        Expected configuration file to exist at
                      'C:\ProgramData\TailSpinToys\tstoy\tstoy.config.json'.
                        The configuration file was not found at
                      'C:\ProgramData\TailSpinToys\tstoy\tstoy.config.json'.
                      }

InDesiredState : False

RebootRequired : False

InDesiredState : True

ConfigurationScope  : Machine
Ensure              : Present
UpdateAutomatically : True
UpdateFrequency     : 0
Reasons             : {
                      Tailspin.Tailspin.Ensure:
                        Checked existence of the TSToy configuration file in
                      the Machine scope.
                        Expected configuration file to exist at
                      'C:\ProgramData\TailSpinToys\tstoy\tstoy.config.json'.
                        The configuration file exists at
                      'C:\ProgramData\TailSpinToys\tstoy\tstoy.config.json'.
                      ,
                      Tailspin.Tailspin.UpdateAutomatically:
                        Checked value of the 'updates.automatic' key in the
                      TSToy configuration file.
                        Expected boolean value of 'True'
                        Actual boolean value of 'True'
                      }

{
  "updates": {
    "automatic": true
  }
}

Przegląd

W tym samouczku zostały wykonane następujące czynności:

  1. Szkielet modułu programu PowerShell i zaimplementowanie opartego Tailspin na klasie zasobu DSC
  2. Zdefiniowano właściwości zasobu DSC w celu zarządzania zachowaniem aktualizacji aplikacji TSToy na maszynie i w zakresach użytkownika z walidacją tych właściwości
  3. Zaimplementowano wyliczenia dla $Ensure właściwości i $ConfigurationScope
  4. Zaimplementowano klasę ExampleResourcesReason do raportowania, w jaki sposób zasób jest nieaktualny w konfiguracji maszyny
  5. Zaimplementowano metodę GetConfigurationFile() pomocnika, aby niezawodnie odnaleźć lokalizację konfiguracji aplikacji TSToy w zakresie komputera i użytkownika na różnych platformach
  6. Zaimplementowano metodę Get() w celu pobrania bieżącego stanu zasobu DSC, buforowania go do użycia w Test() metodach i Set()
  7. Zaimplementowano metodę GetReasons() pomocnika do sprawdzania, czy zasób DSC jest w żądanym stanie i, jeśli nie jest, wyliczając, jak jest poza stanem
  8. Zaimplementowano metodę Test() w celu zweryfikowania bieżącego stanu zachowania aktualizacji TSToy w określonym zakresie względem żądanego stanu
  9. Zaimplementowano metodę ToConfigJson , aby przekonwertować żądany stan zasobu DSC na obiekt JSON, którego aplikacja TSToy wymaga dla pliku konfiguracji, uwzględniając ustawienia niezarządzane
  10. Zaimplementowano metodę Set() i metody pomocnika Create, Remove i Update , aby idempotentnie wymusić żądany stan dla zachowania aktualizacji TSToy w określonym zakresie, zapewniając, że zasób DSC nie ma niepożądanych skutków ubocznych
  11. Ręcznie przetestowano typowe scenariusze użycia dla zasobu DSC

Na końcu implementacji definicja modułu wygląda następująco:

[DscResource()]
class Tailspin {
    [DscProperty(Key)] [TailspinScope]
    $ConfigurationScope

    [DscProperty()] [TailspinEnsure]
    $Ensure = [TailspinEnsure]::Present

    [DscProperty(Mandatory)] [bool]
    $UpdateAutomatically

    [DscProperty()] [int] [ValidateRange(1, 90)]
    $UpdateFrequency

    [DscProperty(NotConfigurable)] [ExampleResourcesReason[]]
    $Reasons

    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
            $CurrentState.Reasons = $this.GetReasons($CurrentState)

            $this.CachedCurrentState = $CurrentState

            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
        }

        $CurrentState.Reasons = $this.GetReasons($CurrentState)

        $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
        }
    }

    [ExampleResourcesReason[]] GetReasons([Tailspin]$CurrentState) {
        [ExampleResourcesReason[]]$DefinedReasons = @()

        $FilePath = $this.GetConfigurationFile()

        if ($this.Ensure -eq [TailspinEnsure]::Present) {
            $Expected = "Expected configuration file to exist at '$FilePath'."
        } else {
            $Expected = "Expected configuration file not to exist at '$FilePath'."
        }

        if ($CurrentState.Ensure -eq [TailspinEnsure]::Present) {
            $Actual = "The configuration file exists at '$FilePath'."
        } else {
            $Actual = "The configuration file was not found at '$FilePath'."
        }

        $DefinedReasons += [ExampleResourcesReason]@{
            Code   = "Tailspin.Tailspin.Ensure"
            Phrase = @(
                "Checked existence of the TSToy configuration file in the $($this.ConfigurationScope) scope."
                $Expected
                $Actual
            ) -join "`n`t"
        }

        if ($CurrentState.Ensure -ne $this.Ensure) {
            return $DefinedReasons
        }

        if ($CurrentState.Ensure -eq [TailspinEnsure]::Absent) {
            return $DefinedReasons
        }

        $DefinedReasons += [ExampleResourcesReason]@{
            Code   = "Tailspin.Tailspin.UpdateAutomatically"
            Phrase = (@(
                    "Checked value of the 'updates.automatic' key in the TSToy configuration file."
                    "Expected boolean value of '$($this.UpdateAutomatically)'"
                    "Actual boolean value of '$($CurrentState.UpdateAutomatically)'"
                ) -join "`n`t")
        }

        # Short-circuit the check; UpdateFrequency isn't defined by caller
        if ($this.UpdateFrequency -eq 0) {
            return $DefinedReasons
        }

        $DefinedReasons += [ExampleResourcesReason]@{
            Code   = "Tailspin.Tailspin.UpdateFrequency"
            Phrase = (@(
                    "Checked value of the 'updates.checkFrequency' key in the TSToy configuration file."
                    "Expected integer value of '$($this.UpdateFrequency)'."
                    "Actual integer value of '$($CurrentState.UpdateFrequency)'."
                ) -join "`n`t")
        }

        return $DefinedReasons
    }

    [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
}

class ExampleResourcesReason {
    [DscProperty()]
    [string] $Code

    [DscProperty()]
    [string] $Phrase

    [string] ToString() {
        return "`n$($this.Code):`n`t$($this.Phrase)`n"
    }
}

Czyszczenie

Jeśli nie zamierzasz nadal korzystać z tego modułu ExampleResources , usuń folder i pliki w nim.

Następne kroki

  1. Przeczytaj o zasobach DSC opartych na klasach, dowiedz się, jak działają, i zastanów się, dlaczego zasób DSC w tym samouczku jest implementowany w ten sposób.
  2. Przeczytaj o funkcji konfiguracji maszyny w usłudze Azure Automanage , aby dowiedzieć się, jak można jej używać do przeprowadzania inspekcji i konfigurowania systemów.
  3. Zastanów się, jak można ulepszyć ten zasób DSC. Czy istnieją przypadki brzegowe lub funkcje, których nie obsługuje? Zaktualizuj implementację, aby je obsłużyć.