PowerShell module Devdeer.Caf
A PowerShell module that lets you handle Azure in a convenient but still secure way.
Introduction
This collection of functions for PowerShell is a result of our (DEVDEERs) interpretation on how one should access Azure on a daily base. So in order to understand what these functions are doing is important to understand some core principles first.
Principle 1: Do not use the Azure Portal
This first principle is heavily underrated. A lot of influential material out there (including Microsoft Learn) is showcasing certain activities using the Portal. This is a bad idea for several reasons.
- The portal tends to "lie" to a user because it uses caching a lot. You often find yourself in a situation where you constantly refresh the page until you see the desired result (or not because something really failed).
- You need to access the portal using you personal account. This means that you pretty often need to use MFA and login confirmation which distracts you a lot usually.
- The steps you take in the portal are not repeatable. You need to guide other through it or make notes to remember it yourself.
- The portal hides a lot from you. It is in our opinion more a shiny window which sometimes looks really cool but if you want to dig deeper you get lost pretty fast.
This list is not even complete but we think that it already shows why using the portal on a daily base is not a good idea.
Principle 2: Do not use permanent privileged user accounts
This principle is easy to understand and hard to do. The idea is that you don't privilege your user account to a level where you are able to perform actions which might change resources or obtain sensitive data by accident.
Principle 3: Use Privileged Identity Management (PIM)
In direct contradiction to Principle 2 you often need to interact with Azure using your user account. Whenever this is necessary you should have PIM activated in Azure so that you
- Get the elevated rights only temporary.
- Need approval from a co-worker for that.
- Have this privilege escalation being logged.
Over the years we've encountered many setups at customer tenants where this was done by using 2 accounts for administrative users. This is a really bad idea brought over from on-premise environments where it is a good idea. Being forced to select just a different password set in a browser or posh-session does not help at all. Those sessions are still being cached by default and there is nothing like a sudo-like process start which makes the admin-user-concept so useful in operating systems.
Principle 4: Use service principals for operations
Whenever you are deploying something in Azure, changing the settings of a resource (which you should do as a deployment) or export some backups and stuff like this you should not perform those actions with your personal account but using a service principal instead. In fact, most of these operations should not even started by you imperatively but should be executed by a process, service, API, pipeline or whatever.
The problem
As good and understandable as these principles might be. We experienced that the majority of admins and devs are not adhering to them because its hard to do so or the company forces them to do but then people either run away or hate their interaction with Azure.
Principle 4 and 2 combined for instance force you to somehow know which service principle to use and where to retrieve the password from. Then, in order to just download a file from a storage account, you find yourself doing 2 minutes of sp-login to finally execute this one command. Also this leaves your Azure context of your shell logged in as this service principal which either is restricting you in other sessions or - even worst - let you now have privileged rights.
Even using PIM is not straightforward. Usually you login to the portal and perform your request and then you need to re-login in your posh session and in the browser-sessions in other windows for this action to take effect. When you wait for approvals your usually find yourself refreshing your browser until you can see your new role.
The solution
Well, first of all: there is no solution for this! What we offer with Devdeer.Caf is convenient, efficient but still secure ways to interact with Azure. Those things only can happen if you are willing to follow a lot of conventions (tenant design, naming, ...). Luckily those conventions are defined by a high authority. Microsoft released the Cloud Adoption Framework aka CAF a while ago. Part of it are recommendations for
- Structuring your tenant.
- Setting up networking.
- Naming your resources.
- Using infrastructure as code (IaC).
and many more.
What we try to achieve with Devdeer.Caf is to provide tooling based on these ideas making it easier to follow them.
Disclaimer
We need to interpret a lot out of the statements in the CAF. Very rarely Microsoft says something like "You should do it exactly like this..." or "Don't do this at all!". Thats why the functions provided by us are opinionated. We do not guarantee the functionality or provide support for any of them! Because of the nature of PowerShell you can download and inspect our code at any time.
Why PowerShell?
You can run PowerShell on any platform (Linux and MacOS can use PowerShell Core) whereas bash scripts are not easily ran on a Windows machine. That's why!
Functions
| Function | Description | Needs PIM |
|---|---|---|
| Approve-CafPimRole | Approves a PIM Role assignment request for a single user. | |
| Clear-CafAllSqlFirewallRules | Removes all custom firewall rules currently added to the SQL server given with regard to resource group locks. | |
| Clear-CafPolicyAssets | Deletes all policies defined in the BICEP files under the current path. | |
| Deploy-CafJumphost | Deploys a Jumphost virtual machine to an Azure subscription. | |
| Deploy-CafPolicyAssets | Deploys all BICEP files under the current path considering them to define resources from the provider 'Microsoft.Authorization/*' | |
| Get-CafContext | Retrieves the combined .azcontext-based settings that are valid in the current directory. | |
| Get-CafExpiringKeyVaultSecrets | Retrieves all Key Vault secrets that are expiring within a specified number of days. | |
| Initialize-CafAdoServiceConnections | Initializes Azure DevOps service connections for all projects defined in the .azcontext file. | |
| Initialize-CafAdoToAcrServiceConnection | Initializes an Azure DevOps service connection to an Azure Container Registry. | |
| Initialize-CafAdoToARMServiceConnection | Creates a service connection from Azure DevOps to Azure Resource Manager. | |
| Initialize-CafBicep | Initializes the DEVDEER Bicep modules for a project. | |
| Initialize-CafBudget | Creates a budget resource for a single Azure subscription. | |
| Initialize-CafBudgets | Creates budget resources for all Azure subscriptions in the tenant. | |
| Initialize-CafCsp | Initializes the tenant to be associated with a Cloud Solution Provider. | ✔️ |
| Initialize-CafDeploymentSpGroup | Initializes the security group AZ-CAF-DeployPrincipals for deployment service principals. | ✔️ |
| Initialize-CafServicePrincipals | Initializes the default service principals in all management groups and subscriptions of the tenant. | ✔️ |
| Initialize-CafSubscription | Initializes the subscription management resources for a single subscription. | ✔️ |
| Initialize-CafSubscriptions | Initializes the subscription management resources in all subscriptions of the tenant. | ✔️ |
| Initialize-CafTenant | Initializes the whole tenant by performing all CAF setup actions in sequence. | ✔️ |
| New-CafDeployment | Deploys Azure resources by using a Bicep file within an automatically created service principal context. | |
| New-CafSqlFirewallRule | Adds a firewall rule to an Azure SQL Server for the current public IP. | |
| Remove-CafJumphost | Removes a Jumphost virtual machine and its associated OS disk. | |
| Remove-CafLocks | Removes locks from the resource group with the given name. | |
| Remove-CafStaleRoleAssignments | Deletes leftover role assignments for already deleted service principals. | |
| Restore-CafLocks | Restores all given locks to the specified resource group. | |
| Set-CafServicePrincipal | Creates or updates a service principal and stores the credentials in a Key Vault. | ✔️ |
| Show-CafNamingConvention | Builds all the naming conventions from the context file and displays them in a table. | |
| Show-CafSubscriptionsCost | Displays cost information for all Azure subscriptions in the current tenant. | |
| Start-CafPimGroup | Assigns the user to a PIM group. | |
| Start-CafPimRole | Activates the user's PIM Role assignment. | |
| Start-CafScoped | Executes a command or script file in a new PowerShell session authenticated with a service principal. | |
| Stop-CafPimGroup | Deactivates the user's assignment to a PIM group. | |
| Stop-CafPimRole | Deactivates the user's PIM Role assignment. | |
| Use-CafContext | Ensures that the current PowerShell context for Azure is aligned with Get-CafContext. | |
| Use-CafServicePrincipal | Sets the context for a service principal using credentials stored in an Azure Key Vault. |