Invoke SQL Command with Secure Creds - powershell

A little stuck on how I use a secure string within a Powershell command to run a remote SQL Query.
The following command run's fine in a powershell, as this is running under my own account - it also returns results when providing the values for username and password.
"SELECT COUNT(E.intEmployeeID) AS Count FROM Employees E WITH(NOLOCK)" - ServerInstance "SERVERA\INSTANCEA" -Database "DATABASEA" -u USER1 -p SomePassword
I want to automate/schedule this script and as I don't want the password in clear txt in my script, I was looking at ways of making this a secure/encrypted string. So I have created an encrypted password using the below. The problem is I'm not sure how to pass this password back into my Command..
This creates the encrypted string and stores in a file. This will be a file secured somewhere remotely.
$File = "C:\password.txt"
[Byte[]] $Key = (1..16)
$Password = "SomePassword" | ConvertTo-SecureString -AsPlainText -Force
$Password | ConvertFrom-SecureString -key $Key | Out-File $File
This then will read the encrypted file and store in secure string... But how do I get my Invoke SQL command to use this password.
$File = "C:\Cred.txt"
[Byte[]] $Key = (1..16)
$Password = Get-Content $File | ConvertTo-SecureString -Key $Key
The value for $Password is System.Security.SecureString, if I use this variable in the original command, the command fails with 'Login Failed for User'
The account being used to perform the SQL query is a SQL Authenticated account, not a Domain account..
Any advice would be Welcome
Thanks.

Create a Credential object:
$cred = new-object -typeName System.Management.Automation.PSCredential -ArgumentList $user, $pass
Then, convert password to plain text:
[string]$pass = $cred.GetNetworkCredential().Password
invoke-sqlcmd -UserName $user -Password $pass -Query 'select ##servername'
Ivoke-SqlCmd can only use plain text passwords.

It is possible to use a secure string without storing the plain text in a variable. Here is an example.
$Server = Read-Host "Server"
$Database = Read-Host "Database"
$Username = Read-Host "User"
$Password = Read-Host "password for user $Username on $Server" -AsSecureString
Invoke-Sqlcmd -Database $Database -ServerInstance $Server -Verbose -Username $Username -Password (New-Object PSCredential "userDummy", $Password).GetNetworkCredential().Password -Query "SELECT table_catalog [database], table_schema [schema], table_name name, table_type type FROM INFORMATION_SCHEMA.TABLES GO"

Related

Retrieve password from Import-Clixml to login sqlplus using Powershell Get-credential

I have a powershell script that will automatically run the SQL query in the SQLPlus hourly through the task scheduler.
The below code run successfully.
$username = "abcuser";
$password = "123pw";
$dir = "D:\SQL\Test"
cd $dir
echo exit | sqlplus $username/$password '#Justscript.sql
But I want to store the credentials through Get-Credential for security, thus did it this way. Note that I have already ran the Get-credential and exported the credentials
$db_filename = 'C:\Users\Maria Maria\db_credentials.txt'
$db_cred = Import-Clixml -Path $db_filename
$dir = "D:\SQL\Test"
cd $dir
#this doesn't work
echo exit | sqlplus $db_cred '#Justscript.sql
this doesn't work either
$username = $db_cred.UserName;
$password = $db_cred.Password;
echo exit | sqlplus $username/$password '#Justscript.sql
This was able to read the username, but the password don't. Do you have other way?
This is how you get the plaintext data from Get-Credential:
$credentials = Get-Credential
$username = $credentials.UserName
$password = $credentials.GetNetworkCredential().password

Invoke-Command with static password

I want to execute a script that requires privileges on a remote Windows Server machine , but each time i execute the script a pop-up window appears in order to type the password, so how can I add the password as a static parameter into the command so I can escape typing it each time (the password is fixe and unknown)
Here is the code that I wrote
Param (
$user
)
Invoke-Command -ComputerName 'Server.Admin.6NLG-AD' -FilePath C:\SECnology\Data\Utilities\PS_Block_Internet_Access_GPO.ps1 -ArgumentList $user -Credential ADMIN\Administrateur
You can add this code block:
$User = "UserName"
$Password = ConvertTo-SecureString -String "Password" -AsPlainText -Force
$Credential = [pscredential]::new($User,$Password)

How to handle PowerShell Secure Strings

How do I properly enter and store a secure password? I am needing to Convert it from Secure in order to put in to JSON to get a REST token.
My example is:
PS C:\Temp> $secpass = Read-Host -assecurestring "Please enter password";
Please enter password: *****
PS C:\Temp> echo $secpass
System.Security.SecureString
PS C:\Temp> $pass =
ConvertFrom-SecureString $secpass
PS C:\Temp> echo $pass
01000000d08c9ddf0115d1118c7a00c04fc297eb010000004fe37b5a39a93542a74298c3740cae0b0000000002000000000003660000c00000001000000096aa9947681adf56ce6f9fd2d9ced2140000000004800000a0000000100000006bbff8b1e2115682e9be4c775d8372ee10000000b80a4a99147901275a9080c257712b1914000000010eabc8c134837751dbd2d648dbbca1f7335e9f
PS C:\Temp>
I want to run the ConvertFrom-SecureString and get my simple plain text password back.
EDIT;
I have the following function that obtains a REST Token:
function Get-AuthToken {
Param(
[string]$server,
[string]$username,
[securestring]$password
)
$creds = #{
username = $username
password = $password
grant_type = "password"
};
Invoke-RestMethod -Method Post "$server/api/token" -Body $creds
}
To build $creds correctly the password needs to be in plain text.
Furthermore I also have the following to cover the case where password string is not given when the script is run:
If(!$Password) {
[ValidatePattern("\w+")]$password = Read-Host -assecurestring "Please enter password";
}
As per first answer given here Convert a secure string to plain text I have tried to add the following before calling the Get-AuthToken function:
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
$unsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
$response = Get-AuthToken -username $User -password $unsecurePassword -server $server
If I do a Write-Host $unsecurePassword I can see the correct string however the Get-AuthToken fails authentication.
EDIT 2:
If I change the function to :
function Get-AuthToken {
Param(
[string]$server,
[string]$username,
[string]$password
)
$creds = #{
username = $username
password = $password
grant_type = "password"
};
Invoke-RestMethod -Method Post "$server/api/token" -Body $creds
}
Which is making the $password parameter a string rather than a secure string then it works however don't believe this is best practice since Visual Studio Code is complaining.
It's not recommended for production systems but try this:
$secpass = Read-Host -assecurestring "Please enter password"
$credentials = New-Object System.Net.NetworkCredential("UserTest", $secpass, "Domain")
$credentials.Password
You cannot do that with ConvertFrom-SecureString

using secure password with multiple users without prompt

I am trying to have my password secured and stored in a file so that I don't need to enter each time when I run the script.
First step, I ran the following, entered the password which got stored into E:\cred.txt file. The txt file now contains an encrypted password.
(Get-Credential).Password | ConvertFrom-SecureString | Out-File "E:\cred.txt"
Secondly, I ran the below Script:
$File = "E:\cred.txt"
$User = "jason#domain.com"
#### I have two different user accounts, one for admin and other for operator,
#### however both user accounts use same password.
$adminuser = $User
$operator = $User -replace "#domain.com"
#### I would need to read $File to get only the password
$pass = New-Object -TypeName System.Management.Automation.PSCredential `
-ArgumentList (Get-Content $File | ConvertTo-SecureString)
$adminuser
$operator
$pass
Output:
jason#domain.com
jason
UserName Password
-------- --------
From the output, it seems New-Object refers to both UserName and Password. And when I try to connect to systems, it fails with Authentication error. Since I already have two different usernames hard coded within the script, how should I get only the password stored in $pass? or is it possible to include all usernames ($User, $adminuser, $operator) into the cred.txt file?
Try this:
#saving credentials
Get-Credential | Export-CliXml -Path c:\credential.xml
#importing credentials to a variable
$Credential = Import-CliXml -Path c:\credential.xml
Or this:
#you could then write it to a file or, i say its a better approach to a registry key
$SecurePassword = ConvertTo-SecureString -String 'P#ssw0rd' -AsPlainText -Force | ConvertFrom-SecureString
#now you are taking it back as a secure string
$RegistrySecureString = $SecurePassword | ConvertTo-SecureString
#you can aslo see the password
$UserName = "NULL"
$Credentials = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName, $RegistrySecureString
$Password = $Credentials.GetNetworkCredential().Password
#P#ssw0rd

Using Powershell -encodedcommand to pass parameters

I'm trying to find an elegant way of passing parameters to a powershell script where the string can contain any number of special characters that would need to be escaped. For example, a complex password with special characters.
I was looking at the -encodedcommand option but it seems like this is only intended to pass an encoded script block, not an encoded version of parameters.
For example, consider the following script:
param(
[Parameter()][Alias("un")][string]$Username,
[Parameter()][Alias("pw")][string]$Password
)
Write-Host "Username: $Username"
Write-Host "Password: $Password"
The string '-un testuser -pw testpw' is base64 encoded as the following:
LQB1AG4AIAB0AGUAcwB0AHUAcwBlAHIAIAAtAHAAdwAgAHQAZQBzAHQAcAB3AA==
I tried calling the script as a .ps1 file and passing -encodedcommand with the above string but got the error 'A parameter cannot be found that matches parameter name 'encodedcommand'
So, fine, this has to be a call to powershell.exe directly.
Also tried the following:
powershell.exe -encodedcommand LQB1AG4AIAB0AGUAcwB0AHUAcwBlAHIAIAAtAHAAdwAgAHQAZQBzAHQAcAB3AA== -file Base64ParamTest.ps1
This ran the script, but the parameters had no value.
This is behaving as I would expect, but not as I would hope. Is there a way to actually pass my parameters themselves as safely encoded strings?
You have to include the script invocation as part of the command e.g.:
PS> $command = "& '$pwd\login.ps1' -un testuser -pw testpw"
PS> $bytes = [Text.Encoding]::Unicode.GetBytes($command)
PS> $encodedCommand = [Convert]::ToBase64String($bytes)
PS> powershell.exe -noprofile -encodedCommand $encodedCommand
Username: testuser
Password: testpw
Here are some notes I've taken in the past on how to deal with passwords in scripts:
###########################################################
#
# Stashing passwords to avoid interactive password prompting
#
# NOT RECOMMENDED BUT IF PASSWORD IS DYNAMIC OR WIDELY KNOWN
$passwd = ConvertTo-SecureString "Not Very Secret Password" -AsPlainText -Force
# Need a way to prompt for password and use clear text password for use with net use
$cred = Get-Credential
$cred.GetNetworkCredential().UserName
$cred.GetNetworkCredential().Password
#
# SAFE BUT NOT NECESSARILY PORTABLE APPROACH
# Depends on how DPAPI works with roaming profiles
#
# Capture once and store to file
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd
$encpwd
$encpwd > $path\password.bin
# Later pull this in and restore to a secure string
$encpwd = Get-Content $path\password.bin
$passwd = ConvertTo-SecureString $encpwd
# Let's see if the rehydrate worked?
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwd)
$str = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$str
$cred = new-object System.Management.Automation.PSCredential 'john',$passwd
$cred
# NOTE: The "secret" required to rehyrdate correctly is stored in DPAPI - consequence:
# You can only rehydrate on the same machine that did the ConvertFrom-SecureString
#
# PORTABLE BUT NOT NECESSARILY SAFE APPROACH
#
# Let's do this so that it will work on multiple machines:
$key = 1..32 | ForEach-Object { Get-Random -Maximum 256 }
$passwd = Read-Host "Enter password" -AsSecureString
$encpwd = ConvertFrom-SecureString $passwd -Key $key
$encpwd
# Could easily modify this to store username also
$record = new-object psobject -Property #{Key = $key; EncryptedPassword = $encpwd}
$record
$record | Export-Clixml $path\portablePassword.bin
# Later pull this in and restore to a secure string
$record = Import-Clixml $path\portablePassword.bin
$passwd = ConvertTo-SecureString $record.EncryptedPassword -Key $record.Key
# Let's see if the rehydrate worked?
$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($passwd)
$str = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
$str
$cred = new-object System.Management.Automation.PSCredential 'john',$passwd
$cred
Start-Process powershell.exe -Credential $cred -NoNewWindow
# Portable is better BUT the secret (Key) is shared (stored with the password file)
# Can be reversed to original password - still much better than clear-text password
# stored in your script.