Read-Credential.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#----------------------------------------------------[Function synopsis]-----------------------------------------------------

Function Read-Credential {

<#
   .SYNOPSIS
    Reads credentials from disk, saved with Write-Credential. Part of CredentialsManager module.
 
   .DESCRIPTION
    Reads credentials (User Name and Password) from disk, saved with Write-Credential.
    Credentials are saved as encrypted data. Encryption is done with current user identifier, so only current user can decrypt saved credentials.
    Other users cannot decrypt content, even if they get access to credential files (i.e. saved on network share).
 
    .EXAMPLE
    $Cred = Read-Credential -Environment Dev
    Gets username and password from files saved in repository and saves it in variable.
 
    .EXAMPLE
    Get-WMIObject Win32_Service -Computer '58.138.249.212' -Credential (Read-Credential -Environment Dev)
    Gets credentials from repository and directly uses it in network operation.
 
    .EXAMPLE
    Get-Item '\\dfs\restricted\*' -Credential (Read-Credential -Environment BAcc)
    Gets B account credentials from repository and directly uses it to access restricted folder content.
 
    .PARAMETER Environment
    Name of environment for which credentials should be obtained. It must be already saved with Write-Credential.
    It is also used as part of file names in Repository.
 
    .PARAMETER Path
    Optional parameter which defines where credentials are saved on the disk.
    If not specified, %APPDATA%\CredentialsManager is used.
    If specified, default value will be updated and re-used on next calls within the same PowerShell session.
 
    .PARAMETER ListAvailable
    If specified, function will list all credentials at default or provided path. Returned object(s) will have added property Environment which is named according to Write-Credential function.
 
    .OUTPUTS
    Function returns Credentials object the same as Get-Credential, System.Management.Automation.PSCredential.
 
    .LINK
    https://www.powershellgallery.com/packages/CredentialsManager
 
    .NOTES
    NAME: Read-Credential
    AUTHOR: Igor Iric, IricIgor@Gmail.com
    CREATEDATE: October, 2015
 
 #>



#-------------------------------------------------[Parameters definitions]--------------------------------------------------

[cmdletbinding()]

Param(
  [parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName='Environments',Position=1)][string[]]$Environment,
  [parameter(Mandatory=$false,ValueFromPipeline=$false)][string]$Path=$Script:CredentialsPath,
  [parameter(Mandatory=$true,ValueFromPipeline=$false,ParameterSetName='Available')][switch]$ListAvailable
) #end param


#-------------------------------------------------[Function initialization]--------------------------------------------------
BEGIN {
    # function begin phase

    # process Path parameter, add default value, check if it exists
    if (!$Path) {
        # Path not provided, neither global is defined
        $Script:CredentialsPath = Join-Path -Path $env:APPDATA -ChildPath 'CredentialsManager'
        Write-Verbose -Message "Setting default path to: $Script:CredentialsPath"
        $Path = $Script:CredentialsPath
        } 

    if (!(Test-Path -Path $Path)) {
        Throw "Folder $Path is not existing. Please provide another path, or run Write-Credential first."
    } elseif ($Path -ne $Script:CredentialsPath) {
        # Path correct and specified directly, it will be saved for next usage
        Write-Verbose -Message "Updating default path to $Path."
        $Script:CredentialsPath = $Path
    } 

    # process ListAvailable
    if ($ListAvailable) {
        Write-Verbose -Message 'Obtaining list of available environments'
        <#
        $UserNames = (Get-ChildItem -Path $Path -Filter '*_UserName.cred') -replace '_UserName.cred',''
        $PassNames = (Get-ChildItem -Path $Path -Filter '*_Password.cred') -replace '_Password.cred',''
        if ($UserNames -and $PassNames) {
            $Environment = (Compare-Object -ReferenceObject $UserNames -DifferenceObject $PassNames -ExcludeDifferent -IncludeEqual).InputObject
        }
        #>

        $Environment = (Get-ChildItem -Path $Path -Filter '*.creds' -Name) -replace '.creds',''

        if (!$Environment) {
            Write-Warning -Message 'No stored credentials found. Try using Write-Credential, or Read-Credential with Path and ListAvailable parameters.'
        } else {
            Write-Verbose -Message ('Obtained '+($Environment.Count)+' environments.')
        } 
    }
}

#---------------------------------------------------[Function processing]----------------------------------------------------
PROCESS {

    foreach ($E in @($Environment)) {
        # function process phase, executed once for each element in main Prameter
        Write-Verbose -Message '----------------------'
        Write-Verbose -Message "Processing environment $E..."
        $VerboseMessage = "Environment $E processed with issues." # it will be updated at the end, if successfull
        $Cred = $null

        # main code
        #$FileNameUser = Join-Path -Path $Path -ChildPath ($E + '_UserName.cred')
        #$FileNamePass = Join-Path -Path $Path -ChildPath ($E + '_Password.cred')
        $FileName = Join-Path -Path $Path -ChildPath ($E + '.creds')

        #if ((!(Test-Path -Path $FileNameUser)) -or (!(Test-Path -Path $FileNamePass))) {
        if (!(Test-Path -Path $FileName)) {
            Write-Error -Message "Credentials file not found."
        } else {
            Write-Verbose -Message 'Credentials file found.'

            $Content = Get-Content -Path $FileName
            if (($Content.Count) -ne 2) {
                Write-Error -Message "Credentials file not in proper format."
            } else {
            
                try {           
                    Write-Verbose -Message 'Attempting to decrypt data.'
                    #$UserSec = Get-Content -Path $FileNameUser | ConvertTo-SecureString
                    #$PassSec = Get-Content -Path $FileNamePass | ConvertTo-SecureString
                    $UserSec = $Content[0] | ConvertTo-SecureString
                    $PassSec = $Content[1] | ConvertTo-SecureString
                    $UserPlain = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($UserSec))

                    $Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $UserPlain,$PassSec
                    Write-Verbose -Message 'Data encryption completed.'
                    $VerboseMessage = "Environment $E processed successfully."
                } catch {
                    if (($Error[0].Exception.Message) -eq 'Key not valid for use in specified state.') {
                        Write-Error 'Data encryption failed. Only user account that saved data can decrypt it.'
                    } else {
                        Write-Error -Message ('Data encryption failed. '+$Error[0].Exception.Message)
                    }
                }
            }
        }

        # return values
        if ($Cred) {
            if ($ListAvailable) {
                Write-Verbose -Message 'Adding Environment property.'
                $Cred | Add-Member -MemberType NoteProperty -Name Environment -Value $E
            }
            Write-Verbose -Message "Returning $E credentials value to output."
            $Cred
        }

        Write-Verbose -Message $VerboseMessage

    } # end of foreach

} # end of function

#-----------------------------------------------------[Function closing]-----------------------------------------------------

END {
    # function closing phase
    Write-Verbose -Message 'Read-Credential finishing.'

}

} # end of function code
#----------------------------------------------------[End of function]------------------------------------------------------

#---------------------------------------------------[Comments section]------------------------------------------------------