A while ago I got this rather annoying problem when I wanted to connect to Office 365 using PowerShell to administer my tenants. I have always connected using the classic connection script that can be found anywhere in internet. It looks like this, and had been unchanged for a couple of years:
$adminCredential = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $adminCredential -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking
Connect-MsolService -Credential $adminCredential
When run, the script began to return this error a few months ago:
Import-PSSession : Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
At C:\Users\MYUSER\office365\powershell\Connect_to_MSOnline.ps1:3 char:1
+ Import-PSSession $Session -DisableNameChecking
+ CategoryInfo : NotSpecified: (:) [Import-PSSession], ArgumentOutOfRangeException
+ FullyQualifiedErrorId : System.ArgumentOutOfRangeException,Microsoft.PowerShell.Commands.ImportPSSessionCommand
So what was wrong? The $session variable had the expected content, so I knew the New-PSSession command had finished without error, but still the Import-Session command threw the «Index was out of range» error.
At first, my suspiscion went to Microsoft and the everchanging Office 365 sphere. Could I have missed a notice saying I had to upgrade the Microsoft Online Services Sign-in Assistant? A quick check confirmed that I had the latest version, but I reinstalled it just to be safe. What about the Windows Azure Active Directory Module? Also the latest, but I reinstalled this one as well.
Still no go!
I then turned to the PowerShell version. I had upgraded to Windows 10 when it was released, and I am running PowerShell 5. I have a Windows 7 virtual machine where the connection script worked as it should, and this VM is running PS 4. I found no information indicating that the PS version should have anything to do with the problem, though.
So what else is the difference between the Windows 7 VM and my Windows 10 PC? Both are domain joined and in the same OU, so there are no computer GPOs that are only effective on one of the computers. But then I noticed that on the Windows 7 VM, I was logged in with a local account, not a domain account. Could there be a user GPO in effect? This got me thinking.
Then it dawned on me – AppLocker! We introduced AppLocker when we started our general Windows 10 deployment, and I had forgotten that they had tightened the screw with the AppLocker rules. Our AppLocker rules state that regular users are not allowed to run anything from the temp directories (C:\Windows\Temp, %TEMP% and %TMP%). This is to prevent malware to run on the computer, but it also means that files that are downloaded when the Import-Module command is run can’t execute, and the session won’t be imported.
So how can I work my way around this AppLocker policy, being a regular user? If a regular user could change the AppLocker rules locally, that would render the rules pointless. So I have to find another way to make my script run.
The solution: If I can’t run anything from the temp directory, could I possibly redirect where the script thinks the temp directory is located? It was worth a shot.
Luckily, our AppLocker rules state that all users can run their
problematic «special» programs from a predefined directory. Let’s just call it C:\Approved for now. I can manipulate the $env:temp variable inside the script, so the Import-Module command downloads it’s files to C:\Approved just for this script. You could use any other directory where regular users have write and execute access.
So I added a single line to the top of my connection script:
$env:tmp = «C:\Approved»
Then I held my breath and ran the script.
Eureka! The script ran beautifully! The module is downloaded and imported (I can see the files on the disk), and all cmdlets are available.
When the module is downloaded, it now creates a temporary directory inside the C:\Approved directory. This directory is given a name that starts with «tmp_» followed by 8 random characters and a random 3 character extension. Inside the directory three files are created. These three files have the same random name with .ps1xml, .psd1 and .psm1 extensions.
So will I have hundreds of randomly named directorys in my C:\Approved directory after a while? No, when I close my PowerShell session with the command «Get-PSSession | Remove-PSSession» or simply by closing PowerShell ISE, the directory and files are deleted automagically. Nice!
So that’s it. That is how I got my connection script running again…