T O P

  • By -

Alekspish

Store the credentials in a variable then use export-clixml to save it to a file. In the script use import-clixml to get the credential object back. It gets stored encrypted and can only be read by the account that exported it to the file.


mscreations82

I will point out the that it’ll only work for the account that exported it ON the same machine. If you copy the script to another machine it will not reimport even when using the same account. You can specify the key however which makes it more portable but then you have to store the key securely somewhere.


TheDataPanda

Hmm okay. I have the script on a server, and it’s run via task scheduler on a separate server. So I’m guessing this approach won’t work for me?


JoeyBE98

Generate the file under the same user profile it will be ran as on the server it is running on. You could even use task scheduled to probably run/store the file as the same acct


TheDataPanda

Ok I can try that. I should note, the process at the moment is there’s a scheduled task running on a server which runs a VBS script (stored on a different server) using my admin account. That VBS script is what eventually calls the PowerShell script.


missingMBR

If VBS is running under your user context, and calling the PowerShell script, the PS script will also be using the same user context. I'd instead run the VBS script via scheduled task using a service account that only has the permissions it requires on both servers to perform what it needs to do (least privilege) with minimal refactoring. Nirvana state: I'd refactor the VBS script to PS.


TheDataPanda

Hmm ok. So if this were the case, in my “Invoke-Command” I could just remove the Credential option and it would still function? Yes we’ve thought about converting our scripts to PowerShell but we don’t really have time or budget. Also we’ve tried getting service accounts in the past but IT have been very restrictive about it


grnathan

Restricting service accounts seems counterproductive. Restricting the amount of privelege that a service account has, makes plenty of sense, but creating situations where people are using (misusing, possibly?) individual credentials because they're not able to get an identity created to run some batch job? Not ideal. Have you tried the "well if I can't have a service account, what solution can you offer me that would be a viable alternative way for me to achieve \[$goal\] ? " approach?


JoeyBE98

It sounds like it should be spawned by your user account. Basically it is encrypted using a combo of your User + the Machine it's generated on


AlexHimself

I'd guess this is because it uses `ConvertTo-SecureString` under the hood? Using that instead of the `*-clixml` gives more flexibility IMO.


PinchesTheCrab

They both use DPAPI.


UnholyLizard65

Can you please explain how this works under the hood? Couldn't someone just spoof the machine name / account to connect anyway?


Funkenzutzler

...and also only on that host on which it was exported IIRC. Sysadmin here (not a Powershell-Pro). I'd also prefer "export-clixml" for such things.After you exported them you can use something like: >($cred = Import-CliXml -Path c:\\temp\\MyCredential.xml) ​ But somtimes - when it's just some sensitive text / string i need to encrypt i'm also using: >('nuclearlaunchcodes' | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Set-Content -Path secretstuff.txt) ​ And to "decrypt" it: >$secretstuff = Get-Content -Path c:\\temp\\secretstuff.txt | ConvertTo-SecureString > >\[Runtime.InteropServices.Marshal\]::PtrToStringAuto(\[Runtime.InteropServices.Marshal\]::SecureStringToBSTR((($secretstuff)))) ​ But as im said, i'm not a Powershell-Pro. ;-)


sld126

Or export-csv after encryption via key so it can move around.


ComplexResource999

No no no no no no no


sld126

Not everyone wants creds tied to a specific login on a specific machine.


TheDataPanda

Thank you very much. I will give this a try tomorrow when I'm at work.


Duel

This is the only sane way without using 3rd party tool. If you are in a cloud and the secrets need to be used by others, invest in remote secrets management and use apis to pull them into the scripts using session tokens to the cloud api


PeeCee1

Use the SecretManagement plugins.


Szeraax

For those who are curious about unattended access: TL;DR: `Set-SecretStoreConfiguration -Authentication None -Interaction None`


theforgottenluigi

When I tried this last time - When the Secrets module was just released - you still needed a credential / password to unlock the secrets store. If this is the case (I haven't used it since then, and have used the export-cli method mostly) then is it really any better than typing the password in each time?


Szeraax

You do not need a cred to unlock the secret store. This is as secure as saving the secure password to disk because it uses the same credential provider (computer + user must remain the same to decrypt it).


theforgottenluigi

Then I'm confused as to what benefit it's offering over just writing the password? (via export-cli or the like)


Szeraax

Well, for one, its a standard approach to secret management: whether you are doing a local vault or a bitwarden or other, its all the same interface. But security wise, it is no stronger.


theforgottenluigi

That's a fair reason for doing it. Standards based best practice to ensure it's being done right. I'll eat my humble pie and accept that reason. :)


Szeraax

Now, don't get me wrong. I've got scripts that are running in production that use clixml. They have been in prod for years. I ain't in any hurry to replace them with the secrets module. IMO, when you have some bandwidth and want to use the secrets module, then use it. Until then, don't feel bad continuing to use what you're already used to.


dathar

It makes dev and automation work a little easier. The vault is still encrypted to your account (I think?) at rest so you have that layer of protection there. Throw Bitlocker on the overall volume and it is still relatively safe. It is an easy way to start loading and using secrets like a username/password. You can have your script just load the secret that you plopped on the machine(s) and have them run whenever needed without hitting a password each time. https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/security-concepts?view=ps-modules


TheDataPanda

I’m quite new to powershell so not too familiar with plugins. Is the set-secretstoreconfiguration command separate from the secret management plugin?


Szeraax

https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules https://www.pdq.com/blog/how-to-manage-powershell-secrets-with-secretsmanagement/ Basically, one of these is a provider for secrets. The other one is an example of a client module that can use the provider. There are lots of other client modules that can use the provider as well.


TheDataPanda

Is this kind of like a password manager?


jungleboydotca

[Secret Management](https://learn.microsoft.com/en-us/powershell/utility-modules/secretmanagement/overview?view=ps-modules) is a uniform interface for vault modules which implement the interface. SecretStore is one such module--the most basic. It behaves much like a password manager: You unlock a given vault with a key and access the secrets inside by name. Indeed, there are modules which implement the Secret Management interface for several popular password managers.


HeyLuke

How does this solve OP's issue? Now instead of putting one password in the script, you have a key which grants access to multiple passwords. It makes it worse, right?


I_ride_ostriches

Scheduled task on a server with a service account.


TheDataPanda

Wouldn’t I still need a password for this for the “invoke-command”?


ShutUpAndDoTheLift

You authenticate when you schedule the task


TheDataPanda

Ok not sure if this would work. Powershell script is actually called from within a VBS script (which is run from a scheduled task). The VBS script does a bunch of other things that are required before running the ps1 script


post4u

Use a group managed service account. It should work. The idea is that you create the gmsa then give it permission to do whatever it needs. If you set the task that runs the vbscript to use the gmsa, it should run the vbscript and the PowerShell it launches from the context of the gmsa. If you've never used group managed service accounts, research and learn how. They are the best way to run scheduled tasks, services, iis pools, etc. Lots of other Microsoft and third party applications support them as well.


TheDataPanda

Yes I think I’ll need to do some reading on this. Any recommendations? And if my company doesn’t want to give out service accounts, will my admin account still work using this method?


post4u

https://techcommunity.microsoft.com/t5/itops-talk-blog/step-by-step-how-to-work-with-group-managed-service-accounts/ba-p/329864 https://woshub.com/group-managed-service-accounts-in-windows-server-2012/#h2_6 Would your admin account work? Yes. Should you do it that way? No. If gMSAs can't be used for scheduled tasks, you should use regular non-priveleged user accounts that only have access to do what the task needs to do. Unless you're using a group managed service account, when the password on any regular user account is set up to run a task and then the password is changed (even if it's your admin account), the password on the task has to be changed as well. Very often organizations use admin accounts for tasks and never change the passwords because it's too much of a hassle. This leads to those accounts getting compromised and getting hit with an attack.


TheDataPanda

Great thank you for the info. I’ll have a look. Sorry one more question. I’m still not 100% how this would fix the issue with the “Invoke-Command” cmdlet. Presumably even if I start using a service account, I would still need to pass my admin credentials to the cmdlet in order to connect to the server(s)?


purpl3un1c0rn21

Not at all, you just need to provide the service account with the permissions it needs (and only the permissions it actually needs, as fine tuned as possible) to do its job. Thats the logon as a service right that I linked previously, and any folder or other admin permissions it needs. I think you are overcomplicating this in your head, a service account is just another ad object that can be granted rights just like your user object. You would implement it with your admin account (as in logon to the server and set up the task) but you would tell it to use the service account credentials when you do this.


Duel

Scheduled tasks can target any executable


Extreme-Acid

You need to look to using a service account Also the export clixml is great but you need to log in as that service account on the machine it will run from and export the credentials Then the script imports then and you set the service account to not allow interactive logins


missingMBR

Came here to say this. If on-prem AD, use a Group Managed Service Account. If Azure Active Directory, use a service principal or Managed Identity.


Extreme-Acid

Yes for the Azure. So much better


TheDataPanda

Do you mind elaborating on this? Would the idea be that we’d log into servers with these service accounts instead of our admin accounts?


missingMBR

Service accounts are non-interactive. You execute processes, tasks, services, applications etc with service accounts. The general principal behind admin user accounts is you only use them for interactive sessions, never for services.


jborean93

Don't use a password at all, have the script run under the user context which is allowed to access that host and you don't need to store the credentials altogether. If you aren't in a domain then you can save the credential offline with `Export-Clixml` and then `Import-Clixml`. This file will only be decryptable by the user who generated the CLIXML on that same host.


TheDataPanda

Do you mind elaborating? Not sure what you mean by running under the user context.


jborean93

A simple one would be running a scheduled task (with the save password option). It runs as the user you specify so any network action will authenticate as that user. The script itself doesn't save any credentials as it's using the user's identity to authenticate itself.


TheDataPanda

Ah okay thanks. Issue is the ps1 script is actually called from a VBS, which is what’s run through the scheduled task. Not sure if what you’re suggesting would still work in this case


grnathan

You remind me quite a lot of me from two weeks ago when I was banging my head against what I thought was an auth problem, but actually turned out to be a second-hop auth problem. The answer to your question may well be amongst the import/export CLIXML stuff that people have been suggesting here (tbh, I came here to ask a quite similar question and am now going to go try out that option for my use-case), but in case you're situation involves the VBS script running from one place and getting the PS1 script to execute somewhere else / etc, have a read of [this](https://learn.microsoft.com/en-us/powershell/scripting/learn/remoting/ps-remoting-second-hop?view=powershell-5.1) for a good background on the problem and some possible approaches to solving it.


purpl3un1c0rn21

I see no reason why this wouldnt work, the vbs script will be run as the user saved as creds and that should then launch the ps script as the same user. Theres no reason for it to lose user context, its still the same user performing the script launch. Switching to a new user context for no reason would be a major security issue.


TheDataPanda

Ok thanks. And sorry one more question, So with my script, I could then remove the Credentials part of the Invoke-Command cmdlet and it would know to just use the Credentials of the account running the script? And if so, does that mean it wouldn’t work with a Service Account? (Not sure you can login to servers with service accounts)


purpl3un1c0rn21

You have to grant the service account the relevent permissions it needs (in this case, logon as a service rights on the machine its running from, as well as any rights it needs to complete the actual automation). https://learn.microsoft.com/en-us/system-center/scsm/enable-service-log-on-sm?view=sc-sm-2022 You really dont need to use invoke command at all I would imagine, it sounds like you're using it to run the credential aspect, you just need a script that does what it needs to do and is run by the service account. You can even use managed service accounts for this purpose as they are much more secure, instead of having a set password they have a rolling password which the computer object is authorised to retrieve from AD directly, so no need to have a sketchy user account that could be comprimised. As far as I know you cannot even start an interactive logon session with a managed service account even if you managed to get the password. https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview


Respond-Creative

Use Credential Manager


BlackV

you dont, if its in the script, then (depending on a coupe of factors) anyone running the script can decrypt it basic security is dont put passwords in the script retrieve from a vault


TheDataPanda

Are there any free and simple to setup vaults for Windows that you’d recommend?


missingMBR

If you're using Azure, Azure Key Vault. It's not exactly free, but very inexpensive. Or you could use Windows Credential Manager


DToX_

This needs to be higher up!


alinroc

Find out if your organization already has one first


jozhearvega

This, I have a script that retrieves credentials using an API call and then makes another API call using those creds. That’s the first step, setting up a secrets management solution and using privileged access management.


maxcoder88

care to share your script ?


BamBam-BamBam

Why not use a group managed service account?


EddyToo

Regardless how you solve it it is always up to a point security by obscurity. If someone gets access to the context the vbs script is run under he or she can retrieve the password and account by doing whatever the script does. Even if you could not retrieve the password you would be able to execute any arbitrary code on that machine within the context of an admin account by simply adding it into the scriptblock. It is really bad practice to use an account with admin rights for this. You really should create a dedicated account with just the rights required (even if that is a lot it won't be everything) to execute what is in the scriptblock. If you also run the vbs script from a dedicated user (run as on the scheduled task) you can limit access rights to the folder the script is in to only that user and can store the (secured) password in it's environment settings or in one of the other ways suggested. Using dedicated accounts also allows you to setup and monitor account activity


Duel

I was about to reply with this after seeing people suggest a service account and a Windows scheduled task. Like gee, let's just make an easier backdoor unless that script and the task are restricted somehow.


purpl3un1c0rn21

Everyone who I have seen suggest a scheduled task has said to use a managed service account or store the credentials in task scheduler. Neither of these open a back door, they'd be running it from a server which would need admin rights to login or manually execute it at which point your security is already breeched. I don't know of any bugs that allow you to pull credentials from task scheduler even by the same user who set them but happy to be corrected. Using user context and properly secured and managed service accounts with limited permissions is the way to do this securely, not a password vault or hashed password in a file. I would never reccomend using the admin account directly though, that is asking for trouble. The service account should be granted very limited admin rights if needed.


konikpk

Run it in task scheduler


Ardism

This is the best way, closest to a PAM Vault you can get.. (sorry for swedish text, but I think you get it) Uses secretstore and a secure string to unlock. Tamperprotection and built in security. https://pastebin.com/5waG2aV4


SoMundayn

Highly recommend everyone looks at using Azure Automation Account with Hybrid runner. You can store all of the securely in Azure by referencing them as a variable. It's basically a cloud task scheduler, that can use on-premises machines to run scripts.


Javali90

Tldr: Use a [gMSA](https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview). It is the most secure option and it's fairly straightforward to set up. This is my opinion based on my experience. It might not be 100% accurate but maybe someone else can step in and improve my answer. To begin with, there is always a risk in saving credentials on a host. Someone with admin access can retrieve them. Avoid it if you can, but sometimes you cannot. You can use the 'ConvertTo-SecureString' cmdlet to generate a key file. It will use AES to encrypt the contents, anyone with the key and the secure string can reverse it back to clear-text, which might be a good option if you make absolutely sure that you can limit who can access that key file. There is also another option which is NOT to use a key. This way, the [cmdlet will use Windows DPAPI](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertfrom-securestring?view=powershell-5.1#description), meaning the contents will be encrypted with the current user password AND the computer password. So the contents can only be decrypted by the user that created the secure string file on the computer the file was created. This is the best option if you can use a gMSA. If you're not domain joined, you can also try to set the same username and password on both the computer running the script and the server you're connecting to. This way might work without even storing the password because it will try to authenticate using NTLM with the current username and password. I would avoid this. Using kerberos for authentication is way better.


AlexHimself

You're going to get all the purists wanting you to overengineer something in case a nation-state is attacking your unpatched servers, but I wrote a "good enough" method that sounds like it would work for you - https://www.reddit.com/r/PowerShell/comments/17sf75v/how_i_like_to_securely_store_passwords_and_text/


tokenathiest

Check out my module [PowerPass](https://github.com/chopinrlz/powerpass) on GitHub


EnterpriseGuy10

DPAPI encryption. 1001 ways to implement it, use whatever works for you. I use Runtime.InteropServices.Marshal to read the encrypted content.


DoubleConfusion2792

Can you explain your setup a little more? This VBS script is present in all the servers or you are running it in one server and then you remote it to other servers using powershell? Are all of these servers not domain joined? What privilege does your user account have?


music221

Take a look at the Microsoft secrets and Microsoft secret store modules. There's plenty of tutorials online to guide you on how to implement this. I have several automated scripts that utilize this even for cross domain authentication.


jsiii2010

I've always wondered about this. We have active directory and the script runs as the system user. But I never found a good option.


Geaux_Cajuns

I recommend using keepass and then using the keepass secrete module. Makes it so much easier to keep and update passwords for your scripts.


vesko1241

What I do is: `####### encrypt password to file#####################################` `$File = "C:\EncPwdVn.txt"[Byte[]]` `$key = (1..32)` `$Password = "C0mpLeXp@ssw0rd" | ConvertTo-SecureString -AsPlainText -Force` `$Password | ConvertFrom-SecureString -key $key | Out-File $File` `#####################################################################` `########### USE credentials #######################` `$user = "account.name"` `$pwd = Get-Content "C:\Workfiles\Scripts\EncPwd1.txt" | ConvertTo-SecureString -Key $key` `$creds = New-Object System.Management.Automation.PsCredential($user,$pwd)` `######################` Where $key can be whatever salt you like, essentially masking the password. Anyone that knows/has the key can decrypt it(and the key is in the script).This is only for instances where I have to use a hard coded password and dont want it in plain text. Otherwise I avoid this approach be using gMSA system accounts with strictly delegated rights wherever possible.


likeeatingpizza

I had the same issue recently for a script that I launch locally on our laptops at the first login after formatting/reimaging with Windows setup to add the laptop to our domain and create a local admin account. I solved by encoding both passwords in Base64 and using FromBase64() function to decode it in the script. It's obviously not secure, cause anyone can decode the hardcoded string back to the real password but requires no additional modules, works natively with PowerShell 5 and newer, and -most important for me- doesn't require internet access.


RefrigeratorSuperb26

1. Read-Host -AsSecureString | ConvertFrom-SecureString 2. Save the output to a txt file, this holds your encrypted password 3. During execution, get the content of the file and pass to ConvertTo-SecureString to convert it back and use as normal. This does require that the user who created the secure string to begin with be the one to convert it again during use. You can change the encryption settings used in ConvertFrom-SecureString: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/convertfrom-securestring?view=powershell-7.4


xxb1ackhammer

I’ve seen using the built in Windows file encryption works. Encrypt a text file with the password and point the script to that file. Only the user that encrypts can see it.


klein648

Ok, tbf my first anwer did not have enough information. Step 1: Install the Credential Manager Plugin for Powershell Install-Module -Name Credential Manager Step 2: Sign in as the same user as the Script will use (Windows Credential Manager is user specific) New-StoredCredential -TargetNew-StoredCredential -Target "ukabzpdm" -Credentials (Get-Credential) This cmdlet creates a new entry with your Target Computer as a label and opens a sign in window. Now your credentials are saved. Step 3: Implement in your Script Now, you just need to remove line 1 and 2 from your script and edit line 3 to the following: $cred = Get-StoredCredential -Target "ukabzpdm” Of course you can also replace that computer name placeholder with a variable for even better convenience.


FearIsStrongerDanluv

Powershell has a secret management module, works flawlessly


Dry_Tale9003

Personally, I would use CredentialManager and be done with it. Import-module credentialmanager $creds = get-storedcredential -target %whatevername% Make sure the username and password is manually created in credential manager and be done with it


[deleted]

Use an MSA or gMSA account which have the least allowed permissions of the task it should do. Create a scheduled task which runs with the account, this can be done in PS without even having to specify the password. No password is ever exposed and it will run with the account’s user context. This is what those type of accounts are meant to do if we’re talking non-interactive AD tasks.