Requirements, a PowerShell idempotent and declarative system configuration framework
Tags: idempotence, powershell, iac, devops
Published: 2019-06-28 18:00:49

---
title:  Requirements, a PowerShell idempotent and declarative system configuration framework  
published: true
description: Unlike PowerShell DSC, Requirement is configuration that can work with the Windows dependency
tags: Idempotence, PowerShell, IaC , DevOps 
---


In a DevOps world, concerning configuration management and Infrastructure as code, there are two import notions.   
Idempotence, the property of an operation that can be applied several times but only change the state to the desired state only on the initial run.   
Declarative is a programming paradigm where you say what is the desired state and not (only) how to do it.   

One of my friends, FX [@lazywinadmin](https://twitter.com/lazywinadmin), pointed me to a new framework, Requirements. It’s a new configuration management framework made with PowerShell Core. 

Requirements define a state with a collection of requirements. The collection is used to implement the desired state. 

A requirement can be a script or a DSC resource (for PowerShell 5.x) and is composed by:  


* Name, the name of the requirement. It must be unique in a collection  
* Describe, a description of the requirement such as create a text file or change the mine/type on IIS Website  
* DependsOn, an array containing names of other requirements needed before setting this one  

For a script requirement, you also need   

* Test, the test to determine if the requirement is in the desired state or not. The test script needs to return a Boolean, $true or $false  
* Set, the script to put the requirement in the desired state  

For a DSC requirement, you will need  

* ResourceName, the name of the DSC resource used in the requirement  
* ModuleName, the name of the module used by the resource. Requirements do not manage module, the module needs to be present on the machine, but you can use a script requirement to perform this task  
* Property, un hashtable representing the configuration  

A requirement looks like that   

```powershell
@{  

  Name = "File-Add"  
  
  Describe = "Add test.txt file"  

  Test = {   
    test-path -Path "c:\test.txt"  
  }  
  Set = {   
     new-item -Path "c:\test.txt"  
  }  
}  
```

And as you need a collection of requirements you need to use an array   


```powershell
$requirements = @(  
    @{…} ,
    @{…}  
)  
```

You can also create a requirement object by using New-Requirement cmdlet with the same parameters  

To start the configuration simply pass the requirement array object by pipeline to the invoke-Requirements  

```powershell
$MyRequirementsObjectList | invoke-requirements   
``` 

You can also use   

```powershell
Invoke-requirements –Requirements $MyRequirementsObjectList   
```

You can also use the Test-Requirements cmdlet to only see if everything is in the desired state.  

Let’s look a deeper example. The first one will create a file and set the content in the current directory. This script work with Windows PowerShell and PowerShell Core on Linux or Windows   

```powershell
$ErrorActionPreference = "Stop"
Import-Module Requirements

$PathSeparator = [IO.Path]::DirectorySeparatorChar
$TextContent = "This is a script from Requirements"

$ScriptRequirements = @(
    @{
        Name     = "File script"
        Describe = "File script.txt is present in $PSScriptRoot"
        Test     = { 
            test-path -Path "$($PSScriptRoot)$($PathSeparator)script.txt"
         }
        Set      = { 
            new-item -Path "$($PSScriptRoot)$($PathSeparator)script.txt" | Out-Null 
        }
    },
    @{
        Name     = "Set content in script"
        Describe = "Ensure script.txt contain This is a script from Requirements"
        Test     = { 
            $fileContent = Get-Content -Path "$($PSScriptRoot)$($PathSeparator)script.txt"
            $fileContent -eq $TextContent
         }
        Set      = { 
            set-content -Path "$($PSScriptRoot)$($PathSeparator)script.txt" -Value $TextContent
        }
        DependsOn = @("File script")
    }
)

$ScriptRequirements | Invoke-Requirement | Format-table
``` 

Note the 

```powershell
$PathSeparator = [IO.Path]::DirectorySeparatorChar  
```

I use it to deal with the Path separator difference on Linux and Windows so I don’t have to care where I run my script.   

  

The second script does exactly the same things, create a file and set up its content but with DSC so it works on Windows PowerShell.   

```powershell
$ErrorActionPreference = "Stop"
Import-Module Requirements

$file = "$($PSScriptRoot)\dsc.txt"
$TextContent = "This is DSC from Requirements"

$requirement = @{
    Name = "Dsc"
    Describe     = "Dsc Requirement"
    ResourceName = "File"
    ModuleName   = "PSDesiredStateConfiguration"
    Property     = @{
        Contents        = $TextContent
        DestinationPath = $file
        Force           = $true
    }
}
New-Requirement @requirement | Invoke-Requirement
```

Note that I use the second construction for requirements with New-Requirement to create the object.   

The Requirements module work with PowerShell core (on Linux and on Windows) and with Windows PowerShell 5.1. The module is present in the PowerShell Gallery.   

To install it   

```powershell
Install-module –name Requirement –Scope CurentUser   
```

The project is located on [GitHub](https://github.com/microsoft/Requirements) and it's still under development