PowerShell Script Authentication to Office 365
Connecting PowerShell to the cloud is a fairly simple process. This requires the correct version of PowerShell and potentially specific PowerShell modules to be installed, but the connection process is relatively straight forward.
When connecting PowerShell, we need to provide credentials that have permissions to manage Office 365 or Azure services. We typically enter these dynamically, but what if we needed an automated script to authenticate? How can we securely store credentials that may have Global Admin permissions to our tenant that not even other domain admins can access?
Fortunately, there is a relatively simple way to do this.
Create the Stored Credential
The first thing we need to do is get the credential we want to use in our script to authenticate to the cloud service. This can be an on-premises synchronized account or a cloud-only account, but either way it needs to be assigned the necessary permissions to connect via PowerShell and perform the administrative tasks the script contains.
$cred = Get-Credential
This stores our credential in the variable $cred so we can pull out the password. If we try to look at the password ($cred.Password), we are simply shown the data type being returned and cannot actually see the password itself.
What we need to do now is extract the password so that we can use it in our script later on. To do this, we run the following, which saves the password into a file on the local machine.
ConvertFrom-SecureString $cred.Password | Out-File "$env:USERPROFILE\desktop\pswd.cred"
First, let's take a look at what the ConvertFrom-SecureString cmdlet does. This converts a secure string, which in our case is the password, to an encrypted text string. We could specify a key value to use AES encryption, but without the key parameter specific, this uses the Windows Data Protection API to encrypt the string using the currently logged-in user and computer account.
What this means is that this encrypted text string can ONLY be decrypted using the user account that performed this command on the computer the command was performed from. Since we saved the output to a file on our desktop (pswd.cred), this means that even if someone else was able to gain access to this file, they would not be able to reverse the encryption to see the saved password.
Note: When saving to an environment variable, which I have done above using $env:USERPROFILE, make sure to use the double-quotation to indicate a string. If you use an apostrophe or single-quote instead, the environment variable will not be recognized.
Using the Stored Credential
Now that we have our securely saved password, we can now write a script to automatically connect to cloud services. The first thing we need to do is rebuild the credential within our script file.
To do this, we need to reverse the password encryption. Remember, this can only be done using the user account on the computer in which the secured password file was created, otherwise you will not be able to decrypt the password string in the file.
$pswd = Get-Content "$env:USERPROFILE\desktop\pswd.cred | ConvertTo-SecureString
What this does is convert the encrypted password string to a Secure String in PowerShell. We cannot view the contents of this secure string object, so even though we were able to decrypt the password string, we do not get visibility into what the password actually is.
Now that we have the password in a secure string object, we can rebuild the credential object to use to authenticate to the cloud.
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList "email@example.com", $pswd
Here, we create a new credential object using New-Object System.Management.Automation.PSCredential and set it to the variable $cred. We need to specify arguments to define the username and the password for the credential, which is done as a comma-separated list. The first is the username. In the example above where we extracted the password, we created a credential object using the username firstname.lastname@example.org.
The second argument is our password, which we stored in the variable $pswd above. The result is a credential object ($cred) that can now be called to authenticate to cloud services.
For example... Connect-MSOLService -Credential $cred
This can be used in any PowerShell script to automate the connection to Office 365 or Azure and perform administrative tasks.
If your script is called as a scheduled task, the task simply needs to be on the computer that the encrypted password string was created on and the task must run under the context of the user account that created the encrypted password string.
Note that this will not work if the account used for authentication is enabled for multi-factor authentication. In this case, you would need to complete the MFA challenge, which cannot be performed by the script itself.
Using AES instead of WDP
As mentioned above, when we extract the password for the credential we do so as an encrypted text string. By default, this uses the Windows Data Protection API, which uses the currently logged-in user account and computer account to perform the encryption. This restricts the ability to decrypt the text string on any other computer using any other computer.
We could specify a 128, 192, or 256 bit key to use instead, which would use AES instead of the Windows Data Protection API. To do this, we need to create a key value first.
What is a key? Well, technically it is just an array of bit values. So we can create a key simply by creating an array in PowerShell.
$key = (2, 64, 246, 194, 95, 37, 10, 3, 185, 205, 198, 169, 49, 73, 12, 201, 48, 18, 85, 184, 163, 189, 204, 4)
Here, we are specifying a 192-bit key. This is because our key is an array of 24 digits, and each digit represents a byte. Because each byte is 8-bits, we have a 192-bit key (24 x 8 = 192). The important part here is that each value in the array is less than the value of 256, as 8-bits can represent the maximum value of decimal value of 256.
Now that we have our key, we can convert a secure string to an encrypted text string.
ConvertFrom-SecureString $pswd -Key $key | Out-File "$env:USERPROFILE\desktop\pswd.cred"
Instead of using the Windows Data Protection API, which uses the currently logged-in user account and computer account to encrypt the text string, we now use the AES algorithm to perform the encryption using our 192-bit key.
To decrypt this encrypted text string, we simply need to provide the key in the ConvertTo-SecureString cmdlet.
$pswd = Get-Content "$env:USERPROFILE\desktop\pswd.cred | ConvertTo-SecureString -Key (2, 64, 246, ... 4)
We could also specify our key variable here instead of the actually array of numbers.
The benefit of this approach is that this can make your script to automate administration tasks in the cloud portable to other computers and usable by other users as long as they have the key value. The downside is that this requires you to protect the key value and securely share it for your script to be used by others.