Monday, 13 November 2017

Learn PowerShell DSC - Part 6

Introduction

In Part 5, started looking at DSC Pull using an SMB share. If you need a refresher, click here. In this part, we’ll run through the setup of HTTP/HTTPS pull which is a little more complex but makes life a little easier especially as it requires fewer firewall ports open and is more secure.

Other parts in this series:

What is DSC HTTP/HTTPS Pull?

HTTP/HTTPS pull allows you to store your DSC configurations (.MOF) on a web server which the target machines can connect to, download and apply their configurations. They do this over the standard ports: TCP port 80 for HTTP and TCP port 443 for HTTPS.

The setup steps we will run through are:

  1. Install the required DSC module on the DSC pull server
  2. Install a certificate on the DSC pull server
  3. Create a DSC configuration to deploy the DSC pull server
  4. Create a MOF file
  5. Deploy the DSC configuration to the DSC pull server

The steps are almost the same whether you want to set up an HTTP or an HTTPS pull server but there are some differences.

Set up a DSC HTTP/HTTPS pull server

1) Install the required DSC module on the DSC pull server

We need to use some of the DSC Resources which are available in the xPSDesiredStateConfiguration DSC module which is not included in Windows. To install this module, we run the command:

Install-Module xPSDesiredStateConfiguration

2) Install a certificate on the DSC pull server

The next step is to install a certificate on the HTTPS pull server. If you don’t need to have encrypted traffic and therefore don’t need to use HTTPS then you can skip this step. The certificate needs to have the correct Subject Name. Now, adding the certificate

In my case, my DSC pull server is called contchidsc01.contoso.com so this is what I need on my certificate. I’ll just confirm that contchidsc01 has a certificate with the correct subject:

Enter-PSSession contchidsc01
Get-ChildItem Cert:\LocalMachine\My\ | fl Thumbprint,Subject

image

We also need to note down the certificate thumbprint as we’ll need this in the next step. In my case it’s F8E20068359E75922F3EC35F58282C348D4511CF.

3) Create a DSC configuration to deploy the DSC pull server

The most interesting part! Deploying a DSC pull server requires a number of steps and so we’ll use DSC to configure the DSC pull server. The steps are below:

The full DSC configuration we need is below:

configuration HTTPSPullServer
    {
        Param (
            [Parameter(Mandatory = $true)]
            [string] $ComputerName,
            [ValidateNotNullOrEmpty()] 
            [string] $certificateThumbPrint,
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string] $RegistrationKey
        )

        Import-DSCResource -ModuleName xPSDesiredStateConfiguration
        Import-DSCResource -ModuleName PSDesiredStateConfiguration

        Node $ComputerName
            {
                WindowsFeature DSCServiceFeature
                    {
                        Ensure = "Present"
                        Name   = "DSC-Service"
                    }

                WindowsFeature IISConsole 
                    {
                        Ensure = "Present"
                        Name   = "Web-Mgmt-Console"
                    }

                xDscWebService PSDSCPullServer
                    {
                        Ensure                  = "Present"
                        EndpointName            = "PSDSCPullServer"
                        Port                    =  443
                        PhysicalPath            = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer"
                        CertificateThumbPrint   =  $certificateThumbPrint
                        ModulePath              = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules"
                        ConfigurationPath       = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration"
                        State                   = "Started"
                        UseSecurityBestPractices=  $false
                        DependsOn               = "[WindowsFeature]DSCServiceFeature"
                    }

                File RegistrationKeyFile
                    {
                        Ensure          = 'Present'
                        Type            = 'File'
                        DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt"
                        Contents        = $RegistrationKey
                    }
            }
    }

Let’s go through what this actually does.

This first part collects parameters for the ComputerName, certificateThumbPrint and the RegistrationKey when we run the configuration to create a MOF file:

        Param (
            [Parameter(Mandatory = $true)]
            [string] $ComputerName,
            [ValidateNotNullOrEmpty()] 
            [string] $certificateThumbPrint,
            [Parameter(Mandatory)]
            [ValidateNotNullOrEmpty()]
            [string] $RegistrationKey
        )

  • ComputerName = The name of the target machine which we will configure to be a DSC HTTP/HTTPS pull user
  • certificateThumbprint = The thumbprint of the certificate which we installed on the DSC pull server
  • RegistrationKey = A key which target machines will use to do the initial registration with the pull server. After this initial registration, the target machine will generate a self-signed certificate which will be used to authenticate with the pull server.

This next part installs the DSC Service and IIS Management Console Windows features we need installed on our pull server:

                WindowsFeature DSCServiceFeature
                    {
                        Ensure = "Present"
                        Name   = "DSC-Service"
                    }

                WindowsFeature IISConsole 
                    {
                        Ensure = "Present"
                        Name   = "Web-Mgmt-Console"
                    }

The next part configures the DSC Web Service so that it works as a pull server. We need to specify a number of parameters here:

  • Port = Port number to use for the pull server.
  • PhysicalPath = Path on disk for the virtual directory that will be set up in IIS.
  • CertificateThumbprint = The certificate thumbprint for the certificate we installed on the pull server. As we have a parameter for this, we’ll set this to $certificateThumbPrint in the configuration. If you don’t want to use HTTPS then you can set this value to “AllowUnencryptedTraffic” and the pull server will use HTTP.
  • ModulePath = The path where DSC modules will be stored. The target machines can download any required modules if they don’t already have these installed.
  • ConfigurationPath = The path where DSC configurations will be stored.

                xDscWebService PSDSCPullServer
                    {
                        Ensure                  = "Present"
                        EndpointName            = "PSDSCPullServer"
                        Port                    =  443
                        PhysicalPath            = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer"
                        CertificateThumbPrint   =  $certificateThumbPrint
                        ModulePath              = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules"
                        ConfigurationPath       = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration"
                        State                   = "Started"
                        UseSecurityBestPractices=  $false
                        DependsOn               = "[WindowsFeature]DSCServiceFeature"
                    }

The final part of the configuration is to store the registration key in a text file. We use the File DSC Resource to create a new text file and set the contents to $RegistrationKey

                File RegistrationKeyFile
                    {
                        Ensure          = 'Present'
                        Type            = 'File'
                        DestinationPath = "$env:ProgramFiles\WindowsPowerShell\DscService\RegistrationKeys.txt"
                        Contents        = $RegistrationKey
                    }

4) Generate the MOF file

To generate our MOF file, we need to specify our parameters:

  • ComputerName = contchidsc01
  • certificateThumbprint =  F8E20068359E75922F3EC35F58282C348D4511CF 
  • RegistrationKey =  (New-Guid).Guid

We then call our configuration, specify the output path and the parameters:

HTTPSPullServer -OutputPath C:\DSC\HTTPS -ComputerName contchidsc01 `
-certificateThumbPrint F8E20068359E75922F3EC35F58282C348D4511CF -RegistrationKey (New-Guid).Guid

5) Deploy the DSC configuration to the DSC pull server

The final step is to deploy the DSC configuration to the pull server using Start-DscConfiguration:

Start-DscConfiguration -Path C:\DSC\HTTPS -Verbose -Wait

If we use the -Verbose and -Wait parameters, we can see detailed output as below:

image

image

6) Test the configuration

Before moving on, let’s just test our configuration. We can do this using Test-DscConfiguration and we can see that contchidsc01 is in the desired state:

Test-DscConfiguration -Path C:\DSC\HTTPS

image

We can also see the PSDSCPullServer virtual directory in IIS:

image

….and we can confirm that our Registration Key was saved to the file we specified:

image

We’ll move on to configuring a target machine to use the new pull server.

Configure a target machine to use our DSC HTTPS pull server

We need to configure our target machine Local Configuration Manager (LCM) with the DSC pull server URL and the registration key so it can complete the initial registration.

We’ll be configuring our target machine contchich01. In this configuration, we’re specifying the settings below:

  • ConfigurationID: This is the ID of the LCM on the target machine and is used to find the correct configuration to apply as there may be many configurations for other machines on the same pull server
  • RefreshMode: This sets our LCM to pull instead of push which is the default
  • ConfigurationRepositoryWeb ServerURL: This is the URL of the pull server
  • ReportServerWeb ServerURL: This is the URL of the pull server
  • RegistrationKey: We specify this for both the report server and the configuration repository so the target machine can register against the pull server

[DSCLocalConfigurationManager()]
configuration ConfigurePullClient
{
     Param (
        [Parameter(Mandatory = $true)]
        [string] $ComputerName,
        [Parameter(Mandatory = $true)]
        [string] $RegistrationKey,
        [Parameter(Mandatory = $true)] 
        [string] $GUID
     )

    Node $ComputerName
    {
        Settings
        {
            RefreshMode          = 'Pull'
            ConfigurationID = $guid
        }

        ConfigurationRepositoryWeb CONTOSO-PullSrv 
        {
            ServerURL          = 'https://contchidsc01.contoso.com/PSDSCPullServer.svc/'
            RegistrationKey    = $RegistrationKey
        }  

        ReportServerWeb CONTOSO-PullSrv 
        {
            ServerURL       = 'https://contchidsc01.contoso.com/PSDSCPullServer.svc/'
            RegistrationKey =  $RegistrationKey
        }
    }
}

Once we have the LCM configuration, we need to push this out specifying the RegistrationKey, ComputerName and a GUID for the ConfigurationID:

$RegistrationKey = Get-Content '\\contchidsc01\c$\Program Files\WindowsPowerShell\DscService\RegistrationKeys.txt'

$ComputerName = "contchich01"
$GUID = (New-Guid).Guid
ConfigurePullClient -ComputerName $ComputerName -RegistrationKey $RegistrationKey -OutputPath C:\DSC\Configs -GUID $GUID

Next, we need to set the LCM using Set-DscLocalConfigurationManager:

Set-DscLocalConfigurationManager -ComputerName $ComputerName -Path C:\DSC\Configs -Verbose

image

We then run these commands to confirm the configuration:

$Settings = Get-DscLocalConfigurationManager -CimSession $ComputerName 
$Settings | fl PSComputerName,RefreshMode,RefreshFrequencyMins,RebootNodeIfNeeded,ConfigurationID
$Settings.ConfigurationDownloadManagers[0]

image

As you see above, we have the LCM set to pull and the ServerURL set to our new pull server.

Create a DSC configuration for an HTTPS pull target machine

This is basically the same as creating a normal DSC configuration but instead of naming the mof file according to our target machine i.e. contchich01.mof, we need to name it using the ConfigurationID of the LCM. The simple configuration is below and this creates a test file on the C drive called testfile.txt:

Configuration TestDSCPull {
   
    Param (
    [Parameter(Mandatory=$true)]
    [string]$ComputerName
    )
   
    Import-DscResource -ModuleName PSDesiredStateConfiguration

    Node $ComputerName {
   
        File TestFile {

            DestinationPath = 'C:\testfile.txt'
            Type = 'File'
            Ensure = 'Present'
            Contents = 'Test file contents'
            }
        }

}

Create your MOF file as normal:

$ComputerName = "contchich01"
TestDSCPull -ComputerName $ComputerName -OutputPath C:\DSC\Configs

In the next section, we get the ConfigurationID from the target machine and then copy the MOF file over to the configuration repository on the DSC pull server and rename it to <guid>.mof:

$guid=(Get-DscLocalConfigurationManager -CimSession $ComputerName).ConfigurationID
$DestinationFile = '\\contchidsc01\c$\Program Files\WindowsPowerShell\DscService\Configuration\' + $guid + '.mof'
copy C:\DSC\Configs\$ComputerName.mof $DestinationFile -Force

We also need a checksum file so create one using New-DscChecksum (use -Force to overwrite a checksum if there is already one there):

New-DscChecksum $DestinationFile -Force

To get the machine to pull the configuration, we then use Update-DscConfiguration:

Update-DscConfiguration -ComputerName $ComputerName -Wait -Verbose

image

…..and there we have it, the target machine pulled the configuration and we can confirm the test file exists and that the contents are correct:

image

Conclusion

If you’ve made it this far then take a break - you deserve it! It takes a little bit of time to get it all set up but once you’re done, all your servers can be configured to automatically pull their configuration from the pull server and you have a central repository for your configurations so all you have to do is create configurations.

Monday, 23 October 2017

Learn PowerShell DSC - Part 5

Introduction

Welcome to part 5! Hopefully you’re getting the hang of pushing out DSC configurations and are wondering if there are other ways to deploy DSC configurations and in fact, you’re in luck! Today’s we’ll go through how you can get the target machine to automatically pull the DSC configuration rather than you having to push it out.

There are two methods you can use to pull DSC configurations - SMB and HTTP/HTTPS. We’ll go through the simplest one first - SMB pull.

Other parts in this series:

What is DSC SMB Pull?

SMB Pull allows you to store your DSC configurations (.MOF) in an SMB share which the target machines then connect to, download and apply their configurations. There are three steps we will go through:

  • Set up a DSC SMB Pull Server
  • Configure target machine for DSC SMB Pull
  • Create DSC configuration for SMB pull target machine

Once done, we basically create a DSC configuration, target it at the target machine and also create a checksum file. We’ll go through this in more detail later on.

Set up a DSC SMB Pull Server

Now, this is easy. Just create a share and provide read access to the computer account of the target machine. This is because DSC runs as the Local System account by default.

1. Create a folder e.g. C:\DSCSMB

2. Share the folder as DSCSMB and grant full control to Everyone:

image

3. Configure the NTFS permissions by disabling inheritance, removing permissions for Users and granting read only permissions for the target machine computer account or an AD group which contains the computer accounts.As I’m just doing a demo, I’ll assign permissions to “Contoso\Domain Computers”.

image

Configure target machine for DSC SMB Pull

By default, DSC is configured for push configurations only and obviously it has no idea that we have an SMB Pull server or what its UNC path is so we’ll need to configure this. The DSC local configuration is configured using the the DSC Local Configuration Manager or LCM for short. The LCM is a WMI provider built into PowerShell v4 and later.

To configure DSC using the LCM, you basically create a configuration specifying [DSCLocalconfigurationManager()] and then you can create a MOF file, and then push this out to your target server using Set-DscLocalConfigurationManager.

1. Create the LCM configuration as below. Note that we’re specifying the parameters:

    • ConfigurationID: This is the ID of the LCM on the target machine and is used to find the correct configuration to apply as there may be many configurations for other machines in the same share
    • RefreshMode: This sets out LCM to pull instead of push which is the default
    • SourcePath: This is the UNC path of our DSC SMB Pull server

[DSCLocalconfigurationManager()]
Configuration Configure_LCM_SMBPULL 
{
    param
        (
            [Parameter(Mandatory=$true)] 
            [string[]]$ComputerName,

            [Parameter(Mandatory=$true)]
            [string]$guid 
        )
        
Node $ComputerName
{
Settings {
            RefreshMode = 'Pull' 
            ConfigurationID = $guid
        }
          
ConfigurationRepositoryShare DSCSMB {
            Sourcepath = "\\contchidsc01\DSCSMB" 
        }  
}
}

2. Specify the computer name and the GUID. Here we’re creating a new GUID.

$ComputerName='contchisql01'
$guid=[guid]::NewGuid()

3. Create the MOF file however it’s a .meta.mof file for the LCM

Configure_LCM_SMBPULL -ComputerName $ComputerName -Guid $guid -OutputPath C:\Scripts\DSC

image

4. We now configure the LCM on the target machine using Set-DscLocalConfigurationManager

Set-DscLocalConfigurationManager -ComputerName $ComputerName   -Path C:\Scripts\DSC -Verbose

image

5. Our LCM should now be configured and we can use Get-DscLocalConfigurationManager to confirm our settings:

$a = Get-DscLocalConfigurationManager -CimSession $ComputerName 
$a | ft PSComputerName,RefreshMode,ConfigurationID,`
@{Name='ConfigurationRepositoryShare';Expression=`
{[string]$_.ConfigurationDownloadManagers.SourcePath}}

image

We can see that RefreshMode is set to Pull, a ConfigurationID is assigned and the ConfigurationRepositoryShare to our DSC SMB Pull server UNC path. Great! Our next task is to create a configuration for the target server and store it in the SMB share.

Create DSC configuration for SMB pull target machine

This is basically the same as creating a normal DSC configuration but instead of naming the mof file contchisql01.mof, we need to name it using the ConfigurationID of the target machine LCM i.e. c46a4a4b-8b5f-49e5-90f4-faaf80e2ec9f.mof. We also need to create a checksum file called c46a4a4b-8b5f-49e5-90f4-faaf80e2ec9f.mof.checksum.

1. Create your configuration as normal. See my example one below which basically just creates a text file C:\testfile1.txt and sets the contents to My test file

Configuration CreateTestFile {
   
    Param (
    [Parameter(Mandatory=$true)]
    [string]$ComputerName
    )
   
    Import-DscResource -ModuleName PSDesiredStateConfiguration

   
    Node $ComputerName {
   
        File CreateTestFile {
            Type = 'File'
            DestinationPath = 'C:\testfile1.txt'
            Contents = 'My test file'
            }
        }

}

2. Specify the computer name and create your MOF file

$ComputerName = "contchisql01"
CreateTestFile -ComputerName $ComputerName -OutputPath C:\Scripts\DSC

image

3. Get the LCM ConfigurationID and copy the mof file to the SMB share with a name <ConfigurationID>.mof

$guid=(Get-DscLocalConfigurationManager -CimSession $ComputerName).ConfigurationID
$DestinationFile = 'C:\DSCSMB\' + $guid + '.mof'
Copy-Item C:\Scripts\DSC\$ComputerName.mof $DestinationFile -Force

image

4. Create a checksum file in the SMB share: <ConfigurationID>.mof.checksum

New-DscChecksum $DestinationFile -Force

image

5. Now you can either wait 30mins for the target machine to apply the configuration or you can force it using Update-DscConfiguration

Update-DscConfiguration -ComputerName $ComputerName -Wait -Verbose

image

6. That looks successful so let’s confirm our file is there and the contents are set:

Get-Content '\\contchisql01\c$\testfile1.txt'

image

There you have it - our test file is created!

Conclusion

In this post, we configured an SMB pull server then configured the LCM on a target machine to use the pull server. We then created a configuration for the target machine and put this in the SMB share and confirmed the target machine can pull it and apply it.

In the next post, we’ll look at HTTP pull servers.

Wednesday, 4 October 2017

VSCode - Keep focus on editor

In VSCode, you’ll find that when you run your script using F8 or F5, your focus will shift to the terminal which may not be what you want especially as ISE doesn’t change the focus.

To change this behaviour, just click on the settings icon and go to settings:

image

Then add the below line into the user settings window on the right:

"powershell.integratedConsole.focusConsoleOnExecute": false

It should now look like this:

image

Close User Settings and save your changes and there you go.

Monday, 18 September 2017

Create a PowerShell Module

Introduction

When you build up your own library of functions, it’s really useful to have them always available on your machine without you having to load script files etc. This is where creating your own PowerShell module comes in handy. You can have all your functions available in this module and just take it around wherever you go.

Today, I’m going to write a simple function and then create my own module which includes this function.

How to build a PowerShell module

So, this is really not as hard as it seems. The basic steps are below:

  1. Create your functions in a single .psm1 file
  2. Copy your .psm1 file into one of the PowerShell module folders
  3. Create a module manifest

1 - Create your functions and save the file as .psm1

I’ve created three simple functions below:

function Get-LoggedOnUser
    {
        ($env:USERDOMAIN).ToLower() + "\" + $env:USERNAME
    }

function List-Process 
    {
        Get-Process -IncludeUserName
    }

function List-MyProcesses
    {
        List-Process | ? {$_.UserName -eq (Get-LoggedOnUser)}
    }

List-MyProcesses

Get-LoggedOnUser just gets the user name. List-Process gets a list of all processes and List-MyProcesses filters the processes using the logged on user details.

We only want to have the function List-MyProcesses available to users who use the module and we’ll configure this when we create our module manifest in step 3.

2 - Copy your .psm1 file

You can store your psm1 file in one of three locations by default in Server 2016:

  • C:\Users\Administrator.contoso\Documents\WindowsPowerShell\Modules
  • C:\Program Files\WindowsPowerShell\Modules
  • C:\Windows\system32\WindowsPowerShell\v1.0\Modules

…..and you can confirm these are the locations by running the command below:

$env:PSModulePath -split ";"

image

We now need to make a folder for our module and give it a name. In this case, we will call our module ProcessTroubleshooting and we’ll save it in a module folder.

Make a new folder: C:\Program Files\WindowsPowerShell\Modules\ProcessTroubleshooting

Save your PowerShell file as C:\Program Files\WindowsPowerShell\Modules\ProcessTroubleshooting\ProcessTroubleshooting.psm1

Note the psm1 extension. This is the convention for creating modules.

3 - Create a module manifest

We now need a module manifest so that our module can be loaded automatically and so that PowerShell knows which functions to provide to the user. To create a module manifest, you need to use the New-ModuleManifest cmdlet. The module manifest file is a .psd1 file.

New-ModuleManifest -Path 'C:\Program Files\WindowsPowerShell\Modules\ProcessTroubleshooting\ProcessTroubleshooting.psd1' `
-RootModule 'C:\Program Files\WindowsPowerShell\Modules\ProcessTroubleshooting\ProcessTroubleshooting.psm1' `
-Author "Mark Gossa" `
-FunctionsToExport List-MyProcesses

We now have a module manifest which includes information about our module and very importantly which functions to export and allow the user to access. The FunctionsToExport parameter lists which functions we want to provide to users, i.e. which functions we want to export. It’s all nice and simple really.

You should now be able to import your module and use all the functions you’ve exported. In Server 2012 and later, you don’t need to even import it - Windows Server just imports it as you call one of the functions. See below for our function in action:

image

So, there we have it - that's how you create PowerShell modules but manual steps I hear you say? Well, you can just write your own function that will package up your module for you and create the manifest and you can even save that function in another module.

Happy scripting!