- Manage Azure Policy using Terraform - Tue, Aug 2 2022
- Getting started with Terraform in Azure - Tue, Jul 12 2022
- Azure Bicep: Getting started guide - Fri, Nov 19 2021
The examples I'm going to focus on primarily relate to scheduling PowerShell scripts for Exchange, Skype for Business, and Office 365 services. Let's start with on-premises Exchange and Skype for Business. I often schedule scripts for creating new mailboxes or user accounts, or for gathering statistics for a dashboard. Connecting to the Exchange or Skype for Business servers from a remote system requires creating a remote PowerShell session, or PSSession. This is done by creating a credential object and using it when creating the PSSession. Here is an example for connecting to on-premises Exchange Server:
$UserCredential = Get-Credential $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://<FQDNExchangeServer>/PowerShell/ -Authentication Kerberos -Credential $UserCredential Import-PSSession $Session
The Get-Credential command will open a dialog box for inputting a username and password. This is great for an interactive session, but the point is to automate the task so you don't need to worry about inputting credentials. Instead of prompting for credentials, you can store them in the script to create the credential object for the session. For example:
$Username = "domain\username" $Password = "Passw0rd123!" | ConvertTo-SecureString -AsPlainText -Force $UserCredential = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$Password
Now you can use the $UserCredential variable to create the PSSession in the first example. However, note that is not secure, and I do not recommend this as an option. This should only be used for testing or troubleshooting and not in a production script.
A better option would be to use the built-in security options to run the task as a different user account. The security options in the Create Task wizard allow for selecting another account in order to run the PowerShell script as that user account.
When you go to save the task, the wizard will prompt for the alternate user account's password, like this:
When creating the PSSession in the script, instead of using Kerberos as the Authentication Type and a credential object, use NegotiateWithImplicitCredential, like this:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://<FQDNExchangeServer>/PowerShell/ -Authentication NegotiateWithImplicitCredential
This authentication type will use the credentials of the current logged-on user account for the PowerShell window. For the scheduled task, this account is our ScheduledTaskUser as the task is being run as this service account. The advantages here are that no passwords are being stored in the script and you don't have to create a credential object. However, if this service account's password is changed, you must go into each scheduled task and re-save the password. This could be a chore if you have multiple servers where scripts are stored or if the password must change frequently.
Using the built-in security options when creating a task is useful in most scenarios; however, you will run into an issue using this with Office 365. When creating a PSSession for Exchange Online, you must use a credential object and use Basic for the Authentication Type. This means you cannot use the account that is running the PowerShell window in the scheduled task.
To solve this issue, we have a third option of storing encrypted credentials to a file on the computer. The PowerShell script uses the encrypted password from the file to create a credential object. In order to create the encrypted file, first create and store a credential object on the computer where the task is scheduled using the Get-Credential command:
Next, convert the password stored in the credential object to an encrypted text file using the ConvertFrom-SecureString command:
$UserCredential.Password | ConvertFrom-SecureString | Out-File C:\ps\ScheduledTaskUser.txt
The contents of the text file will not contain the password in plain text but should look something like this:
To use the password, you will get the contents of this file and use ConvertTo-SecureString to store the password in a variable. You then use it to create a credential object to use when creating the PSSession. An example using this technique looks like this:
$Username = "domain\username" $SecurePassword = Get-Content C:\ps\ScheduledTaskUser.txt | ConvertTo-SecureString $UserCredential = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$SecurePassword $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection Import-PSSession $Session
You might be thinking that anyone will be able to use this encrypted file, but this is not the case. What makes this method a little more secure is that the encryption uses the Windows Data Protection API; only the person who encrypts the password can decrypt it again and only on the computer it was encrypted on. Even if someone had the credentials of the person who encrypted the file, they would not be able to decrypt it if they copied the file to another computer.
When creating the password file, I suggest starting PowerShell as the service account that will be used in the script in order to create the encrypted password file. Use the same service account in the security options when creating the scheduled task as illustrated earlier.
Subscribe to 4sysops newsletter!
The PowerShell window inside the scheduled task will run as the service account and have permissions to get the password from the encrypted file as it was the one to encrypt it in the first place. This way if anyone gets access to the file, they will not be able to decrypt it unless they have the service account's credentials. This method still requires that the service account's password is kept in a secure place, access to which is limited to a few people.
I recall a post by The Scripting Guy that used this command to securely store the credential:
Get-Credential <domain\usersname> | Export-Clixml c:\data\PowerShell\Secure_credentials.xml
I then use this function to connect to my Exchange server and recall the script was similar when I used Office365.
function Connect-Exchange
{
$UserCredential = Import-Clixml c:\data\powershell\Secure_credentials.xml
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://srvexchange.<domain>/PowerShell/ -Authentication Kerberos -Credential $UserCredential
Import-PSSession $Session
}
Can you explain the difference between ConvertFrom-SecureString and Export-Clixml ?
Can you also explain the risks in entering (and storing) the password for a service account in Task Scheduler? How is that password secured?
Thank you.
Hi Paul, apologies for the late reply. I believe you might be referencing this article (https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/25/use-powershell-to-explore-office-365-installation). From what I can tell in the article, Export-CliXml uses ConvertFrom-SecureString, so both commands get to the same result. Just depends on if you want to store it as a text file or in XML code. It looks like XML stores the whole object with both username and password while my method just stores the password. You would still need to provide the username, like I did using a variable.
As for storing the password for a service account in Task Scheduler, the password is stored in the Windows Credential Manager. It is encrypted and should be pretty secure.
Thanks Jeff.
You can use Cerdential Manager module to store a user/password in Windows Credential Manager. It’s easy, fast and secure. Check this out: https://www.powershellgallery.com/packages/CredentialManager/1.0
Great post!!!
The tidbit about ConvertTo/From-SecureString using Windows Data Protection, rendering the file only of use to the original owner, is very great to hear/know. It changes everything!
Thanks for the great article.
Original account as well as the original computer 😉
Will the “New-PSSession -Authentication NegotiateWithImplicitCredential”-method be possible to use with the Connect-AIPService?