PSCredentialStore/src/Store/New-CredentialStore.ps1
Marco Blessing 3d90d912ee
All checks were successful
continuous-integration/drone/push Build is passing
fix lint (PSScriptAnalyzer) issues (#62)
#### 📖 Summary

<!-- Provide a summary of your changes. Describe the why and not how. -->

#### 📑 Test Plan

> 💡 Select your test plan for the code changes.

- [x] Tested via Drone.io pipeline
- [ ] Custom test
- [ ] No test plan

##### Details / Justification

<!-- Add your test details or justification for missing tests here. -->

#### 📚 Additional Notes

<!-- A place for additional detail notes. -->

Co-authored-by: OCram85 <marco.blessing@googlemail.com>
Reviewed-on: #62
2022-07-15 10:59:56 +02:00

248 lines
9.3 KiB
PowerShell

function New-CredentialStore {
<#
.SYNOPSIS
Creates a new credential store File
.DESCRIPTION
You need to run this script first to create a new credential store before you try to
save new credentials with New-CredentialStoreItem.
.PARAMETER Path
Define a location for the new shared CredentialStore. The default store will be created in
$Env:ProgramData\PSCredentialStore dir.
.PARAMETER Shared
Creates a CredentialStore in the Shared mode. This enables you to read the CredentialStore Items on
different systems or profiles. In addition you can optionally provide a custom path wit the -Path parameter.
.PARAMETER Force
Use this switch to reset an existing store. The complete content will be wiped.
.PARAMETER SkipPFXCertCreation
You can skip the pfx certificate creation process. This makes sense if you have a previously created cert
or want to import a cert in cross-platform environments.
.Parameter UseCertStore
Instead of using a plain pfx file beside your CredentialStore file you can import it into the user or
machine certificate store. In this case the system itself secures the cert and you don't hat to set custom
NTFS permissions so secure your shared certificate.
.INPUTS
[None]
.OUTPUTS
['PSCredentialStore.Store'] Returns the recently created CredentialStore object if the -PassThru parameter
was given.
.EXAMPLE
New-CredentialStore
# Creates a new private CredentialStore
.EXAMPLE
New-CredentialStore -Force
# Resets an existing private CredentialStore
.EXAMPLE
New-CredentialStore -Shared
# Creates a new shared CredentialStore
.EXAMPLE
New-CredentialStore -Shared -Path "C:\TMP\CredentialStore.json"
# Creates a new shared CredentialStore in the given location.
#>
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Private')]
[OutputType('PSCredentialStore.Store')]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'Shared')]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo]$Path,
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[switch]$Force,
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[switch]$PassThru,
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[switch]$SkipPFXCertCreation,
[Parameter(Mandatory = $false, ParameterSetName = 'Private')]
[Parameter(Mandatory = $false, ParameterSetName = 'Shared')]
[switch]$UseCertStore
)
begin {
# Lets get the current Date in a human readable format.
$CurrentDate = Get-Date -Format 'u'
# test if the path input is a valid file path
if ($PSCmdlet.MyInvocation.BoundParameters.ContainsKey('Path')) {
if ($Path.Attributes -contains 'Directory') {
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.IO.InvalidDataException]::new(
'Please provide a full path containing the ' +
'credential store file name with the .json extension!'
)
}
Write-Error @ErrorParams
}
elseif ( ($null -eq $Path.Extension) -or ($Path.Extension -ne '.json')) {
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.IO.InvalidDataException]::new(
'Your provided path does not contain the required file extension .json!'
)
}
Write-Error @ErrorParams
}
}
}
process {
# 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
}
}
# Test if in the CredentialStore already exists.
Write-Verbose 'Test if there is already a credential store.'
if ((Test-Path -Path $Path) -and ($Force -ne $true)) {
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.InvalidOperationException]::new(
'The given file already exists. Use the -Force switch to override the existing store.'
)
}
Write-Error @ErrorParams
}
if (! $SkipPFXCertCreation.IsPresent) {
$CRTParams = @{
Country = 'DE'
State = 'PSCredentialStore'
City = 'PSCredentialStore'
Organization = 'PSCredentialStore'
OrganizationalUnitName = $PSCmdlet.ParameterSetName
CommonName = 'PSCredentialStore'
}
$CRTAttribute = New-CSCertAttribute @CRTParams
# If we are working with a ne shared store we have to create the location first.
# Otherwise openssl fails with unknown path
$StoreHome = Split-Path -Path $Path -Parent
if (! (Test-Path -Path $StoreHome)) {
New-Item -ItemType Directory -Path $StoreHome -ErrorAction Stop
}
$PfxParams = @{
CRTAttribute = $CRTAttribute
KeyName = Join-Path -Path $StoreHome -ChildPath 'private.key'
CertName = Join-Path -Path $StoreHome -ChildPath 'PSCredentialStore.pfx'
ErrorAction = 'Stop'
Confirm = $false
}
# test if there is already a cert
if ((Test-Path $PfxParams.CertName) -and (! $Force.IsPresent)) {
$ErrorParams = @{
Exception = [System.IO.InvalidDataException]::new(
'There is already a PfxCertificate for a private CredentialStore!'
)
ErrorAction = 'Stop'
}
Write-Error @ErrorParams
}
try {
New-CSCertificate @PfxParams
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.Exception]::new(
'Could not create the private PfXCertificate!'
)
}
Write-Error @ErrorParams
}
try {
$FreshCert = Get-PfxCertificate -FilePath $PfxParams.CertName -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
$_.Exception.Message | Write-Error
Write-Error -Message 'Could not read the new PfxCertificate.' -ErrorAction Stop
}
}
# We need to use the IDictionary to keep the property sorting in the object.
$ObjProperties = [ordered]@{
PSTypeName = 'PSCredentialStore.Store'
Version = $CSVersion
Created = $CurrentDate
PfxCertificate = $null
Thumbprint = $null
Type = $null
}
if ($PSCmdlet.ParameterSetName -eq 'Shared') {
$ObjProperties.Type = 'Shared'
}
else {
$ObjProperties.Type = 'Private'
}
if (! $SkipPFXCertCreation.IsPresent) {
$ObjProperties.Thumbprint = $FreshCert.Thumbprint
if ($UseCertStore.IsPresent) {
Write-Verbose 'Importing new PFX certificate file...'
Import-CSCertificate -Type $ObjProperties.Type -Path $PfxParams.CertName
}
else {
$ObjProperties.PfxCertificate = $PfxParams.CertName
}
}
$CredentialStoreObj = [PSCustomObject]$ObjProperties
try {
$JSON = ConvertTo-Json -InputObject $CredentialStoreObj -ErrorAction Stop
$JSON | Out-File -FilePath $Path -ErrorAction Stop -Force
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.IO.IOException]::new(
'Unable to convert or write the CredentialStore'
)
}
Write-Error @ErrorParams
}
if ($PassThru.IsPresent) {
return $CredentialStoreObj
}
}
end {}
}