Desired State Configuration (DSC) for Windows

Desired State Configuration (DSC) for Windows

I had an ansible role to install certain items from chocolatey in a windows server machine. Due to a change in the situation and requirement, there was a need to look at a different alternative to install on the windows server. That's when, I stumbled upon Powershell DSC and I had not used it before. It satisfied the requirement and that's when I went ahead with it.

What is Powershell DSC?

Powershell Desired State Configuration (DSC) is an infrastructure automation tool - used for Infrastructure as a Code (Iaac)

he Desired State Configuration (DSC) is a management platform in a PowerShell that is used for development, configuration, and management of systems. It is used to get to get the specific inventory from the servers if they exist or not.

PowerShell and DSC both are different things. However, DSC can be implemented using PowerShell.

There is also Azure DSC for cloud as infrastructure code.

Now, DSC has to maintain the state of the remote server, which it does with the help of Local Configuration Manager (LCM)

Local Configuration Manager (LCM)

LCM engine acts as a bridge between the declared configuration and the remote server. It controls all the executions of the DSC Configurations.

For instance, in some cases (particularly in the pull method here), LCM polls and checks if the remote servers are in the desired state, if not, LCM calls the configuration to make them in the desired state.

Using DSC for Windows

To create and run DSC configurations on Windows, you need to create a configuration MOF document. The `Configuration' keyword is used to create the configuration.

First, there is a need to install the built-in modules containing the DSC resources

Install-Module 'cChoco' -Verbose -Force
Install-Module 'PSDesiredStateConfiguration' -Verbose -Force
Find-DscResource xRemoteFile | Install-Module -Force -Verbose

Then you define the configuration as per your requirement

Configuration DscConfig
{
   Import-DscResource -Module cChoco
   Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
   Import-DscResource -ModuleName xPSDesiredStateConfiguration
   Node "localhost"
   {
      LocalConfigurationManager
      {
          DebugMode = 'ForceModuleImport'
      }
      cChocoInstaller installChoco
      {
        InstallDir = "c:\choco"
      }
      cChocoPackageInstaller installPrometheus
      {
        Name     = "prometheus-windows-exporter.install"
        Version  = "0.18.1"
        DependsOn   = "[cChocoInstaller]installChoco"
      }
      cChocoPackageInstaller installFilebeat
      {
        Name     = "filebeat"
        Version  = "7.15.1"
        DependsOn   = "[cChocoInstaller]installChoco"
      }

        xRemoteFile FilebeatFileDownload{
         URI = "https://nexus.cloudsys.com/repository/ansible/dsc/filebeat.yml"                
         DestinationPath = "C:\Install\Files\filebeat.yml"
      }

        xRemoteFile PrometheusFileDownload{
         URI = "https://nexus.cloudsys.com/repository/ansible/dsc/windows-exporter.yml"                
         DestinationPath = "C:\Install\Files\windows-exporter.yml"
      }

        #Windows-Exporter Install
        msiexec /i "C:\Windows\Temp\chocolatey\prometheus-windows-exporter.install\0.18.1\windows_exporter-0.18.1-amd64.msi" --% EXTRA_FLAGS="--config.file=C:\Install\Files\windows-exporter.yml"

        #Filebeat setup
        Archive UnzipFilebeat {
        Ensure = "Present"
        Path = "C:\Windows\Temp\chocolatey\filebeat\7.15.1\filebeat-7.15.1-windows-x86_64.zip"
        Destination = "C:\Program Files"
        }

        Script RenameFile
        {
        SetScript = { Rename-Item 'C:\Program Files\filebeat-7.15.1-windows-x86_64' 'C:\Program Files\Filebeat' }
        TestScript = { $false } 
        GetScript = { $true }
        }

        Script FilebeatService
        {
        SetScript = { 
              # Delete and stop the service if it already exists.
        if (Get-Service filebeat -ErrorAction SilentlyContinue) {
        $service = Get-WmiObject -Class Win32_Service -Filter "name='filebeat'"
        $service.StopService()
        Start-Sleep -s 1
        $service.delete()
        }

        # Create the new service.
        New-Service -name filebeat `
        -displayName Filebeat `
        -binaryPathName "`"C:\Program Files\Filebeat\filebeat.exe`" --environment=windows_service -c `"C:\Install\Files\filebeat.yml`" --path.home `"C:\Program Files\Filebeat`" --path.data `"$env:PROGRAMDATA\filebeat`" --path.logs `"$env:PROGRAMDATA\filebeat\logs`" -E logging.files.redirect_stderr=true"

        # Attempt to set the service to delayed start using sc config.
        Try {
        Start-Process -FilePath sc.exe -ArgumentList 'config filebeat start= delayed-auto'
        }
        Catch { Write-Host -f red "An error occured setting the service to delayed start." }

         }
        TestScript = { $false } 
        GetScript = { $true }

    }

      }

   }

Here, you import the installed modules having the DSC resource. A node is defined which is basically the server where you want the configuration steps to take place.

Then, you can apply Configuration documents (MOF files) to a machine with the Start-DscConfiguration cmdlet.

Start-DscConfiguration .\DscConfig -wait -Verbose -force

The following is the complete powershell script using DSC. It downloads the items on the machine and installs it using a custom config file and make the required changes in the machines for the downloaded items to work.

Install-Module 'cChoco' -Verbose -Force
Install-Module 'PSDesiredStateConfiguration' -Verbose -Force
Find-DscResource xRemoteFile | Install-Module -Force -Verbose

Configuration DscConfig
{
   Import-DscResource -Module cChoco
   Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
   Import-DscResource -ModuleName xPSDesiredStateConfiguration
   Node "localhost"
   {
      LocalConfigurationManager
      {
          DebugMode = 'ForceModuleImport'
      }
      cChocoInstaller installChoco
      {
        InstallDir = "c:\choco"
      }
      cChocoPackageInstaller installPrometheus
      {
        Name     = "prometheus-windows-exporter.install"
        Version  = "0.18.1"
        DependsOn   = "[cChocoInstaller]installChoco"
      }
      cChocoPackageInstaller installFilebeat
      {
        Name     = "filebeat"
        Version  = "7.15.1"
        DependsOn   = "[cChocoInstaller]installChoco"
      }

        xRemoteFile FilebeatFileDownload{
         URI = "https://nexus.cloudsys.com/repository/ansible/dsc/filebeat.yml"                
         DestinationPath = "C:\Install\Files\filebeat.yml"
      }

        xRemoteFile PrometheusFileDownload{
         URI = "https://nexus.cloudsys.com/repository/ansible/dsc/windows-exporter.yml"                
         DestinationPath = "C:\Install\Files\windows-exporter.yml"
      }

        #Windows-Exporter Install
        msiexec /i "C:\Windows\Temp\chocolatey\prometheus-windows-exporter.install\0.18.1\windows_exporter-0.18.1-amd64.msi" --% EXTRA_FLAGS="--config.file=C:\Install\Files\windows-exporter.yml"

        #Filebeat setup
        Archive UnzipFilebeat {
        Ensure = "Present"
        Path = "C:\Windows\Temp\chocolatey\filebeat\7.15.1\filebeat-7.15.1-windows-x86_64.zip"
        Destination = "C:\Program Files"
        }

        Script RenameFile
        {
        SetScript = { Rename-Item 'C:\Program Files\filebeat-7.15.1-windows-x86_64' 'C:\Program Files\Filebeat' }
        TestScript = { $false } 
        GetScript = { $true }
        }

        Script FilebeatService
        {
        SetScript = { 
              # Delete and stop the service if it already exists.
        if (Get-Service filebeat -ErrorAction SilentlyContinue) {
        $service = Get-WmiObject -Class Win32_Service -Filter "name='filebeat'"
        $service.StopService()
        Start-Sleep -s 1
        $service.delete()
        }

        # Create the new service.
        New-Service -name filebeat `
        -displayName Filebeat `
        -binaryPathName "`"C:\Program Files\Filebeat\filebeat.exe`" --environment=windows_service -c `"C:\Install\Files\filebeat.yml`" --path.home `"C:\Program Files\Filebeat`" --path.data `"$env:PROGRAMDATA\filebeat`" --path.logs `"$env:PROGRAMDATA\filebeat\logs`" -E logging.files.redirect_stderr=true"

        # Attempt to set the service to delayed start using sc config.
        Try {
        Start-Process -FilePath sc.exe -ArgumentList 'config filebeat start= delayed-auto'
        }
        Catch { Write-Host -f red "An error occured setting the service to delayed start." }

         }
        TestScript = { $false } 
        GetScript = { $true }

    }

      }

   }


DscConfig

Start-DscConfiguration .\DscConfig -wait -Verbose -force

Although DSC is a very large topic, but it has been summarized here with the needed concepts to get a gist of it. Hope you find it useful!

Link to the Github Repo: https://github.com/AnvaySingh/Powershell/blob/main/DSC-For-Windows.ps1