Permalink
Find file
1307 lines (1004 sloc) 23.9 KB

DSC Resource Style Guidelines

In order to provide clean and consistent code, please follow the style guidelines listed below when contributing to any DSC Resource Kit repositories.

Table of Contents

Markdown Files

If a paragraph includes more than one sentence, end each sentence with a newline. GitHub will still render the sentences as a single paragraph, but the readability of git diff will be greatly improved.

General

Correct File Encoding

Make sure all files are encoded using UTF-8, except mof files which should be encoded using ASCII. You can use ConvertTo-UTF8 and ConvertTo-ASCII to convert a file to UTF-8 or ASCII.

Descriptive Names

Use descriptive, clear, and full names for all variables, parameters, and functions. All names must be at least more than 2 characters. No abbreviations should be used.

Bad:

$r = Get-RdsHost

Bad:

$frtytw = 42

Bad:

function Get-Thing
{
    ...
}

Bad:

function Set-ServerName
{
    param
    (
        $mySTU
    )
    ...
}

Good:

$remoteDesktopSessionHost = Get-RemoteDesktopSessionHost

Good:

$fileCharacterLimit = 42

Good:

function Get-ArchiveFileHandle
{
    ...
}

Good:

function Set-ServerName
{
    param
    (
        [Parameter()]
        $myServerToUse
    )
    ...
}

Correct Format for Long Function Calls

When calling a function with many long parameters, use parameter splatting. More help on splatting can be found using the command:

Get-Help -Name 'About_Splatting'

Make sure hashtable parameters are still properly formatted with multiple lines and the proper indentation.

Bad:

$superLongVariableName = Get-MySuperLongVariablePlease -MySuperLongHashtableParameter @{ MySuperLongKey1 = 'MySuperLongValue1'; MySuperLongKey2 = 'MySuperLongValue2' } -MySuperLongStringParameter '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890' -Verbose

Good:

$getMySuperLongVariablePleaseParams = @{
    MySuperLongHashtableParameter = @{
        mySuperLongKey1 = 'MySuperLongValue1'
        mySuperLongKey2 = 'MySuperLongValue2'
    }
    MySuperLongStringParameter = '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
    Verbose = $true
}

$superLongVariableName = Get-MySuperLongVariablePlease @getMySuperLongVariablePleaseParams

Correct Format for Arrays

Arrays should be written in the following format. Arrays should be writen on one line unless they exceed the line character limit. There should be a single space between each element in the array. Hashtables should not be declared inside an array.

Bad:

$array = @( 'one', `
'two', `
'three'
)

Good:

$hashtable = @{
    Key = "Value"
}

$array = @( 'one', 'two', 'three', $hashtable )

Correct Format for Hashtables or Objects

Hashtables and Objects should be written in the following format. Each property should be on its own line indented once.

Bad:

$hashtable = @{Key1 = 'Value1';Key2 = 2;Key3 = '3'}

Bad:

$hashtable = @{ Key1 = 'Value1'
Key2 = 2
Key3 = '3' }

Good:

$hashtable = @{
    Key1 = 'Value1'
    Key2 = 2
    Key3 = '3'
}

Good:

$hashtable = @{
    Key1 = 'Value1'
    Key2 = 2
    Key3 = @{
        Key3Key1 = 'ExampleText'
        Key3Key2 = 42
    }
}

Correct Format for Comments

There should not be any commented-out code in checked-in files. The first letter of the comment should be captialized.

Single line comments should be on their own line and start with a single pound-sign followed by a single space. The comment should be indented the same amount as the following line of code.

Comments that are more than one line should use the <# #> format rather than the single pound-sign. The opening and closing brackets should be on their own lines. The comment inside the brackets should be indented once more than the brackets. The brackets should be indented the same amount as the following line of code.

Formatting help-comments for functions has a few more specific rules that can be found here.

Bad:

function Get-MyVariable
{#this is a bad comment
    [CmdletBinding()]
    param ()
#this is a bad comment
    foreach ($example in $examples)
    {
        Write-Verbose -Message $example #this is a bad comment
    }
}

Bad:

function Get-MyVariable
{
    [CmdletBinding()]
    param ()

    # this is a bad comment
    # On multiple lines
    foreach ($example in $examples)
    {
        # No commented-out code!
        # Write-Verbose -Message $example
    }
}

Good:

function Get-MyVariable
{
    # This is a good comment
    [CmdletBinding()]
    param ()

    # This is a good comment
    foreach ($example in $examples)
    {
        # This is a good comment
        Write-Verbose -Message $example
    }
}

Good:

function Get-MyVariable
{
    [CmdletBinding()]
    param ()

    <#
        This is a good comment
        on multiple lines
    #>
    foreach ($example in $examples)
    {
        Write-Verbose -Message $example
    }
}

Whitespace

Indentation

For all indentation, use 4 spaces instead of tabs. There should be no tab characters in the file unless they are in a here-string.

No Trailing Whitespace After Backticks

Backticks should always be directly followed by a newline

Newline at End of File

All files must end with a newline, see StackOverflow.

Newline Character Encoding

Save newlines using CR+LF instead of CR. For interoperability reasons, we recommend that you follow these instructions when installing Git on Windows so that newlines saved to GitHub are simply CRs.

No More Than Two Consecutive Newlines

Code should not contain more than two consecutive newlines unless they are contained in a here-string.

Bad:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'


    return $MyValue
}

Bad:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'
    return $MyValue
}



function Write-Log
{
    Write-Verbose -Message 'Logging...'
}

Good:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'
    return $MyValue
}

Good:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'
    return $MyValue
}

function Write-Log
{
    Write-Verbose -Message 'Logging...'
}

One Newline Before Braces

Each curly brace should be preceded by a newline unless assigning to a variable.

Bad:

if ($booleanValue) {
    Write-Verbose -Message "Boolean is $booleanValue"
}

Good:

if ($booleanValue)
{
    Write-Verbose -Message "Boolean is $booleanValue"
}

When assigning to a variable, opening curly braces should be on the same line as the assignment operator.

Bad:

$scriptBlockVariable =
{
    Write-Verbose -Message 'Executing script block'
}

Bad:

$hashtableVariable =
@{
    Key1 = 'Value1'
    Key2 = 'Value2'
}

Good:

$scriptBlockVariable = {
    Write-Verbose -Message 'Executing script block'
}

Good:

$hashtableVariable = @{
    Key1 = 'Value1'
    Key2 = 'Value2'
}

One Newline After Opening Brace

Each opening curly brace should be followed by only one newline.

Bad:

function Get-MyValue
{

    Write-Verbose -Message 'Getting MyValue'

    return $MyValue
}

Bad:

function Get-MyValue
{ Write-Verbose -Message 'Getting MyValue'

    return $MyValue
}

Good:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'
    return $MyValue
}

Two Newlines After Closing Brace

Each closing curly brace ending a function, conditional block, loop, etc. should be followed by exactly two newlines unless it is directly followed by another closing brace. If the closing brace is followed by another closing brace or continues a conditional or switch block, there should be only one newline after the closing brace.

Bad:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'
    return $MyValue
} Get-MyValue

Bad:

function Get-MyValue
{ Write-Verbose -Message 'Getting MyValue'

    if ($myBoolean)
    {
        return $MyValue
    }

    else
    {
        return 0
    }

}
Get-MyValue

Good:

function Get-MyValue
{
    Write-Verbose -Message 'Getting MyValue'

    if ($myBoolean)
    {
        return $MyValue
    }
    else
    {
        return 0
    }
}

Get-MyValue

One Space Between Type and Variable Name

If you must declare a variable type, type declarations should be separated from the variable name by a single space.

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    [Int]$number = 2
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    [Int] $number = 2
}

One Space on Either Side of Operators

There should be one blank space on either side of all operators.

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    $number=2+4-5*9/6
}

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    if ('example'-eq'example'-or'magic')
    {
        Write-Verbose -Message 'Example found.'
    }
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    $number = 2 + 4 - 5 * 9 / 6
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    if ('example' -eq 'example' -or 'magic')
    {
        Write-Verbose -Message 'Example found.'
    }
}

One Space Between Keyword and Parenthesis

If a keyword is followed by a parenthesis, there should be single space between the keyword and the parenthesis.

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    if('example' -eq 'example' -or 'magic')
    {
        Write-Verbose -Message 'Example found.'
    }

    foreach($example in $examples)
    {
        Write-Verbose -Message $example
    }
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param ()

    if ('example' -eq 'example' -or 'magic')
    {
        Write-Verbose -Message 'Example found.'
    }

    foreach ($example in $examples)
    {
        Write-Verbose -Message $example
    }
}

Functions

Function Names Use Pascal Case

Function names must use PascalCase.

Bad:

function get-targetresource
{
    # ...
}

Good:

function Get-TargetResource
{
    # ...
}

Function Names Use Verb-Noun Format

All function names must follow the standard PowerShell Verb-Noun format.

Bad:

function TargetResourceGetter
{
    # ...
}

Good:

function Get-TargetResource
{
    # ...
}

Function Names Use Approved Verbs

All function names must use approved verbs.

Bad:

function Normalize-String
{
    # ...
}

Good:

function ConvertTo-NormalizedString
{
    # ...
}

Functions Have Comment-Based Help

All functions should have comment-based help with the correct syntax directly above the function. Comment-help should include at least the SYNOPSIS section and a PARAMETER section for each parameter.

Bad:

# Creates an event
function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,

        [Parameter()]
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
    # Implementation...
}

Good:

<#
    .SYNOPSIS
        Creates an event

    .PARAMETER Message
        Message to write

    .PARAMETER Channel
        Channel where message should be stored

    .EXAMPLE
        New-Event -Message 'Attempting to connect to server' -Channel 'debug'
#>
function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,

        [Parameter()]
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
    # Implementation
}

Parameter Block at Top of Function

There must be a parameter block decalred for every function. The parameter block must be at the top of the function and not declared next to the function name. Functions with no parameters should still display an empty parameter block.

Bad:

function Write-Text([Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]$Text)
{
    Write-Verbose -Message $Text
}

Bad:

function Write-Nothing
{
    Write-Verbose -Message 'Nothing'
}

Good:

function Write-Text
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Text
    )

    Write-Verbose -Message $Text
}

Good:

function Write-Nothing
{
    param ()

    Write-Verbose -Message 'Nothing'
}

Parameters

Correct Format for Parameter Block

  • An empty parameter block should be displayed on its own line like this: param ().
  • A non-empty parameter block should have the opening and closing parentheses on their own line.
  • All text inside the parameter block should be indented once.
  • Every parameter should include the [Parameter()] attribute, regardless of whether the attribute requires decoration or not.
  • A parameter that is mandatory should contain this decoration: [Parameter(Mandatory = $true)].
  • A parameter that is not mandatory should not contain a Mandatory decoration in the [Parameter()].

Bad:

function Write-Nothing
{
    param
    (

    )

    Write-Verbose -Message 'Nothing'
}

Bad:

function Write-Text
{
    param([Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
                    [String] $Text )

    Write-Verbose -Message $Text
}

Bad:

function Write-Text
{
    param
    (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Text

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [String]
        $PrefixText

        [Boolean]
        $AsWarning = $false
    )

    if ($AsWarning)
    {
        Write-Warning -Message "$PrefixText - $Text"
    }
    else
    {
        Write-Verbose -Message "$PrefixText - $Text"
    }
}

Good:

function Write-Nothing
{
    param ()

    Write-Verbose -Message 'Nothing'
}

Good:

function Write-Text
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Text
    )

    Write-Verbose -Message $Text
}

Good:

function Write-Text
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Text

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String]
        $PrefixText

        [Parameter()]
        [Boolean]
        $AsWarning = $false
    )

    if ($AsWarning)
    {
        Write-Warning -Message "$PrefixText - $Text"
    }
    else
    {
        Write-Verbose -Message "$PrefixText - $Text"
    }
}

Parameter Names Use Pascal Case

All parameters must use PascalCase.

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        $SOURCEPATH
    )
}

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        $sourcepath
    )
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        $SourcePath
    )
}

Parameters Separated by One Line

Parameters must be separated by a single, blank line.

Bad:

function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
}

Good:

function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,

        [Parameter()]
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
}

Parameter Type on Line Above

The parameter type must be on its own line above the parameter name. If an attribute needs to follow the type, it should also have its own line between the parameter type and the parameter name.

Bad:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        [String] $SourcePath = 'c:\'
    )
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [String]
        $SourcePath = 'c:\'
    )
}

Good:

function Get-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter()]
        [PSCredential]
        [Credential()]
        $MyCredential
    )
}

Good:

function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,

        [Parameter()]
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
}

Parameter Attributes on Separate Lines

Parameter attributes should each have their own line. All attributes should go above the parameter type, except those that must be between the type and the name.

Bad:

function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][String]
        $Message,

        [ValidateSet('operational', 'debug', 'analytic')][String]
        $Channel = 'operational'
    )
}

Good:

function New-Event
{
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Message,

        [Parameter()]
        [ValidateSet('operational', 'debug', 'analytic')]
        [String]
        $Channel = 'operational'
    )
}

Variables

Variable Names Use Camel Case

Variable names should use camelCase.

Bad:

function Write-Log
{
    $VerboseMessage = 'New log message'
    Write-Verbose $VerboseMessage
}

Bad:

function Write-Log
{
    $verbosemessage = 'New log message'
    Write-Verbose $verbosemessage
}

Good:

function Write-Log
{
    $verboseMessage = 'New log message'
    Write-Verbose $verboseMessage
}

Script, Environment and Global Variable Names Include Scope

Script, environment, and global variables must always include their scope in the variable name unless the 'using' scope is needed. The script and global scope specifications should be all in lowercase. Script and global variable names following the scope should use camelCase.

Bad:

$fileCount = 0
$GLOBAL:MYRESOURCENAME = 'MyResource'

function New-File
{
    $fileCount++
    Write-Verbose -Message "Adding file to $MYRESOURCENAME to $ENV:COMPUTERNAME."
}

Good:

$script:fileCount = 0
$global:myResourceName = 'MyResource'

function New-File
{
    $script:fileCount++
    Write-Verbose -Message "Adding file to $global:myResourceName to $env:computerName."
}

Best Practices

Named Parameters Instead of Positional Parameters

Call cmdlets using named parameters instead of positional parameters.

Bad:

Get-ChildItem C:\Documents *.md

Good:

Get-ChildItem -Path C:\Documents -Filter *.md

No Cmdlet Aliases

When calling a function use the full command not an alias. You can get the full command an alias is using by calling Get-Alias.

Bad:

ls -File $root -Recurse | ? { @('.gitignore', '.mof') -contains $_.Extension }

Good:

Get-ChildItem -File $root -Recurse | Where-Object { @('.gitignore', '.mof') -contains $_.Extension }

No Backslash in Paths

To support the possibility of cross-platform use in the future, backslashes should not be used within a path. Instead Join-Path and Split-Path should be used to build a path.

Bad:

$currentPath = Split-Path -Parent $MyInvocation.MyCommand.Path
Import-Module -Name "$currentPath\..\..\CommonResourceHelper.psm1"

Good:

$currentPath = Split-Path -Path $MyInvocation.MyCommand.Path -Parent
$modulePath = (Join-Path -Path (Split-Path -Path (Split-Path -Path $currentPath -Parent) -Parent) `
                         -ChildPath 'CommonResourceHelper.psm1')
Import-Module -Name $modulePath