Home » Blog » Saving Credentials for Office 365 PowerShell Scripts and Scheduled Tasks

Saving Credentials for Office 365 PowerShell Scripts and Scheduled Tasks

PowerShell is an efficient way to perform management tasks for Office 365, and also allows a great deal of automation through the use of PowerShell scripts to perform routine and repetitive tasks.

One of the challenges when using PowerShell for automation is handling authentication for the connection to various Office 365 services. Running a script as a scheduled task means that it needs to be able to use a set of stored credentials to authenticate to Office 365. Storing passwords in scripts is obviously not a good idea, because the passwords could easily be exposed if the script is obtained by another person who uses the same computer, or by accidentally sharing it online (such as on GitHub) with the creds still included.

Another challenge is constantly need to access the passwords to login. I keep my Office 365 admin credentials in a password database, and hopefully you use one as well (LastPass, 1Password, KeePass, Dashlane, etc). This makes it easy to have multiple credentials (such as admin accounts for different tenants, or for different tiers of administrative privilege) that each have unique, complex passwords. Thanks to web browser plugins for the likes of LastPass and 1Password it’s easy to login to Office 365 web-based portals, but copying and pasting passwords into PowerShell authentication prompts gets quite tedious.

I solve both of those problems by storing credentials on my admin workstation. This requires no special tools; it can achieved by using built-in PowerShell functionality. If the idea of storing credentials on your computer sounds like a security risk, please read on.

First, let’s look at how you might typically connect to Office 365 using PowerShell. The Connect-MsolService cmdlet will connect you to an Office 365 for management tasks such as assigning a license to a user.

The cmdlet will prompt you for credentials to use for authenticating the session.

new-stored-credential-popup

To avoid that credential prompt for repeat connections, you can use Get-Credential to capture your username and password as a credential object in PowerShell first, and use that for subsequent commands. For example:

That solves part of the issue (repeatedly entering credentials), but doesn’t solve the issue of managing multiple accounts (should you create several credential objects every time you open a PowerShell console?) or running scheduled tasks (since you won’t be there to answer the credential prompt for the script).

But what we can do is store the password from the credential to a file on our computer. The password value is stored in memory as a secure string, and can be encrypted and stored in a text file, for example:

The text file contains the encrypted value, which will look something like this.

powershell-stored-credential

Now, at this point you may be concerned that anyone who has access to that file could decrypt the password. Fortunately, that’s not the case. The ConvertFrom-SecureString cmdlet encrypts the password using the Windows Data Protection API. In short, this means that only the user who encrypted the password can decrypt it again, and they can only do so on the same computer that it was encrypted on. If the text file is accessed by another person, or by the same user on a different machine, they’ll be unable to decrypt it.

Of course, you should still protect the files from being accessed by others, in much the same way you’d protect your SSH keys or your password database. For example, store them in a location on the workstation that is not included in cloud storage or backups, and is on a Bitlocker encrypted disk.

The downside of this approach is that you won’t be able to simply copy your stored credentials to another computer. Instead, you’ll need to recreate them on the new computer, which is not a huge hassle since you only need to do it once. There is another approach that avoids this problem, but it has other caveats. I’ll go into that in a separate article some other time.

So now that you can see how passwords can be securely stored and retrieved, how can this technique be used to manage stored credentials for day to day use, as well as for scheduled tasks? I’ve written some PowerShell functions that make it easy.

Preparation Tasks

The two functions that I’ve written use a variable named $KeyPath, and I recommend you add that variable to your PowerShell profile. If you don’t, the functions will prompt you for a path to save or retrieve credentials from, and that’s just going to get annoying.

Download the script containing the two functions, and run it to load the functions in your PowerShell console. I recommend adding them both to your PowerShell profile as well so that you can re-use them in any PowerShell session.

Storing a New Credential

The first function will save a new stored credential. Simply run New-StoredCredential and enter the username and password when prompted.

new-stored-credential-popup-2

The credential will be saves as a file named username.cred in the path that you configured for $KeyPath.

Using a Stored Credential

You can list the stored credentials in your $KeyPath folder by running Get-StoredCredential with the -List parameter.

Get-StoredCredential can also be used to retrieve the stored credential and save it as a variable to then use with a cmdlet that requires authentication, for example:

You can also use Get-StoredCredential in a one-liner like this.

The difference between the examples above and the use of Get-MsolService earlier in this post, is that using the stored credential avoids the need to respond to an authentication prompt. So your passwords can be unique and complex without you needing to remember them or copy/paste them from a password manager.

And it’s not just for connecting to Office 365 services. You can use the stored credentials for pretty much any PowerShell cmdlet that uses credentials.

If you try to use a credential that doesn’t exist, you’ll simply get an error.

Using a Stored Credential for a Scheduled Task

The Get-StoredCredential function can also be easily integrated into scripts that you are running as scheduled tasks. The most important thing to remember is that the password can only be decrypted by the user account that encrypted it in the first place.

If your scheduled tasks run using a service account, you’ll need to log on as that service account to run New-StoredCredential, or use Runas to launch a PowerShell console as the service account. The stored credential file will also need to be located in a folder that the service account can access, and that location needs to be defined as $KeyPath in your script as well.

Download the New-StoredCredential and Get-StoredCredential Functions

The script to load the functions is available on GitHub. Comments and feedback are welcome in the comments below, or you can raise an issue on GitHub if you find a bug or have a suggested improvement to the script.

Paul is a Microsoft MVP for Office Servers and Services. He works as a consultant, writer, and trainer specializing in Office 365 and Exchange Server. Paul is a co-author of Office 365 for IT Pros and several other books, and is also a Pluralsight author.
Category: Blog

2 comments

  1. Rob de Jong says:

    Hi Paul,

    I noticed you are using the older MSOnline PowerShell module in your examples. It may be useful to start using the newer Azure Active Directory PowerShell V2 module instead, as we will begin deprecating the MSOnline module when we have migrated the functionality of the MSOnline module to the newer module – currently planned for the Spring of 2017.

    Thanks,

    Rob de Jong

    • My function works with any cmdlet that has a -Credential switch, so it’s just an example.

      I’ve been playing with the new AzAD PS V2 module. Spring is a long time away here in Australia. Or perhaps you mean Spring in the northern hemisphere. I can never tell with these season-based release timelines.

Leave a Reply

Your email address will not be published. Required fields are marked *