functions/Save-MDCredential.ps1
function Save-MDCredential { <# .SYNOPSIS Stores credentials securely for use by the specified account. .DESCRIPTION This command encrypts credentials to a protected credentials file in the file system. This is designed to allow storing credential objects for use by scheduled task that run as SYSTEM or a service account. .PARAMETER TargetCredential The credentials to encrypt and write to file. .PARAMETER Path The path where to store the credential. Always considered as local path from the computer it is registered on. .PARAMETER AccessAccount The account that should have access to the credential. Defaults to the system account. Offer a full credentials object for a regular user account. .PARAMETER ComputerName The computer(s) to write the credential to. .PARAMETER Credential The credentials to use to authenticate to the target system. NOT the credentials stored for reuse. .EXAMPLE PS C:\> Save-MDCredential -ComputerName DC1,DC2,DC3 -TargetCredential $cred -Path "C:\ProgramData\PowerShell\Tasks\tesk1_credential.clixml" Saves the credentials stored in $cred on the computers DC1, DC2, DC3 for use by the SYSTEM account #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")] [CmdletBinding()] Param ( [Parameter(Mandatory = $true)] [PSCredential] $TargetCredential, [Parameter(Mandatory = $true)] [string] $Path, [PSCredential] $AccessAccount, [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [PSFComputer[]] $ComputerName = $env:COMPUTERNAME, [PSCredential] $Credential ) process { $parameters = @{ ArgumentList = $TargetCredential, $Path, $AccessAccount ComputerName = $ComputerName Credential = $Credential } Invoke-PSFCommand @parameters -ScriptBlock { Param ( [PSCredential] $Credential, [string] $Path, [PSCredential] $AccessAccount ) #region Folder Management if (Test-Path -Path $Path) { $item = Get-Item $Path if ($item.PSIsContainer) { $folder = $item.FullName $file = Join-Path $folder 'Credential.clixml' } else { $folder = Split-Path $item.FullName $file = $item.FullName } } else { if ([System.IO.Path]::GetExtension($Path)) { $folder = Split-Path $Path $file = $Path } else { $folder = $Path $file = Join-Path $folder 'Credential.clixml' } } if (-not (Test-Path -Path $folder)) { $null = New-Item -Path $folder -ItemType Directory -Force -ErrorAction Stop } #endregion Folder Management #region Access Privileges $accessUserName = $AccessAccount.UserName if (-not $accessUserName) { $accessUserName = "SYSTEM" } $acl = Get-Acl -Path $folder if (-not ($acl.Access | Where-Object IdentityReference -like $accessUserName | Where-Object { ($_.FileSystemRights -Band 278) -and ($_.FileSystemRights -Band 65536) })) { $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($accessUserName, 'Read, Write', 'Allow') $null = $acl.AddAccessRule($rule) $acl | Set-Acl -Path $folder } #endregion Access Privileges #region Create Task $folderCleaned = (Get-Item $folder).FullName $credFile = "{0}\{1}.txt" -f $folderCleaned, ([guid]::NewGuid()) $task = { $password = [System.IO.File]::ReadAllText("<credfile>") Remove-Item -Path "<credfile>" $credential = New-Object PSCredential("<username>", ($password | ConvertTo-SecureString -AsPlainText -Force)) $credential | Export-Clixml -Path "<exportPath>" } $commandString = $task.ToString().Replace("<credfile>", $credFile).Replace("<username>", $Credential.UserName).Replace("<exportPath>", $file) $encodedCommand = [convert]::ToBase64String(([System.Text.Encoding]::Unicode.GetBytes($commandString))) $action = New-ScheduledTaskAction -Execute powershell.exe -Argument "-NoProfile -EncodedCommand $encodedCommand" if ($accessUserName -ne "SYSTEM") { $principal = New-ScheduledTaskPrincipal -UserId $accessUserName -LogonType Interactive } else { $principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType Interactive } $taskItem = New-ScheduledTask -Action $action -Principal $principal -Description "Temporary Task" $parametersRegister = @{ TaskName = "TempTask_$([guid]::NewGuid())" InputObject = $taskItem } if ($accessUserName -ne "SYSTEM") { $parametersRegister["User"] = $AccessAccount.UserName $parametersRegister["Password"] = $AccessAccount.GetNetworkCredential().Password } $null = Register-ScheduledTask @parametersRegister #endregion Create Task #region Perform Encryption [System.IO.File]::WriteAllText($credFile, $Credential.GetNetworkCredential().Password) Start-ScheduledTask -TaskName $parametersRegister.TaskName Start-Sleep -Seconds 5 #endregion Perform Encryption #region Cleanup Unregister-ScheduledTask -TaskName $parametersRegister.TaskName -Confirm:$false if (Test-Path -Path $credFile) { try { Remove-Item $credFile -Force -ErrorAction Stop } catch { Write-Warning "[$env:COMPUTERNAME] Clear Text Credential File still exists!! $credFile | $_" } } if (-not (Test-Path -Path $file)) { throw "[$env:COMPUTERNAME] Failed to create credential file! ($file)" } #endregion Cleanup } } } |