Skip to content

Latest commit

 

History

History
942 lines (691 loc) · 19.7 KB

STYLE_GUIDELINES.md

File metadata and controls

942 lines (691 loc) · 19.7 KB

Desired State Configuration Resources for VMware Style Guidelines & Best Practices

In order to provide clean and consistent code, please follow the style guidelines listed below when contributing to the repository.

Table of Contents

Style Guidelines

General

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:

$vmh = Get-VMHost

Bad:

$sixty = 60

Bad:

function Get-Something {
    ...
}

Bad:

function Set-VMHost {
    param (
        $myVMH
    )
    ...
}

Good:

$vmHost = Get-VMHost

Good:

$secondsInAMinute = 60

Good:

function New-DNSConfig {
    ...
}

Good:

function Update-DNSConfig {
    param (
        [VMware.Vim.HostNetworkSystem] $NetworkSystem
    )
    ...
}

Correct Parameter Usage in Function and Cmdlet Calls

Use named parameters for function and cmdlet calls rather than positional parameters. Named parameters help other developers who are unfamiliar with your code to better understand it.

When calling a function with many long parameters, use parameter splatting. If splatting is used, then all the parameters should be in the splat. More help on splatting can be found in the article About Splatting.

Bad:

Not using named parameters.

Get-VMHost MyVM

Good:

Get-VMHost -Name MyVM

Correct Format for Arrays

Arrays should be written in one of the following formats.

If an array is declared on a single line, then there should be a single space between each element in the array. If arrays written on a single line tend to be long, please consider using one of the alternative ways of writing the array.

Bad:

Array elements are not format consistently.

$ntpServer = @( 'ntp server 1', `
'ntp server 2', `
'ntp server 3'
)

Bad:

There are no single space beetween the elements in the array.

$ntpServer = @('ntp server 1','ntp server 2','ntp server 3')

Bad:

There are multiple array elements on the same row.

$ntpServer = @(
    'ntp server 1', 'ntp server 2', `
    'ntp server 3', `
    'ntp server 4', 'ntp server 5'
)

Good:

$ntpServer = @('ntp server 1', 'ntp server 2', 'ntp server 3')

Good:

$ntpServer = @(
    'ntp server 1',
    'ntp server 2',
    'ntp server 3'
)

Good:

$ntpServer = @(
    'ntp server 1'
    'ntp server 2'
    'ntp server 3'
    'ntp server 4'
    'ntp server 5'
)

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:

$performanceIntervalArgs = @{ Key = $currentPerformanceInterval.Key
Name = $currentPerformanceInterval.Name
Enabled = $this.SpecifiedOrCurrentValue($this.Enabled, $currentPerformanceInterval.Enabled)
Level = $this.SpecifiedOrCurrentValue($this.Level, $currentPerformanceInterval.Level)
SamplingPeriod = $this.SpecifiedOrCurrentValue($this.IntervalMinutes * $this.SecondsInAMinute, $currentPerformanceInterval.SamplingPeriod)
Length = $this.SpecifiedOrCurrentValue($this.PeriodLength * $this.Period, $currentPerformanceInterval.Length)
}

Good:

$performanceIntervalArgs = @{
    Key = $currentPerformanceInterval.Key
    Name = $currentPerformanceInterval.Name
    Enabled = $this.SpecifiedOrCurrentValue($this.Enabled, $currentPerformanceInterval.Enabled)
    Level = $this.SpecifiedOrCurrentValue($this.Level, $currentPerformanceInterval.Level)
    SamplingPeriod = $this.SpecifiedOrCurrentValue($this.IntervalMinutes * $this.SecondsInAMinute, $currentPerformanceInterval.SamplingPeriod)
    Length = $this.SpecifiedOrCurrentValue($this.PeriodLength * $this.Period, $currentPerformanceInterval.Length)
}

Correct use of single and double quotes

Single quotes should always be used to delimit string literals wherever possible. Double quoted string literals may only be used when it contains ($) expressions that need to be evaluated.

Bad:

$vmhost = "VMHost with name was not found."

Good:

$vmhost = 'VMHost with name was not found.'

Good:

$vmhost = 'VMHost with name $($this.Name) was not found.'

Correct Format for Comments

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

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 brackets should be indented the same amount as the following line of code.

Bad:

 <#
    .DESCRIPTION
    Name of the Server we are trying to connect to. The Server can be a vCenter or ESXi.
    #>
    [DscProperty(Key)]
    [string] $Server

Bad:

    # Empty array specified as desired, but current is not an empty array, so update VMHost NTP Server.
 return $true

Good:

 <#
 .DESCRIPTION

 Name of the Server we are trying to connect to. The Server can be a vCenter or ESXi.
 #>
 [DscProperty(Key)]
 [string] $Server

Good:

 # Empty array specified as desired, but current is not an empty array, so update VMHost NTP Server.
 return $true

Correct Format for Keywords

PowerShell reserved Keywords should be in all lower case and should be immediately followed by a space if there is non-whitespace characters following (for example, an open brace).

The following is the current list of PowerShell reserved keywords in PowerShell 5.1:

begin, break, catch, class, continue, data, define do, dynamicparam, else,
elseif, end, enum, exit, filter, finally, for, foreach, from, function
hidden, if, in, inlinescript, param, process, return, static, switch,
throw, trap, try, until, using, var, while

This list may change in newer versions of PowerShell.

The latest list of PowerShell reserved keywords can also be found on this page.

Bad:

# Missing space after keyword and before open bracket
foreach($item in $list)

Bad:

# Capital letters in keyword
TRY

Bad:

# Capital letters in 'in' and 'foreach' keyword
ForEach ($item In $list)

Bad:

try
{
    # Do some work
}

Good:

foreach ($item in $list)

Good:

try {
    # Do some work
}

Whitespace

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.

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-VMHost {
    Write-Verbose -Message 'Getting VMHost'


    return $vmHost
}

Good:

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

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-VMHost {
    Write-Verbose -Message 'Getting VMHost'
    return $vmHost
} Get-VMHost

Bad:

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

    if ($myBoolean) {
        return $vmHost
    }

    else {
        return 0
    }

}
Get-VMHost

Good:

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

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

Get-VMHost

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-VMHost {
    [CmdletBinding()]
    param ()

    [VMHost]$vmHost = <VMHost>
}

Good:

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

    [VMHost] $vmHost = <VMHost>
}

One Space on Either Side of Operators

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

Bad:

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

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

Bad:

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

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

Good:

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

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

Good:

function Get-Message {
    [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-Message {
    [CmdletBinding()]
    param ()

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

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

Good:

function Get-Message {
    [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. This means that each concatenated word is capitalized.

Bad:

function get-vmhost {
    # ...
}

Good:

function Get-VMHost {
    # ...
}

Function Names Use Verb-Noun Format

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

Bad:

function VMHostGetter {
    # ...
}

Good:

function Get-VMHost {
    # ...
}

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
}

Parameters

Parameter Names Use Pascal Case

All parameters must use PascalCase. This means that each concatenated word is capitalized.

Bad:

function Get-VMHost {
    [CmdletBinding()]
    param (
        $VMHOSTNAME
    )
}

Bad:

function Get-VMHost {
    [CmdletBinding()]
    param (
        $vmhost
    )
}

Good:

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

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

General Best Practices

Avoid Using Hardcoded Computer Name

Using hardcoded computer names exposes sensitive information on your machine. Use a parameter or environment variable instead if a computer name is necessary.

Bad:

Invoke-Command -Port 0 -ComputerName 'hardcodedName'

Good:

Invoke-Command -Port 0 -ComputerName $env:computerName

Avoid Empty Catch Blocks

Empty catch blocks are not necessary. Most errors should be thrown or at least acted upon in some way. If you really don't want an error to be thrown or logged at all, use the ErrorAction parameter with the SilentlyContinue value instead.

Bad:

try {
    Get-Command -Name Invoke-NotACommand
}
catch {}

Good:

Get-Command -Name Invoke-NotACommand -ErrorAction SilentlyContinue

Ensure Null is on Left Side of Comparisons

When comparing a value to $null, $null should be on the left side of the comparison. This is due to an issue in PowerShell. If $null is on the right side of the comparison and the value you are comparing it against happens to be a collection, PowerShell will return true if the collection contains $null rather than if the entire collection actually is $null. Even if you are sure your variable will never be a collection, for consistency, please ensure that $null is on the left side of all comparisons.

Bad:

if ($myArray -eq $null) {
    Remove-AllItems
}

Good:

if ($null -eq $myArray) {
    Remove-AllItems
}

Avoid Global Variables

Avoid using global variables whenever possible. These variables can be edited by any other script that ran before your script or is running at the same time as your script. Use them only with extreme caution, and try to use parameters or script/local variables instead.

Bad:

$global:configurationName = 'MyConfigurationName'
...
Set-MyConfiguration -ConfigurationName $global:configurationName

Good:

$script:configurationName = 'MyConfigurationName'
...
Set-MyConfiguration -ConfigurationName $script:configurationName

Use Declared Local and Script Variables More Than Once

Don't declare a local or script variable if you're not going to use it. This creates excess code that isn't needed

Use PSCredential for All Credentials

PSCredentials are more secure than using plaintext username and passwords.

Bad:

function Get-Settings {
    param (
        [String]
        $Username

        [String]
        $Password
    )
    ...
}

Good:

function Get-Settings {
    param (
        [PSCredential]
        [Credential()]
        $UserCredential
    )
}

Pester Tests

Capitalized Pester Assertions

Pester assertions should all start with capital letters. This makes the code easier to read.

Bad:

it 'Should return something' {
    get-targetresource @testParameters | should -be 'something'
}

Good:

It 'Should return something' {
    Get-TargetResource @testParameters | Should -Be 'something'
}