This document provides a guide to deploying and demonstrating Azure Bastion and it's capabilities.
Read more about the architecture on my project blogs titled Azure Bastion Capabilities.
- Azure Subscription with sufficient permissions.
- Terraform installed locally.
You'll need a CI/CD pipeline like Jenkins, GitHub Actions or ArgoCD in the real world environment - Basic knowledge of Azure networking and RBAC.
- Azure CLI installed.
- Optional capabilities, Highly recommended in production
- Entra ID with Premium license for Conditional Access and PIM.
- Privileged Identity Management (PIM) enabled in Entra ID.
- Just-In-Time (JIT) access configured.
- Access Packages configured in Entra ID.
-
Checkout the repository locally.
-
In the root directory, login to your Azure account.
az login. Note this is not needed if running via a CI tool. -
Run the below command and take note of the
idvalueaz account show
-
-
If deploying a Public endpoint architecture, then skip next step
-
If deploying a Private endpoint architecture
- Update the following variables in the
tfvarsfilebastion_skuset this toPremiumbastion_is_privateset totruebastion_session_recording_enabledset totrueif you want to see this in action- If you dont have a VPN you will need one. Set
use_vpn_gatewaytotrue
- Update the following variables in the
-
Execute the below command in the terminal you logged in from via the cli
terraform plan -var-file=dev.tfvars -var subscription_id=<your_subscription_id>
-
Review the plan. This is very good practice with terraform, done assume always double check.
-
When done with reviews run the below command
terraform apply -auto-approve -var-file=dev.tfvars -var subscription_id=<your_subscription_id>
The
-auto-approveis like the Linux-y, it signals to terraform to proceed without prompting you for approvals. You'd find this mostly with CI configurations -
While you wait, go to you Azure portal, look for the new resource group and follow the creation of the resources.
-
After deployment, verify the vms are all
ready -
Follow the verification steps below to test this out
- IP Overlaps, you may need to consider this. Change them to what suits if errors in the
applyon this. - VM SKUs and SKU limits, especially or personal accounts. Change to whats applicable in you environment
locationyours may not beUK Southupdate as per your requirements- Naming conventions. As it's mostly driven via the
tfvarsfile, make any necessary adjustments to suit your needs or policy
You’ll need at least these groups:
Bastion-Connect → Engineers who can connect to VMs via Bastion.
Bastion-UploadDownload → Engineers allowed to upload and download files.
Bastion-UploadOnly → Engineers allowed to upload files only.
There are a couple ways to create these groups:
- Using Access Packages (Recommended for JIT):
- Go to Entra ID → Identity Governance → Access Packages → New access package.
- Create an access package for each role, linking to the respective group.
- Configure policies for approval, expiration, and JIT access.
- Publish the access packages.
- Engineers can request access via the Access Package portal.
- Approvals and expirations are managed automatically.
- This method provides better governance and auditing.
- Manually Creating Groups:
- If the creation of the groups failed or you don't have the permissions to create groups, you can get someone with the required permissions to assist. See below for manual group creation steps.
- Via Scripts:
- Execute the cli commands in the
setup-bastion-rbac.shscript file to assign the roles to the groups. Groups should have been created by Terraform, this us an addition just incase
- Execute the cli commands in the
How to create groups manually:
- Go to Microsoft Entra admin center → Groups → New group.
- Choose Security as the group type.
- Name the group (e.g., Bastion-Connect).
- Add members (or leave empty if you’ll use JIT later).
- Repeat for the other two groups.
Azure Bastion uses Azure RBAC roles for permissions. Assign these roles at the Bastion resource scope:
- Required for SSH/RDP access to VMs.
- Assign to Bastion-Connect group.
- Allows viewing Bastion resource.
- Assign to all groups.
Upload & Download: Action → Microsoft.Network/bastionHosts/fileTransfer/action
Upload Only: Action → Microsoft.Network/bastionHosts/fileUpload/action
Assign these custom roles to Bastion-UploadDownload and Bastion-UploadOnly respectively.
Apply Conditional Access policies to these groups:
- Require MFA.
- Require compliant device.
- Restrict access to VPN IP ranges.
If you want engineers to request access dynamically (similar to Access Packages), you can use:
- Privileged Identity Management (PIM) for group membership.
- Configure eligible assignments for these groups.
There are some verification steps in the Verification Step Scripts document. Follow the example to validate you can access resources via the newly deployed Azure bastion
Essentially we want to validate
- VPN connectivity verified (either the p2s or own VPN)
- Conditional Access enforced (If configured, requires P1 and above license)
- Bastion deployed in management VNet
- Session recording enabled and tested (if using premium)
- File transfer tested for both roles
- Native client and browser access verified
- Tunneling tested
- Logs flowing to Log Analytics
These would cover most use cases where an IaaS VM is deployed as a JumpBox and should validate that Azure bastion can provide same capabilities.
If testing things out in your personal account or using your organisations provided "sandbox", ensure you delete what you have created as it will cost you or your organisation money by the hour.
To remove all resources run the below command
terraform destroy -auto-approve -var-file=dev.tfvars -var subscription_id=<your_subscription_id>