How to setup and run Azure Cloud Shell locally

Hello folks,

Lately I've been building and tearing down complete environments in Azure to test and record demos.  To do that I've been using a combination of PowerShell , ARM templates and AzureCLI in the Azure Cloud Shell. Some of those require a significant amount of time to run. (especially the ones with gateways).

The last time I setup my demo environment I started the process, went to the kitchen for a cup of coffee and came back to my desk to the dreaded Cloud Shell timed out message.


A fit of rage and despair filled my heart….


Once I regained my calm, I started to thing that there must be a way to avoid this issue with the Azure Cloud Shell 20-minute timeout… We know that the cloud shell is intended for interactive use cases. Therefore, any long-running non-interactive sessions are ended WITHOUT warning.

I started researching way to run the shell locally.  I already knew that I could run the shell in a Windows Terminal but that would not solve my issue since this basically presents the online Azure cloud Shell inside the terminal windows.

Some background on Azure Cloud Shell.  It runs in the background on a virtual machine and provides your Cloud Shell session.  That is temporary, and it is recycled after your session is inactive for 20 minutes in order to insure we (Microsoft) have enough capacity for everyone.

I found an open sourced  image in a Microsoft public Azure Container Registry. Its our primary Registry for all Microsoft Published docker images.

I am now using this image locally to take advantage of all the benefits of using the managed environment and tooling available through the Azure Cloud Shell without the limitation of running it in the cloud.

Those benefits are a great pre-configured shell experience for managing Azure resources without the overhead of installing, versioning, and maintaining all the tooling on my own machine.

There are other reasons you might want to run the shell locally.  For example, if your security team has disabled access to Cloud Shell from your for your users you could use it locally. Cloud Shell utilizes access to the domain, which can be denied, stopping any access to Cloud Shell's entry points including,, Code Azure Account extension, and

It's not my issue but if you work in a large enterprise and require more than 20 concurrent sessions, you might also want to run this locally.  Azure Cloud Shell has a limit of 20 concurrent users per tenant per region. If you try to open more sessions than your limit, you will get a “Tenant User Over Quota” error.

The solution

The solution to my issues ended up being a combo of different services and tools.  I'm now running the Azure Cloud Shell on my Windows 10 workstation in a Docker container. (See the documentation here for installation).

I keep my code (scripts, ARM templates, code samples…) in an Azure Repo (you could use any code repo like GitHub or other…) and just clone it locally in a directory (I put mine on here E:azrshell).

I built a PowerShell script that I use to ensure I am running the latest image and to connect to an existing container should it already be running. (Code below)

function SelectShellType {
DO {
Write-Host “~~~~~~~~~~~~~~~~~~ Menu Title ~~~~~~~~~~~~~~~~~~” -ForegroundColor Cyan
Write-Host “1: Enter 1 to select PowerShell”
Write-Host “2: Enter 2 to select Bash”

$input = (Read-Host “Please make a selection”).ToUpper()
switch ($input) {
‘1' { $shellType = “/usr/bin/pwsh” }
‘2' { $shellType = “/bin/bash” }
} While ($input -NotIn 1..2)
return $shellType

$results = $(docker ps -q –filter
If ($results -ne $null) {
Write-Host “container running…”
Write-Host “connecting to container…”
docker exec -it $results bash
Else {
Write-Host “container not running”
Write-Host “Updating container image…”
Write-Host “Picking Shell Type”

$StartShellType = SelectShellType
$default = “E:azrshell”
if (!($ScriptsLocation = Read-Host “Enter the path where your local scripts are located. Press Enter to accept the default = [$default]”)) { $ScriptsLocation = $default }
Write-Host “updating container image…”
docker pull
Write-Host “Starting container and connecting your shell…”
Write-Host “Mapping your scripts directory in the container home drive to ” $ScriptsLocation “…”
Write-Host “___________________________________________________________”
docker run -it -v “”$ScriptsLocation':/usr/cloudshell/scripts'”” $StartShellType

I know it's not the prettiest script and it could use some refinements, but it works as intended.  I created a shortcut on my desktop and in my start menu with the following command.

“C:Program FilesPowerShell7-previewpwsh.exe” -ExecutionPolicy Bypass -File “c:UserspierrerDesktopAzrShell.ps1”

The open-source image I leverage has all the bits needed to run both the Bash shell and the PowerShell Core shell just like the one in the online one.  When I click on the shortcut the script will provides me a choice of shells and the opportunity to specify where the clone of my script repo is.

The first time running this script, will take about 5 minutes to completely pull the image and run the container.

If no update to the image is needed, it will load in seconds.


There are some of you who will ask “what's the catch??”  There must be some differences.

Well, yes there are some…

In Cloud Shell, we provide a way to automatically obtain tokens for the user connected to the shell. When you run the image locally, you'll have to explicitly before you can access Azure resources using “az login” for AzureCLI or “Connect-AzAccount” for PowerShell.

We don't mount the Cloud Drive from your Azure Cloud Shell.  That's why we mount a local drive in the container pointing to our local clone.

In Azure Cloud Shell you always run as a regular user. When running the image locally, you run as root. Therefore, you can make changes to the installed tooling.

The last main difference I found is the fact that in the Azure Cloud Shell, when using the PowerShell option, you have the Azure drive (Azure:) It enables easy discovery and navigation of Azure resources such as Compute, , etc. like a filesystem navigation.


I hope this little story on how I addressed my own issue will help you get around some of the Azure shell restrictions that you may have.

Let me know in the comment below if you have scenarios, you'd like us to explore.




This article was originally published by Microsoft's Azure Blog. You can find the original article here.