PSCredentialStore/src/Item/Set-CredentialStoreItem.ps1

173 lines
6.4 KiB
PowerShell

function Set-CredentialStoreItem {
<#
.SYNOPSIS
Changes the credentials for the given remote host in the store.
.DESCRIPTION
Use this function to update your already stored RemoteHost items.
.PARAMETER Path
Define the store in which your given host entry already exists.
.PARAMETER RemoteHost
Specify the host you for which you would like to change the credentials.
.PARAMETER Identifier
Defaults to ''. Specify a string, which separates two CredentialStoreItems for the
same hostname.
.PARAMETER Shared
Switch to shared mode with this param. This enforces the command to work with a shared CredentialStore which
can be decrypted across systems.
.PARAMETER Credential
Provided the new credentials you want to update inside the RemoteHost item.
.INPUTS
[None]
.OUTPUTS
[None]
.EXAMPLE
Set-CredentialStoreItem -Path 'C:\TMP\mystore.json' -RemoteHost 'esx01.myside.local'
.EXAMPLE
Set-CredentialStoreItem -Path 'C:\TMP\mystore.json' -RemoteHost 'esx01.myside.local' -Identifier svc
#>
[CmdletBinding(DefaultParameterSetName = 'Private')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute(
'PSUseShouldProcessForStateChangingFunctions',
'',
Justification = 'Updates existing credential object.'
)]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'Private')]
[Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
[string]$RemoteHost,
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[string]$Identifier,
[Parameter(Mandatory = $false, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[PSCredential]$Credential,
[Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[ValidateNotNullOrEmpty()]
[string]$Path
)
begin {
# Set the CredentialStore for private, shared or custom mode.
Write-Debug ('ParameterSetName: {0}' -f $PSCmdlet.ParameterSetName)
if ($PSCmdlet.ParameterSetName -eq 'Private') {
$Path = Get-DefaultCredentialStorePath
}
elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
}
}
}
process {
# Define the default splatting.
$DefaultSplatting = @{
Path = $Path
}
# Check if the user passed -Shared. If he added -Shared, we'll pass it into the splatting
if ($PSBoundParameters.ContainsKey('Shared')) {
$DefaultSplatting.Add('Shared', $true)
}
else {
$DefaultSplatting.Add('Shared', $false)
}
# Now lets check the given CredentialStore.
if (-not(Test-CredentialStore @DefaultSplatting)) {
$MessageParams = @{
Message = ('The given CredentialStore ({0}) does no exist.' -f $Path)
ErrorAction = 'Stop'
}
Write-Error @MessageParams
}
# Read the file content based on the given ParameterSetName
$CSContent = Get-CredentialStore @DefaultSplatting
# Get a formatted current date for the last update time of the Item.
$CurrentDate = Get-Date -Format 'u'
# Check if the user supplied an identifier. If so, we need to mangle the CredentialName, as that's where
# the identifier is actually added.
if ($Identifier -ne '') {
$CredentialName = $RemoteHost = '{0}/{1}' -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
# If the user didn't supply a CredentialObject, we need to prompt for it.
if (-not($Credential)) {
$Credential = Get-Credential -Message $CredentialName
}
# If the username isn't empty, we ca go ahead and update the entry.
if ($null -ne $Credential.UserName -and -not [string]::IsNullOrWhiteSpace($Credential.UserName)) {
# Check if the path to the PfxCertificate is stored in the CredentialStore. If so load the certificate.
# If not, load try loading the certificate from the Filepath of the CredentialStore.
if ($null -eq $CSContent.PfxCertificate) {
$Cert = Get-CSCertificate -Type $CSContent.Type -Thumbprint $CSContent.Thumbprint
}
else {
$Cert = Get-PfxCertificate -FilePath $CSContent.PfxCertificate -ErrorAction Stop
}
# Now locate the Item.
if (Get-Member -InputObject $CSContent -Name $CredentialName -MemberType Properties) {
# Get a random AES key for the entry.
$RSAKey = Get-RandomAESKey
$CSContent.$CredentialName.User = $Credential.UserName
$ConvertParams = @{
SecureString = $Credential.Password
Key = $RSAKey
}
# Now create a updated item containing the updated credentials.
$CSContent.$CredentialName.Password = ConvertFrom-SecureString @ConvertParams
$CSContent.$CredentialName.LastChange = $CurrentDate
$CSContent.$CredentialName.EncryptedKey = [Convert]::ToBase64String(
$Cert.PublicKey.Key.Encrypt(
$RSAKey,
[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
)
)
# Convert the CredentialStore back into JSON and save it to the file.
ConvertTo-Json -InputObject $CSContent -Depth 5 | Out-File -FilePath $Path -Encoding utf8
}
else {
Write-Warning -Message ('Unable to locate CredentialStoreItem for {0}' -f $CredentialName)
}
}
else {
$MessageParams = @{
Message = 'Please Provide at least a valid user!'
ErrorAction = 'Stop'
}
Write-Error @MessageParams
}
}
end {
}
}