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. .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. .NOTES ``` File Name : New-CredentialStore.ps1 Author : Marco Blessing - marco.blessing@googlemail.com Requires : ``` .LINK https://github.com/OCram85/PSCredentialStore #> [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 -UFormat "%Y-%m-%d %H:%M:%S" # Set latest Credential Store version # Set-Variable -Name "CSVersion" -Value "2.0.0" -Option Constant -Scope # 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 conain 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-CRTAttribute @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-PfxCertificate @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 (! $SkipPFXCertCreation.IsPresent) { $ObjProperties.Thumbprint = $FreshCert.Thumbprint if (!$UseCertStore.IsPresent) { $ObjProperties.PfxCertificate = $PfxParams.CertName } else { Write-Verbose 'Importing new PFX certificate file...' Import-CSCertificate -Path $PfxParams.CertName -StoreName My -StoreLocation CurrentUser } } if ($PSCmdlet.ParameterSetName -eq "Shared") { $ObjProperties.Type = "Shared" } else { $ObjProperties.Type = "Private" } $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 { } }