Remove an iSCSI Target Portal with PowerShell

I ran into a small problem with removing iSCSI Target Portals using PowerShell the other day and thought it might be worth documenting.

Pretend you have an iSCSI Target Portal configured with a Target Portal Address of 192.168.129.24:

ss_iscsi_gettargetportal

You might therefore expect that you could remove this Target Portal with the command:

Remove-IscsiTargetPortal -TargetPortalAddress 192.168.129.24

Unfortunately this won’t work:

ss_iscsi_removetargetportal1

And neither does this:

ss_iscsi_removetargetportal2

What you actually have to do is specify both the Target Portal Address and the Initiator Portal Address when deleting an iSCSI Target Portal:

Remove-IscsiTargetPortal -TargetPortalAddress 192.168.129.24 -InitiatorPortalAddress 192.168.129.30

ss_iscsi_removetargetportalcorrect

Over and out.

 

WMF 5.0 Download Removed from Download Center

Bit of a bump in the WMF 5.0 road today: Microsoft has removed the WMF 5.0 RTM download from the download center because of a significant bug:

We recently released Windows Management Framework (WMF) 5.0 RTM delivering many requested improvements and fixes, via the Microsoft Download Center as announced in a previous blog post. However, we have discovered a bug which resets the PowerShell module environment during installation. As this issue can have a serious impact on our customers, we are taking the action to stop delivery of WMF 5.0 RTM, and have removed the packages from the Download Center. Additionally, we will be unpublishing Azure DSC Extension Handler versions 2.11 and 2.12 as they automatically install WMF 5.0 RTM.

So if you’re planning on rolling WMF5.0 out to production, best hold off for the moment. See the original blog post here.

 

Creating Professional DSC Resources – Part 6

The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing Microsoft Community DSC resources. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, unit and integration tests (don’t worry if you aren’t familiar with those terms).

These articles are also not intended to tell you what you must do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the development community, but may be quite new to operations people.

If you missed any previous articles you can find them here:

 

Recap

In the last couple of articles I covered the importance of automated testing and covered unit testing in particular (I’ll get to integration testing later). I had covered creating new unit tests using the unit test templates that are available here (although they will probably move here). I also covered how to complete the Pester Test Initialization and the Get-TargetResource and Set-TargetResource function areas of the unit test.

 

Unit Testing Completion

The final task in completing the unit tests is to complete the Set-TargetResource  in tests and also optionally tests for any other supporting functions your DSC Resource may have required.

In these unit tests I am using a DSC Resource for creating iSCSI Virtual Disks to illustrate the process. You don’t need to know anything about iSCSI Virtual Disks to understand these articles or resources, but if you’re interested to know the cmdlets I’m using for these, see this page. I’m using the *_iSCSIVirtualDisk cmdlets in this DSC Resource.

Function Test-TargetResource

This area will contain the actual Pester tests that test the Test-TargetResource function. These are fairly similar to the Set-TargetResource except we will be checking these two things:

  1. The output of the Test-TargetFunction is correct. E.g. it returns false if changes are required, which will cause Set-TargetFunction to be called.
  2. The expected Mocks are called by the Function.

This area may contain a large number of tests depending on the complexity of your DSC Resource. In most cases, you should expect there to create the tests from the following list, but often you will need even more for 100% code coverage:

  • Does the function return false when the resource being configured does exist and should, but one of the configured parameters does not match the current values? This test is usually repeated for each parameter in the DSC Resource.
  • Does the function return true when the resource being configured does exist and should, and all the configured parameters match the current values?
  • Does the function return false when the resource being configured does not exist but should?
  • Does the function return false when the resource being configured does exist but should not?
  • Does the function return true when the resource being configured does not exist and should not?

The bottom four of these tests are very similar. So I’ll only show examples of the top two contexts here.

Context ‘Virtual Disk exists and should but has a different …’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return the object we defined in the Pester Test Initialization section. This is the behavior we’d expect if the resource being configured does exist:

Context 'Virtual Disk exists and should but has a different Description' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
It 'should return false' {
{
$Splat = $TestVirtualDisk.Clone()
$Splat.Description = 'Different'
Test-TargetResource @Splat | Should Be $False
} | Should Not Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}

This context will perform two tests:

  1. Should return false – The Test-TargetResource should return false because we are changing the Description parameter so that the resource will require changes (e.g. Set-TargetResource should be called).
  2. Should call the expected mocks – The Test-TargetResource should call the mocked cmdlets the expected number of times. In all contexts in this function this will always be just once.

The purpose of cloning the $TestVirtualDisk object is so we can modify the properties to simulate a property difference without modifying the $TestVirtualDisk object.

You should expect to repeat this context for each parameter that might be different.

 

Context ‘Virtual Disk does not exist but should’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return nothing. This is the behavior we’d expect if the resource being configured does not exist:

Context 'Virtual Disk does not exist but should' {
Mock Get-iSCSIVirtualDisk
It 'should return false' {
$Splat = $TestVirtualDisk.Clone()
Test-TargetResource @Splat | Should Be $False
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}

As you can see, there is not too much different with these tests and you shouldn’t have any problems figuring out the remaining ones. Just remember, the goal is always to get 100% code coverage.

 

Unit Testing Supporting Functions

It is quite common that you might have implemented some supporting functions in your DSC Resource. These supporting functions are usually called by your standard *-TargetResource functions. If that is the case there are two important things you should do:

  1. Write unit tests that cover all code paths in your supporting functions.
  2. Add mocks to your *-TargetResource unit tests that prevent any constructive/destructive cmdlets that exist in your supporting functions from being called.

The first item is fairly self explanatory. For example, I often implement a get-* function in my DSC Resources which is used to pull the actual objects that will be used by the *-TargetResource functions (e.g. Get-VirtualDisk):

Function Get-VirtualDisk {
param
(
[parameter(Mandatory = $true)]
[System.String]
$Path
)
try
{
$VirtualDisk = Get-iSCSIVirtualDisk `
-Path $Path `
-ErrorAction Stop
}
catch [Microsoft.Iscsi.Target.Commands.IscsiCmdException]
{
$VirtualDisk = $null
}
catch
{
Throw $_
}
Return $VirtualDisk
}

To unit test this function I’d write unit tests that tested the following contexts:

  1. Context ‘Virtual Disk does not exist’
  2. Context ‘Virtual Disk does exist’

For example:

Describe "$($Global:DSCResourceName)\Get-VirtualDisk" {
Context 'Virtual Disk does not exist' {
Mock Get-iSCSIVirtualDisk
It 'should return null' {
$Splat = $TestVirtualDisk.Clone()
$Result = Get-VirtualDisk -Path $Splat.Path
$Result | Should Be $null
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}
Context 'Virtual Disk does exist' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
It 'should return expected parameters' {
$Splat = $TestVirtualDisk.Clone()
$Result = Get-VirtualDisk -Path $Splat.Path
$Result.Path | Should Be $MockVirtualDisk.Path
$Result.DiskType | Should Be $MockVirtualDisk.DiskType
$Result.Size | Should Be $MockVirtualDisk.Size
$Result.Description | Should Be $MockVirtualDisk.Description
$Result.ParentPath | Should Be $MockVirtualDisk.ParentPath
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}
}

As you can see, there isn’t much to it.

Earn a Chocolate Fish: If you look at the above supporting function and unit tests carefully, you’ll notice that I haven’t got 100% code coverage on it!

To get 100% code coverage I would have had to implement a unit test that covered the situation where the Get-iSCSIVirtualDisk function threw an exception that wasn’t a [Microsoft.Iscsi.Target.Commands.IscsiCmdException] exception.

In case you’re wondering, the Get-iSCSIVirtualDisk function throws a [Microsoft.Iscsi.Target.Commands.IscsiCmdException] when the cmdlet is called with the path parameter set to a path that does not contain a valid iSCSI Virtual Hard Disk file.

 

Unit Testing Exceptions

When creating unit tests you’ll often need to test a scenario where the function that is being tested is expected to throw an exception. If you read the Pester documentation, you’d might write a test for an exception like this:

Context 'Virtual Disk exists and should but has a different ParentPath' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
It 'should throw an exception' {
{ Test-TargetResource @Splat } | Should Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}

This would of course will work. It will ensure that the code throws an exception in this situation. The problem is we aren’t really sure if it is the exception that we expected it to throw. It could have been thrown by some other part of our code.

So to improve on this we need to do things:

  1. Customize the exception that is thrown.
  2. Change the unit test so that it checks for the customized exception.

 

Customize the Exception

To create a custom exception we need to create a new exception object containing our custom error message. The exception object is then used to create a custom Error Record:

$errorId = 'iSCSIVirtualDiskRequiresRecreateError'
$errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
$errorMessage = $($LocalizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Path
$exception = New-Object -TypeName System.InvalidOperationException `
-ArgumentList $errorMessage
$errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
-ArgumentList $exception, $errorId, $errorCategory, $null
$PSCmdlet.ThrowTerminatingError($errorRecord)

In the above code, you just need to customize the $errorId and $errorMessage variables. The $errorId should just contain a simply string identifier for this particular type of error, but the $errorMessage can contain a full description of the error, including related parameters.

Once you’ve created the $errorRecord object you can call the ThrowTerminatingError method of the $PSCmdLet object, passing the $errorRecord object as the parameter.

Important: the $PSCmdLet object is only available in Functions that include the [CmdletBinding()] function attribute. So ensure your *-TargetResource and supporting functions include this attribute if you want to be able to access this object.

 

Test for the Customized Exception

To test for the custom exception object we need to create an identical object in the unit test and test for it:

Context 'Virtual Disk exists and should but has a different ParentPath' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
It 'should throw an iSCSIVirtualDiskRequiresRecreateError exception' {
$Splat = $TestVirtualDisk.Clone()
$Splat.ParentPath = 'c:\NewParent.vhdx'
$errorId = 'iSCSIVirtualDiskRequiresRecreateError'
$errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument
$errorMessage = $($LocalizedData.iSCSIVirtualDiskRequiresRecreateError) -f $Splat.Path
$exception = New-Object -TypeName System.InvalidOperationException `
-ArgumentList $errorMessage
$errorRecord = New-Object -TypeName System.Management.Automation.ErrorRecord `
-ArgumentList $exception, $errorId, $errorCategory, $null
{ Test-TargetResource @Splat } | Should Throw $errorRecord
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}

The above code creates an identical exception object to the one produced by the exception in our DSC Resource code. The exception object can then be passed to the should throw cmdlet. If a different exception is thrown by the code then the test will fail – it will only pass if the exception object is exactly the same.

Important: Make sure both the $errorId and $errorMessage variables are exactly the same as what would be produced by the code when your unit test calls it. This includes ensuring that if your $errorMessage contains any parameters that the unit test $errorMessage contains the same parameter values.

 

That about completes creating unit tests. After you’ve implemented a few unit tests you’ll no doubt come up with your own method of implementing them, but hopefully this has given you a place to start.

 

Up Next – Integration Tests

In the next article, I’ll cover the integration tests. There are often the most difficult to implement, but if you can take the time to implement them then your DSC Resources are guaranteed to be extremely robust and bugs are far less likely to slip through.

Further parts in this series:

Creating Professional DSC Resources – Part 5

The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing Microsoft Community DSC resources. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, unit and integration tests (don’t worry if you aren’t familiar with those terms).

These articles are also not intended to tell you what you must do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the development community, but may be quite new to operations people.

If you missed any previous articles you can find them here:

 

Recap

Yesterday I talked about  the importance of automated testing and covered unit testing in particular (I’ll get to integration testing later). I had covered creating new unit tests using the unit test templates that are available here (although they will probably move here). I also covered how to complete the Pester Test Initialization and the Function Get-TargetResource areas of the unit test.

 

Unit Testing Continued

The next task in completing the unit tests is to complete the Set-TargetResource area in the unit test.

In these unit tests I am using a DSC Resource for creating iSCSI Virtual Disks to illustrate the process. You don’t need to know anything about iSCSI Virtual Disks to understand these articles or resources, but if you’re interested to know the cmdlets I’m using for these, see this page. I’m using the *_iSCSIVirtualDisk cmdlets in this DSC Resource.

Function Set-TargetResource

This area will contain the actual Pester tests that test the Set-TargetResource function. Unlike the Get-TargetResource this area may contain a large number of tests depending on the complexity of your DSC Resource. In most cases, you should expect there to create the tests from the following list, but often you will need even more for 100% code coverage:

  • Does each parameter get set/updated correctly when the resource being configured does exist and should? This test is usually repeated for each parameter in the DSC Resource.
  • Does it work when the resource being configured does not exist but should?
  • Does it work when the resource being configured does exist but should not?
  • Does it work when the resource being configured does not exist and should not?

Context ‘Virtual Disk exists and should but has a different …’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return the object we defined in the Pester Test Initialization section. This is the behavior we’d expect if the resource being configured does exist.

We are also going to Mock the Set-iSCSIVirtualDisk, New-iSCSIVirtualDisk and Remove-iSCSIVirtualDisk. This is so we can ensure the expected cmdlets are called as well as preventing the real cmdlets from being run:

Context 'Virtual Disk exists and should but has a different Description' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
Mock New-iSCSIVirtualDisk
Mock Set-iSCSIVirtualDisk
Mock Remove-iSCSIVirtualDisk
It 'should not throw error' {
{
$Splat = $TestVirtualDisk.Clone()
$Splat.Description = 'Different'
Set-TargetResource @Splat
} | Should Not Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName New-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Set-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName Remove-iSCSIVirtualDisk -Exactly 0
}
}

This context will perform two tests:

  1. Should not throw error – The Set-TargetResource should not throw an error when called in this context.
  2. Should call the expected mocks – The Set-TargetResource should call the mocked cmdlets the expected number of times.

The purpose of cloning the $TestVirtualDisk object is so we can modify the properties to simulate a property difference without modifying the $TestVirtualDisk object.

It is also important to ensure that we are not only checking that the expected cmdlets are called, but also that the other cmdlets in this function are not called. This is why we are checking the New-iSCSIVirtualDisk and Remove-iSCSIVirtualDisk are being called zero times.

You should expect to repeat this context for each parameter that might be updated.

Note: It is possible that updating some parameters may not be possible because of limitations in the underlying cmdlets. In this case I like to throw an exception so that the user is made aware that they are configuring a scenarios that can not be performed. In that case the test would be to ensure the correct exception occurs. I’ll cover testing exceptions in a later article.

 

Context ‘Virtual Disk does not exist but should’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return nothing. This is the behavior we’d expect if the resource being configured does not exist.

We are also going to Mock the Set-iSCSIVirtualDisk, New-iSCSIVirtualDisk and Remove-iSCSIVirtualDisk. This is so we can ensure the expected cmdlets are called as well as preventing the real cmdlets from being run:

Context 'Virtual Disk does not exist but should' {
Mock Get-iSCSIVirtualDisk
Mock New-iSCSIVirtualDisk
Mock Set-iSCSIVirtualDisk
Mock Remove-iSCSIVirtualDisk
It 'should not throw error' {
{
$Splat = $TestVirtualDisk.Clone()
Set-TargetResource @Splat
} | Should Not Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName New-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName Set-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Remove-iSCSIVirtualDisk -Exactly 0
}
}

The context tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected Mocks will be different.

 

Context ‘Virtual Disk exists but should not’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return the object we defined in the Pester Test Initialization section. This is the behavior we’d expect if the resource being configured does exist.

We are also going to Mock the Set-iSCSIVirtualDisk, New-iSCSIVirtualDisk and Remove-iSCSIVirtualDisk. This is so we can ensure the expected cmdlets are called as well as preventing the real cmdlets from being run:

Context 'Virtual Disk exists but should not' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($MockVirtualDisk) }
Mock New-iSCSIVirtualDisk
Mock Set-iSCSIVirtualDisk
Mock Remove-iSCSIVirtualDisk
It 'should not throw error' {
{
$Splat = $TestVirtualDisk.Clone()
$Splat.Ensure = 'Absent'
Set-TargetResource @Splat
} | Should Not Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName New-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Set-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Remove-iSCSIVirtualDisk -Exactly 1
}
}

The context tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected Mocks will be different.

 

Context ‘Virtual Disk does not exist and should not’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return the object we defined in the Pester Test Initialization section. This is the behavior we’d expect if the resource being configured does not exist.

We are also going to Mock the Set-iSCSIVirtualDisk, New-iSCSIVirtualDisk and Remove-iSCSIVirtualDisk. This is so we can ensure the expected cmdlets are called as well as preventing the real cmdlets from being run:

Context 'Virtual Disk does not exist and should not' {
Mock Get-iSCSIVirtualDisk
Mock New-iSCSIVirtualDisk
Mock Set-iSCSIVirtualDisk
Mock Remove-iSCSIVirtualDisk
It 'should not throw error' {
{
$Splat = $TestVirtualDisk.Clone()
$Splat.Ensure = 'Absent'
Set-TargetResource @Splat
} | Should Not Throw
}
It 'should call expected Mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
Assert-MockCalled -commandName New-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Set-iSCSIVirtualDisk -Exactly 0
Assert-MockCalled -commandName Remove-iSCSIVirtualDisk -Exactly 0
}
}

The context tests are very similar to all the other tests so I won’t go into detail on them here. It is important to note that the expected Mocks will be different.

 

Unit Tests to be Continued…

In the next article, I’ll cover the unit tests for Get-TargetResource as well as unit testing any additional functions. Thanks again for reading and I hope it is useful.

Further parts in this series:

 

Markdown Preview in Visual Studio Code

I used to keep around a copy of Markdown Pad to enable me to preview any markdown files I created. However, after an update to a Awesomium SDK Markdown Pad stopped working. I reported the issue but it hasn’t been resolved yet.

But after Irwin Strachan mentioned using Visual Studio Code as a Markdown editor, I wondered if there was an extension that allowed it to Preview the Markdown as well – because that was really all that was missing for me to do away with Markdown Pad. So I went and did a search for Markdown extensions in VS Code:

ss_vscode_markdownextensions

Want to install extensions in VS Code? Press F1 and type ‘ext install’.

I found a few, but after playing around with them a bit I noticed a small button in the latest version of Visual Studio Code at the top right corner when a markdown file is open:

ss_vscode_previewbutton

The Preview button!

I wonder what that might do?

ss_vscode_preview

That is a great looking markdown preview.

Oh, now look at that! Just what I needed. I don’t know which version of Visual Studio Code this was added in, but it is fantastic!

You can also use the Visual Studio Code split window function to edit and Preview at the same time:

ss_vscode_splitpreview

Changing the markdown automatically updates the preview – nice!

While we’re on the topic of markdown, Irwin also pointed me at this awesome markdown tutorial – it makes learning markdown fun!

Now, Visual Studio Code is not on par with ISE Steroids when it comes to editing PowerShell files, but when you’re dealing with lots of different types of files (including PS files) it can be a real time saver. I highly recommend downloading a copy of Visual Studio Code, installing the PowerShell Extension and giving it a try.

 

Creating Professional DSC Resources – Part 4

The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing Microsoft Community DSC resources. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, unit and integration tests (don’t worry if you aren’t familiar with those terms).

These articles are also not intended to tell you what you must do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the development community, but may be quite new to operations people.

If you missed any previous articles you can find them here:

 

Automated Testing

Automated testing is something that is familiar to most developers, but for operations people it is usually a new concept. However, it is one of the most important things you can add to your DSC Resources – and most DSC resource projects won’t even accept your code contributions if they don’t contain automated tests.

So, what are automated tests? Well, they are just PowerShell scripts that you create and run that will check your DSC Resource is working correctly. Usually automated tests are run on your DSC Resources every time you commit your code – and they’ll tell you if anything has gone wrong. I could spend the next day listing reasons why automated testing is extremely important, but that is not the purpose for this post.

PowerShell contains a great automated test framework called Pester that allows you to describe your tests using special PowerShell functions.

Important: If you aren’t familiar with Pester and automated testing, you should get familiar with it before reading any further. This series is a fantastic place to start. Even if you’re familiar with Pester it is a good read.

An example of a Pester test on a DSC Resource:

Describe 'MSFT_xFirewall\Get-TargetResource' {
Context 'Absent should return correctly' {
Mock Get-NetFirewallRule
It "Should return absent on firewall rule $($FirewallRule.Name)" {
$result = Get-TargetResource -Name 'FirewallRule'
$result.Name | Should Be 'FirewallRule'
$result.Ensure | Should Be 'Absent'
}
}
Context 'Present should return correctly' {
$result = Get-TargetResource -Name $FirewallRule.Name
# Looping these tests
foreach ($parameter in $ParameterList)
{
$ParameterSource = (Invoke-Expression -Command "`$($($parameter.source))")
$ParameterNew = (Invoke-Expression -Command "`$result.$($parameter.name)")
It "should have the correct $($parameter.Name) on firewall rule $($FirewallRule.Name)" {
$ParameterSource | Should Be $ParameterNew
}
}
}
}

The above test is a unit test of the xFirewall resource in the xNetworking module. the Don’t worry if you don’t completely understand this yet, that is the purpose of this article – although you should understand the basic structure of the Pester test – if you don’t, you’ll definitely want to go and review this series.

 

Types of Automated Tests

There are two types of automated tests you should create for your DSC Resources:

  • Unit Tests – these test that each function in your DSC Resource works correctly in isolation. This means that if you call the function with a set of parameters you get expected output.
  • Integration Tests – these tests ensure that your DSC Resource works in a real environment – e.g. work correctly when they are actually integrated into a DSC Configuration file.

For every DSC Resource in your DSC Resource Module you should ensure that there is one unit test file and one integration test file (although usually for integration tests a support file is also needed, but we’ll cover this later).

 

Test Folders

You should place all tests inside a Tests folder in the root of your DSC Module folder:

ss_dsc_testfolders

Unit tests should be placed in a Unit folder within Tests and … I’m sure you get where I’m going here.

 

Unit Tests

Unit tests are a good place to start when creating automated tests for your DSC Resources because they’re u sually quite straight forward. Each DSC Resource in your DSC Module should contain it’s own unit test file. For example, here are the unit tests that are included with the xNetworking DSC Resource module:

ss_dsc_xnetworkingunittests

A note about code coverage: A unit test script will contain lots of individual tests. The purpose of each individual unit test is usually to test a single path through a function that the computer might take when running that function. The goal therefore is to add as many tests as needed to make sure each code path is covered. This is called code coverage. The goal is to have 100% code coverage for your DSC Resource.

In these unit tests I am using a DSC Resource for creating iSCSI Virtual Disks to illustrate the process. You don’t need to know anything about iSCSI Virtual Disks to understand these articles or resources, but if you’re interested to know the cmdlets I’m using for these, see this page. I’m using the *_iSCSIVirtualDisk cmdlets in this DSC Resource.

 

Creating a New Unit Test

You could just go ahead and create a new unit from scratch, but there is a much easier way. There is a unit test template file that you can use to create new unit tests. You need to copy the unit test template file and rename it to match your DSC Resource.

The easiest way to do this is to clone the repository containing the test template files and copy the unit_template.ps1 file to your Tests/Unit folder:

git clone https://github.com/PowerShell/DSCResources.git
Copy-Item .\DSCResources\Tests.Template\unit_template.ps1 .\ciSCSI\Tests\Unit\BMD_ciSCSIVirtualDisk.Tests.ps1

ss_dsc_createnewunittestfromtemplate

You’ll now have a new unit test file that you can open in your PowerShell editor of choice:

ss_dsc_editingnewunittest

Next, customize the TODO area in the header with the your DSC Resource Name and DSC Module Name:

ss_dsc_customizeunittestheader

Feel free to remove the TODO comments if you want (I always do).

Now, onto the job of actually adding our Pester tests. You’ll want to locate the Pester Tests region:

ss_dsc_unittestspestertestsregion

This is usually the only area of code in this file you need edit. There are several areas you need to customize here:

  1. Pester Test Initialization – this is where you would define any variables or objects that you might use for testing.
  2. Function Get-TargetResource – this would contain the Pester Tests to test the Get-TargetResource function.
  3. Function Test-TargetResource – this would contain the Pester Tests to test the Test-TargetResource function.
  4. Function Set-TargetResource – this would contain the Pester Tests to test the Set-TargetResource function.
  5. Pester Tests for any Helper Cmdlets – you would add Describe blocks for any additional functions you have defined in your resource to ensure those functions are tested.

Once there sections are completed for a DSC Resource the tests can be invoked using the Invoke-Pester command.

But first I’ll provide more detail on what you should add into each section.

 

Pester Test Initialization

The purpose of this section is to initialize any variables or objects that you’re going to use for testing.

For example, if you were testing a resource that was for creating an iSCSI Virtual Disk you might define the parameters of the iSCSI Virtual Disk that you’re going to use for testing:

# Create the Mock Objects that will be used for running tests
$TestVirtualDisk = [PSObject]@{
Path = Join-Path -Path $ENV:Temp -ChildPath 'TestiSCSIVirtualDisk.vhdx'
Ensure = 'Present'
DiskType = 'Differencing'
SizeBytes = 100MB
Description = 'Unit Test iSCSI Virtual Disk'
BlockSizeBytes = 2MB
PhysicalSectorSizeBytes = 4096
LogicalSectorSizeBytes = 512
ParentPath = 'c:\Parent.vhdx'
}
$MockVirtualDisk = [PSObject]@{
Path = $TestVirtualDisk.Path
DiskType = $TestVirtualDisk.DiskType
Size = $TestVirtualDisk.SizeBytes
Description = $TestVirtualDisk.Description
ParentPath = $TestVirtualDisk.ParentPath
}

The $TestVirtualDisk object is used to @splat onto the Set-TargetResource and Test-TargetResource functions and for comparison. It saves us lots of typing and therefore potential mistakes.

The $MockVirtualDisk object is used as output to the Mocks that get defined on the Get-iSCSIVirtualDisk cmdlet when we want to simulate the scenario where the iSCSI Virtual Disk already exists.

You don’t of course have to do this, but you’ll find that having some objects defined up front that you can use as test objects will make your code smaller and easier to understand as well as making the tests run faster.

 

Function Get-TargetResource

This area will contain the actual Pester tests that test the Get-TargetResource function. Usually there are only a small number of tests you can perform in this function:

  • Does it work when the resource being configured does not exist?
  • Does it work and return the expected parameters when the resource being configured does exist?

For example:

Describe "$($Global:DSCResourceName)\Get-TargetResource" {
Context 'Virtual Disk does not exist' {
Mock Get-iSCSIVirtualDisk
It 'should return absent Virtual Disk' {
$Result = Get-TargetResource `
-Path $TestVirtualDisk.Path
$Result.Ensure | Should Be 'Absent'
}
It 'should call the expected mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}
Context 'Virtual Disk does exist' {
Mock Get-iSCSIVirtualDisk -MockWith { return @($TestVirtualDisk) }
It 'should return correct Virtual Disk' {
$Result = Get-TargetResource `
-Path $TestVirtualDisk.Path
$Result.Ensure | Should Be 'Present'
$Result.Path | Should Be $TestVirtualDisk.Path
$Result.DiskType | Should Be $TestVirtualDisk.DiskType
$Result.SizeBytes | Should Be $TestVirtualDisk.SizeBytes
$Result.Description | Should Be $TestVirtualDisk.Description
$Result.PhysicalSectorSizeBytes | Should Be $TestVirtualDisk.PhysicalSectorSizeBytes
$Result.LogicalSectorSizeBytes | Should Be $TestVirtualDisk.LogicalSectorSizeBytes
}
It 'should call the expected mocks' {
Assert-MockCalled -commandName Get-iSCSIVirtualDisk -Exactly 1
}
}
}

In the above code we have two Context blocks – one for each of our scenarios above.

Context ‘Virtual Disk does not exist’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return nothing. This is the behavior we’d expect if the resource being configured didn’t exist.

We can now check the object returned is what is expected and that the Get-iSCSIVirtualDisk was called the expected number of times – in this case once.

Context ‘Virtual Disk does exist’

In this scenario we Mock the Get-iSCSIVirtualDisk cmdlet to return the object we defined in the Pester Test Initialization section. This is the behavior we’d expect if the resource being configured does exist.

We can therefore check the object returned is what is expected and all the returned object properties match and that the Get-iSCSIVirtualDisk was called the expected number of times – again this is once.

 

Unit Tests to be Continued…

I wanted to put all the unit test info in a single part of this series, but as you can see it is getting quite long. I’ll complete the rest of the information on unit tests tomorrow.

So in the next article, I’ll cover the unit tests for Set-TargetResource and Get-TargetResource as well as unit testing any additional functions. Thanks again for reading and I hope it is useful.

Further parts in this series:

 

Creating Professional DSC Resources – Part 3

The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing Microsoft Community DSC resources. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, unit and integration tests (don’t worry if you aren’t familiar with those terms).

These articles are also not intended to tell you what you must do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the development community, but may be quite new to operations people.

If you missed any previous articles you can find them here:

 

Coding Style

Everyone has there own preferences of how they like their code to look. Just take a look at all the PowerShell repositories on GitHub and you’d see a lot of different coding styles.

When I refer to coding style, this covers a many different things, for example:

  • Variable name format
  • Tab format (spaces or tabs?)
  • Function name format
  • Maximum line length
  • Comments
  • Curly braces allowed at the end of a line
  • Allow more than one blank line in a row

If you’re writing code that only you will look at and maintain, it doesn’t much matter what your coding style is – as long as you can understand it 6 months later. However, if you would like other people to use and possibly  contribute and maintain your code or if you are contributing to community project, you’ll want to adopt a standard coding style.

The best reason for adopting a standard coding style is to ensure consistency in your code – especially across DSC resources within the same module. This makes it much easier for people to become familiar with your coding style and therefore easier for them to read and understand.

If you don’t have a particular coding style you have adopted, Microsoft has released a simple style guidelines document for DSC Resources that you can adopt quite easily.

Tip: Using the Microsoft style guidelines document is a requirement for contributing code to the Microsoft Community DSC Resources. So if you’re planning on contributing code or even entire resource modules to this project, I’d recommend you adopt the Microsoft style guidelines.

 

Message Localization

Adding support for language localization to your DSC Resources is trivial, but it is often over looked. Supporting data localization is much easier to add when you are creating your DSC Resource rather than adding it later on. This doesn’t mean you have to provide the language files of course, just provide support so that someone else could contribute them should they want to.

To ensure language support, create a new file in the same folder as your DSC Resource with the same name as the DSC Resource but with a psd1 extension. In that file  create a Data section named LocalizedData containing the messages for your default culture. For example:

data LocalizedData
{
# culture="en-US"
ConvertFrom-StringData -StringData @'
GettingiSCSIVirtualDiskMessage=Getting iSCSI Virtual Disk "{0}".
iSCSIVirtualDiskExistsMessage=iSCSI Virtual Disk "{0}" exists.
iSCSIVirtualDiskDoesNotExistMessage=iSCSI Virtual Disk "{0}" does not exist.
SettingiSCSIVirtualDiskMessage=Setting iSCSI Virtual Disk "{0}".
EnsureiSCSIVirtualDiskExistsMessage=Ensuring iSCSI Virtual Disk "{0}" exists.
EnsureiSCSIVirtualDiskDoesNotExistMessage=Ensuring iSCSI Virtual Disk "{0}" does not exist.
iSCSIVirtualDiskCreatedMessage=iSCSI Virtual Disk "{0}" has been created.
iSCSIVirtualDiskUpdatedMessage=iSCSI Virtual Disk "{0}" has been updated.
iSCSIVirtualDiskRemovedMessage=iSCSI Virtual Disk "{0}" has been removed.
TestingiSCSIVirtualDiskMessage=Testing iSCSI Virtual Disk "{0}".
iSCSIVirtualDiskParameterNeedsUpdateMessage=iSCSI Virtual Disk "{0}" {1} is different. Change required.
iSCSIVirtualDiskDoesNotExistAndShouldNotMessage=iSCSI Virtual Disk "{0}" does not exist and should not. Change not required.
iSCSIVirtualDiskRequiresRecreateError=iSCSI Virtual Disk "{0}" needs to be deleted and recreated. Please perform this manually.
'@
}

Each line contains a localized message – in this case for the culture en-us. You could of course use a different default culture if you wanted to.

At the beginning of your DSC Resource you would the following command to ensure the appropriate localization strings are imported:

Import-LocalizedData -BindingVariable LocalizedData -Filename BMD_cMyNewResource.psd1

Alternately, if you want to support Message Localization but don’t want to have to supply your default messages in a separate file, you can place the LocalizedData section for your default culture at the top of your DSC Resource and exclude the Import-LocalizedData command.

 

Using Localization Messages

Once you’ve got the messages in, using them is extremely easy:

Write-Verbose -Message ($LocalizedData.iSCSIVirtualDiskExistsMessage -f $Path)

You can of course consume the messages anyway you like, but all of your localized messages are just properties of the LocalizedData object.

 

Other Localization Files

You put LocalizedData for other languages in separate PowerShell files in sub-folders named after the culture the file contains. For example, you might create a sub-folder called en-uk and place a file called BMD_cMyNewResource.psd1 containing the en-uk messages.

 

Example DSC Configuration Files

Another element of any easy to use DSC Resource are example DSC Configuration files. These can usually be found in the Examples folder in the root of the DSC Module:

ss_dsc_examplesfolder

There should usually be a number of different DSC Configuration files in this folder, showing common scenarios for using your DSC Resources:

ss_dsc_examples

The file name of any example files should be prefixed with Sample or Example so that they can be easily identified and differentiated from types of DSC Module files. The summary of the purpose of the configuration should also be included in the file name. This is fairly obvious I realize, but I have seen public DSC Resources named Example_1, Example_2, Example_3 etc – which reduces usability of the examples.

Each example should contain a DSC Configuration in a form that it could be used without any modification to actually test the resource. This might include installing prerequisite Windows Features or starting services etc. This allows a potential user to test drive the resource without investing a whole lot of work trying to figure out how to use it.

For example:

configuration Sample_ciSCSIInitiator
{
Param
(
[String] $NodeName = 'LocalHost'
)
Import-DscResource -Module ciSCSI
Node $NodeName
{
Service iSCSIService
{
Name = 'MSiSCSI'
StartupType = 'Automatic'
State = 'Running'
}
ciSCSITargetPortal iSCSITargetPortal
{
Ensure = 'Present'
TargetPortalAddress = '192.168.128.10'
InitiatorPortalAddress = '192.168.128.20'
DependsOn = "[WindowsFeature]iSCSIService"
} # End of ciSCSITargetPortal Resource
ciSCSITarget iSCSITarget
{
Ensure = 'Present'
NodeAddress = 'iqn.1991-05.com.microsoft:fileserver01-cluster-target'
TargetPortalAddress = '192.168.128.10'
InitiatorPortalAddress = '192.168.128.20'
IsPersistent = $true
DependsOn = "[ciSCSITargetPortal]iSCSITargetPortal"
} # End of ciSCSITarget Resource
} # End of Node
} # End of Configuration

The above DSC Resource example will ensure the MSiSCSI service is running before configuring an iSCSI Initiator.

Tip: Make the name of the configuration the same as the sample configuration file (without the extension of course).

It is also a great idea to copy the content of any example DSC Configuration files into the Examples section of the Readme.md of your DSC Resource Module along with a brief description of what the configuration will produce. You’ll find that all Microsoft Community DSC Resources do this.

 

In The Next Article

I intended on covering creating unit and integration tests in this article, but as that is by far the most involved part of creating a community DSC resource I’ve decided I’ll dedicate an entire part to each. So, rest assured the next ones will contain this very important component of any public DSC Resource. Thank you for reading this far and I hope you’re finding it useful.

Further parts in this series:

Creating Professional DSC Resources – Part 2

The purpose of this series of articles is to try and document a few of the lessons I learned while releasing new DSC resources as well as contributing to the existing Microsoft Community DSC resources. These articles are not intended to tell you how to write DSC resources from a programming perspective, but to give you some ideas on what might be expected of a DSC resource you’re releasing to the public. For example, unit and integration tests (don’t worry if you aren’t familiar with those terms).

These articles are also not intended to tell you what you must do to release your resource, but more document what will help your resource be easier to use and extend by other people. Some of these these things are obvious for people who have come from the development community, but may be quite new to operations people.

If you missed any previous articles you can find them here:

Before You Start Coding

So, you have an idea or need for a set of super new DSC Resources. Before you write a single line of code you should first go and take a look at the documentation provided by the DSC Community. These guys (many of them from Microsoft) have been doing this stuff for a while and they’ve come up with a set of best practices and instructions on how to get started.

The above GitHub repository should be your first port of call and for DSC creation and it is worth keeping an eye on this repository by watching it:

ss_github_watch

This will cause you to be notified whenever any changes to this repository are made (which isn’t that often). So if the best practices are updated you’ll be kept in the loop!

This repository also contains some template files you can use to create a new DSC Resource module.

Creating the Resource Module

Sure, there is no reason why you can’t just jump straight in and knock out a PSD1 file and some PSM1/MOF files and be done with it. But creating a resource that other people can easily use, usually requires a few other files.

First, you need to decide on the name for the DSC Resource module folder. This is usually simple enough, but if you think your module may contain more than one resource it is worth naming it with a more generic name. For example, if you were creating a DSC Resource module that will contain resources for configuring iSCSI Targets and iSCSI Initiators you might name your folder ciSCSI.

Tip: Your resource folder should begin with a lower case c. This indicates it is a Community DSC resource. This is not a requirement, but it tells people that the resource was created by the community (in this case you). DSC Resource modules starting with an x indicate that this is a Microsoft Community DSC resource and maintained by the PowerShell team as well as the community, but are not built in to DSC.

Once you’ve created the folder to store your new DSC Resource module, you should make a copy of all the files in the GitHub repository folder found here to the root of your new DSC Resource folder:

ss_dsc_newresourcefolder

The easiest way to get a copy of these files is to use Git to clone the DSCResource repository on your computer and then copy the files from the DSCResource.Template folder to your new DSC module folder:

 

The DSCResource.Template Files

At the time of writing this the DSCResource.Template folder only contains two files:

  • Readme.md – tells people how to use your DSC Resource as well as containing usage examples and Version information. You should fill this in as soon as possible and keep it up-to-date everytime you change your DSC Resource.
  • AppVeyor.yml – this is a file that configures the AppVeyor Continuous Integration (CI) for your DSC Resource Module. I will cover AppVeyor CI later in the series. At the moment don’t worry about this file, just copy it to your module folder and leave it alone.

 

Markdown

The readme.md file (like most other files with an .md file extension) is text file in the Markdown format. Because this file is being made available up on GitHub you can use GitHub Flavored Markdown. Markdown is really easy to create straight in your editor of choice and you’ll find it in use all over GitHub, so it is a good idea to get somewhat familiar with it (it should only take you about 2 minutes to get a handle on it).

If you want an example of how your Readme.md file might be constructed, have a look at this example. Of course you are completely free to format it any way you like, but keeping to a standard makes it easy for users to know what they can expect.

 

In The Next Article

As I’m trying to keep these parts short I’ll break for today and continue tomorrow. In the next part I intend to cover code guidelines and examples, with unit and integration testing to follow. I hope you have found this interesting!

Further parts in this series:

Creating Professional DSC Resources -Part 1

Introduction

Writing desired state configuration (DSC) resources can be a little bit tricky at first if you’ve not come from a programming background. But once you’ve written one or two, you’ll quickly find yourself churning them out like Disney releases Star Wars branded rubbish. That is how it went for me.

However, I quickly found that there is a big difference between writing a simple resource for your own consumption and releasing a community DSC resource that may be used and abused by tens, hundreds or even thousands of people. After I decided to release my first resource (this one) to the public, I quickly found that it really wasn’t measuring up to what was being released by Microsoft and other folk in the community. So I spent about 6 months releasing and re-releasing my resources as I got to know the best practices.

So that gets us to the purpose of this post: to try and document the lessons I learned over the last year creating my own DSC resources and contributing to the Microsoft Community DSC resources. Hopefully by doing this it might save someone else a bit of time and avoid the joys or re-writing your resources.

Tip: The best practices for creating DSC resources change quite often as better ways of doing things are discovered it is useful to keep and eye on some of the more active DSC resources (like this one) in the Microsoft Community DSC resources. Even since I started contributing the processes and best practices have changed quite a lot.

Where to Start

If have no idea what Desired State Configuration (DSC) is, then this is a good place to start.

If you’ve not created a custom DSC resource before, then you should first get to know the basic process before reading this guide. There are many really great tutorials on creating DSC resources.

If you have written a resource or two or are familiar with the process, but you want to know how to go about releasing your custom DSC resource to the community then this post might interest you. So read on!

Does it Already Exist?

Before even getting started writing a custom DSC Resource it is a good idea to see if it already exists. It might not be included in the standard DS resources or in the Microsoft Community DSC Resources, but someone else may have already created one that will work for you. So why re-invent the wheel?

The best places to look for DSC resources is to search on PowerShell Gallery (you’ll find the Microsoft Community DSC Resources here as well). If you can’t find anything that looks like it’ll work for you, then you could also do a search on GitHub.

If you have WMF 5.0 installed (or have installed the PowerShell Package Modules installed) then you can search the PowerShell Gallery for DSC resources using PowerShell cmdlets:

Find-Module -Tag DSC

ss_powershell_findmoduletagdsc

Note: this will only find modules tagged with DSC. Some resources may not be tagged with this so they will not appear in this search. Therefore you may need to tweak the search.

If you find a resource you want to examine, you can install it using:

Install-Module -Name cFSRM

Or if you’re not sure you want to trust it you can use the save-module cmdlet to save a copy to a folder so you can open and examine the resource code:

Save-Module -Name cFSRM -Path c:\temp

If you find the DSC resource module else where you’ll need to download and install it manually – the same way install any other module.

Modifying an Existing Resource

If you find something that is close to what you need, but is missing some parameter or functionality, rather than starting a whole new resource you could ask the maintainer if they would be able to add the feature for you. Or even better, you could ask the maintainer if they’d object to you making the change and submitting it back to them via a Pull Request. Even if they’re not interested including your change, at least you can modify their resource adding the functionality you need – which will save you a lot of time. But in my experience, most maintainers are grateful for the assistance and welcome new features (as long as they go along with the resources overall design).

In fact, I’ve learned more from helping out on the community resources than from writing my own. If you have some spare time and want to help the community out, I’d strongly recommend helping out on the Microsoft Community DSC Resources. Just head over there, take a look at a resource you’re interested in then have a look at the issues for the resource and you’re bound to find a request or two for additional features. If you don’t find a request for a new feature but you see that one is missing that you’d like to see implemented, raise an issue yourself and offer to create it.

At the time I wrote this post there were some big holes in the functionality of most of the existing DSC community resources. The xDNSServer resource is a prime example.

Tip: one of the best ways to learn how to write professional quality resources is to look at some of the Microsoft Community DSC Resources. I’d strongly recommend the xNetworking resource (no, not because I contributed to it a bit) because it is very active and when best practices are updated this tends to be one of the first resources to get updated.

Source Control

So you’ve decided you’re going to write a new DSC resource (or modify an existing one). First thing you’ll need to be a little bit familiar with is Source Control. This used to be mainly the realm of developers, but it should now be a key tool in the Operations toolbox. I’m going to assume that you are a little bit familiar with Git and GitHub for this series.

If you’re not familiar with using Git and GitHub, this is the article you should read. It is written by the DSC Community and covers the basics, all from a DSC perspective. Even if you are familiar with Git and GitHub but haven’t used it for DSC resources, this document is worth a read.

Here is a summary of the Source Control things you should already know/have done:

  1. You should have a GitHub account (it’s free). This is the most likely place you’ll be storing your DSC Resources, and you’ll find the other community ones here too.
  2. You should have downloaded and installed the Git client, GitHub Desktop client or be using a tool like Microsoft Code that has a Git client built in.
  3. Ideally you should be familiar with what Forks, Branches and a Pull Requests are. There are some pretty amazing guides out there that will tell you everything you need to know in about 10 minutes.
  4. Decided if you’re going to create a new resource or fork someone elses and modify it.

Gitter: Asking Questions

Something I didn’t find out till many months after I started to contribute was that there is a Gitter chat channel where you can chat directly with some of the many of the people who contribute to the Microsoft Community DSC Resources. It is a great place to lurk and just see what is going on. If you want to contribute to a resource but you’re not sure what, if you ask, I’m sure you’ll receive lots of suggestions as to what needs some work. You’ll learn a lot just watching the discussion and if you need some help it’s a great place to ask.

Tip: If you have a GitHub account, you can sign in to Gitter with it.

What Next?

So, that is pretty the introduction over with. In the next article in this series I’m going to cover what you should do when you’re going to start a DSC Resource from scratch – one that you intend on releasing to the community. The final part of the series I’ll cover adding features to an existing Microsoft Community DSC Resource.

Hopefully this is of interest to a few out there and it doesn’t scare off any potential DSC contributors. It really is a great feeling to contribute back to the community.

Further parts in this series: