Functions/Test-PasswordComplexity.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
function Test-PasswordComplexity {
    <#
.SYNOPSIS
    Tests a password for length and password complexity
.DESCRIPTION
    Tests a password for length and password complexity. Complexity is at least 1 upper case character, 1 lower case character,
    1 numeral, 1 special character.
.PARAMETER SecureString
    The password passed as a secure string. In parameter sets 'SecureString'.
.PARAMETER Credential
    The password passed as part of a credential. In parameter sets 'Credential'.
.PARAMETER Password
    The password passed as plain text. In parameter sets 'Password'.
.Parameter MinimumLength
    Integer minimum number of characters in password. Valid range 1-255. Defaults to 8. Aliased to 'MinLength'.
    In parameter sets 'SecureString', 'Credential', 'Password'
.PARAMETER IncludeInput
    Switch whether to include input in the output. Passwords are masked with a '*'. In parameter sets 'SecureString', 'Credential', 'Password'
.EXAMPLE
    Test-PasswordComplexity -Password 'Password1'
 
    Would return $false as there is no special character
.EXAMPLE
    Test-PasswordComplexity -Password 'Password1' -IncludeInput
 
    Would return the following as there is no special character
    Password MinLength Length MatchComplexity
    -------- --------- ------ ---------------
    ********* 8 9 False
.EXAMPLE
    Test-PasswordComplexity -Password 'Ab(0' -IncludeInput
 
    Although it matches all the character types the password is too short
    Password MinLength Length MatchComplexity
    -------- --------- ------ ---------------
    **** 8 4 False
.NOTES
    Changed logic on getting $*Regex values so there would not be a dependency on Get-PrintableAscii
#>


    #region Parameter
    [CmdletBinding(DefaultParameterSetName = 'SecureString')]
    [OutputType('bool')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
    param (
        [Parameter(ParameterSetName = 'SecureString')]
        [securestring] $SecureString,

        [Parameter(ParameterSetName = 'Credential')]
        [pscredential] $Credential,

        [Parameter(ParameterSetName = 'Password', ValueFromPipeline)]
        [string[]] $Password,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'Credential')]
        [Parameter(ParameterSetName = 'Password')]
        [ValidateRange(0, 255)]
        [Alias('MinLength')]
        [int] $MinimumLength = 8,

        [Parameter(ParameterSetName = 'SecureString')]
        [Parameter(ParameterSetName = 'Credential')]
        [Parameter(ParameterSetName = 'Password')]
        [switch] $IncludeInput
    )
    #endregion

    begin {
        Write-Verbose -Message "Starting [$($MyInvocation.Mycommand)]"
        $Printable = (33..126 | ForEach-Object { ([regex]::Escape([char] $_ )) })
        $LowerRegex = '[a-z]'
        $UpperRegex = '[A-Z]'
        $NumberRegex = '[0-9]'
        $SpecialRegex = '[' + (($Printable | Where-Object { $_ -notmatch $LowerRegex -and $_ -notmatch $NumberRegex } ) -join '|') + ']'
    }

    process {
        switch ($PsCmdlet.ParameterSetName) {
            'Password' {
                foreach ($curPassword in $Password) {
                    if (
                        ($curPassword -match $SpecialRegex) -and
                        ($curPassword -cmatch $LowerRegex) -and
                        ($curPassword -cmatch $UpperRegex) -and
                        ($curPassword -match $NumberRegex) -and
                        ($curPassword.Length -ge $MinimumLength)
                    ) {
                        $ReturnVal = $true
                    } else {
                        $ReturnVal = $false
                    }
                    if ($IncludeInput) {
                        New-Object -TypeName psobject -Property ([ordered] @{
                                Password        = $curPassword -replace '.', '*'
                                MinLength       = $MinimumLength
                                Length          = $curPassword.Length
                                MatchComplexity = $ReturnVal
                            })
                    } else {
                        $ReturnVal
                    }
                }
            }
            'Credential' {
                $curPassword = Convert-SecureStringToString -SecureString $Credential.Password
                if (
                    ($curPassword -match $SpecialRegex) -and
                    ($curPassword -cmatch $LowerRegex) -and
                    ($curPassword -cmatch $UpperRegex) -and
                    ($curPassword -match $NumberRegex) -and
                    ($curPassword.Length -ge $MinimumLength)
                ) {
                    $ReturnVal = $true
                } else {
                    $ReturnVal = $false
                }
                if ($IncludeInput) {
                    New-Object -TypeName psobject -Property ([ordered] @{
                            Password        = $curPassword -replace '.', '*'
                            MinLength       = $MinimumLength
                            Length          = $curPassword.Length
                            MatchComplexity = $ReturnVal
                        })
                } else {
                    $ReturnVal
                }

            }
            'SecureString' {
                $curPassword = Convert-SecureStringToString -SecureString $SecureString
                if (
                    ($curPassword -match $SpecialRegex) -and
                    ($curPassword -cmatch $LowerRegex) -and
                    ($curPassword -cmatch $UpperRegex) -and
                    ($curPassword -match $NumberRegex) -and
                    ($curPassword.Length -ge $MinimumLength)
                ) {
                    $ReturnVal = $true
                } else {
                    $ReturnVal = $false
                }
                if ($IncludeInput) {
                    New-Object -TypeName psobject -Property ([ordered] @{
                            Password        = $curPassword -replace '.', '*'
                            MinLength       = $MinimumLength
                            Length          = $curPassword.Length
                            MatchComplexity = $ReturnVal
                        })
                } else {
                    $ReturnVal
                }
            }
        }

    }

    end {
        Write-Verbose -Message "Ending [$($MyInvocation.Mycommand)]"
    }
}