This course lives on exercises. Whoever runs DevOps does not learn it from text alone, but by creating, viewing, changing and deleting repositories, pipelines, permissions and measurements. That costs money and nerves if you do it carelessly, and almost nothing if you have a few habits. This lesson sets them down before the first object exists: which accounts you use, what is free in them, what costs money over time and how every exercise ends.
The running example is Nordlicht Reisen GmbH, a fictional travel agency, with its invented booking portal ("Buchungsportal" in German). In this course it lives in two places: as a repository on GitHub and as a project in Azure DevOps. The AZ-400 exam requires both tool worlds, which is why the exercises need both.
Prior knowledge: The Azure free account, its limits and cleaning up with resource groups are covered in AZ-104, lesson 0.1. How costs arise in Azure and what a budget looks like as a script is taught in K3, lesson 3.1.2. How to unlock the free allowance for private projects in Azure DevOps was already shown in K3, lesson 3.7.2. This lesson summarizes that for the exercises of this course, adds the GitHub side and sets down the clean-up for all three worlds.
Note on sources and numbers: All statements in this lesson were read in the documentation of Microsoft Learn and of GitHub; the pages are listed under "Further reading". This course does not state any amounts of money. Where Microsoft or GitHub state a quantity (minutes, users, days), it appears here with its source; where the source states only an amount of money or nothing at all, that is stated here too. These statements change. Check them on the linked pages before every series of exercises.
Three accounts, three bills
The exercises need three things that have nothing to do with each other, except that you will connect them:
| Account | What for | Who bills? |
|---|---|---|
| GitHub account | Repositories, Issues, Projects, Actions, Packages | GitHub, according to the plan of the account |
| Azure DevOps organization with a project | Boards, Repos, Pipelines, Artifacts, Wiki | Azure DevOps, through a linked Azure subscription |
| Azure subscription | Resources that pipelines deploy to; billing of the organization | Azure, by consumption |
If you have a production account at work, never use the real one for the course. Create your own organization and your own project, your own GitHub account or at least your own GitHub organization, and an Azure subscription in which nobody works productively. This rule protects in both directions: your exercise cannot destroy anything real, and nothing real shows up in your exercise.
GitHub: account, free tier, limits
A personal GitHub account on the GitHub Free plan is enough for the course. According to GitHub's page on plans, GitHub Free for personal accounts includes unlimited public repositories with the full feature set and unlimited private repositories with a limited feature set. Which features are missing in private repositories is decided by GitHub and the plan; for every exercise that needs a product available only for a fee or only for public repositories, the course says so.
For GitHub Actions, the following applies according to GitHub's billing page:
- GitHub's standard runners (the machines hosted by GitHub on which workflows run) are free in public repositories. This usage does not consume the included allowance.
- Self-hosted runners are also free and do not consume the included allowance. That they need your machine and your maintenance in return is covered in lesson 4.2.
- In private repositories, the standard runners draw on an included allowance of the plan. According to GitHub, this allowance is an amount of money, not a fixed number of minutes. The amount per plan is on the page "GitHub Actions billing" in the documentation. The course does not state it, because amounts of money must not appear here and because they change. Storage for artifacts (shared with Packages) and the cache per repository also have their own limits, which are listed there.
- If the included allowance is used up and no valid payment method is on file, usage is blocked according to GitHub. If a payment method is on file, usage beyond that is billed at the standard rates. Larger runners always require a valid payment method.
- A default value for a spending limit is not on this page; GitHub refers to budgets in the account's billing. Check there which budgets apply to you before you add a payment method.
A simple rule for the course follows from this: exercises with Actions run in public repositories when nothing speaks against it (no secret, no customer data, only sample code), and in private repositories only when the exercise says so. Then the included allowance counts, and you do not add a payment method unless you have to.
What can cost money over time or storage on the GitHub side is stated by each exercise beforehand: private repositories with Actions, GitHub Advanced Security for private repositories (a license per active committer, lesson 5.4), larger runners.
The GitHub tool on your machine
Besides the browser, the exercises use the GitHub CLI (gh). Signing in:
gh auth login --web
gh auth status
gh auth login signs you in to GitHub (--web opens a browser for that); you can change the default host github.com with --hostname. gh stores the token in the system's credential store, and only if there is none or it cannot be used, in a text file. gh auth status shows where the token is stored. Check that once before you use this account for exercises. With --scopes you ask for additional permissions; you need that when cleaning up (below).
You create a practice repository like this:
gh repo create nordlicht-buchungsportal --private --description "Übung AZ-400"
gh repo create takes the name in the format owner/name; without an owner, the command uses the signed-in account. --private makes the repository private, --public makes it public.
Azure DevOps: organization, project and free pipelines
Organization and project
An organization is the frame: members, billing, agent pools and the parallel jobs belong to it. A project in the organization contains Boards, Repos, Pipelines, Artifacts and the wiki. You create both in the browser: Microsoft states that all organizations must be created manually through the web portal; automatic creation is not supported. Creating projects, on the other hand, also works with a command.
This is a change from older tutorials: According to the page "Create an organization", you need an active Azure subscription to create a new organization. When creating it, you give the name, the region for the data and the subscription for billing. The rules for the name: only letters of the English alphabet, digits and hyphens, beginning and ending with a letter or digit, fewer than 50 characters. For an invalid character, the portal reports the error VS850015.
According to Microsoft, the new organization contains this free tier:
| Service | What is free (according to the page "Create an organization") |
|---|---|
| Users | the first five users with a Basic license |
| Azure Pipelines | one Microsoft-hosted job at a time, up to 30 hours a month; one self-hosted job at a time |
| Azure Boards | work item tracking and boards |
| Azure Repos | unlimited private Git repositories |
| Azure Artifacts | two GiB per organization |
Whoever uses more pays the extra usage through the linked Azure subscription. The page on Azure Boards adds: Getting started is free for up to five users, and Stakeholders (users with limited access) are unlimited.
The free execution of pipelines for private projects
A pipeline run needs a parallel job: the right to run a job at the same time as others. Azure DevOps distinguishes between Microsoft-hosted and self-hosted parallel jobs. Both have a free tier; the self-hosted one is granted automatically, the Microsoft-hosted one you must unlock. You read this on the page "Configure and pay for parallel jobs":
| Microsoft-hosted (private projects) | Self-hosted (private projects) | |
|---|---|---|
| Free | one job at a time | one job at a time, plus one per active Visual Studio Enterprise subscriber of the organization |
| Time per job | at most 60 minutes per run | no time limit |
| Time per month | at most 1,800 minutes (30 hours) | no time limit |
| Paid tier | 360 minutes per job, no monthly limit | no time limit |
| Unlocking | link the organization to a valid Azure subscription (set up billing) | not necessary |
The decisive sentence of the page reads: To get the free parallel jobs, link your organization to a valid Azure subscription; as soon as billing is set up, the free tier automatically applies to the organization's private projects. The older practice of requesting the grant from Microsoft is no longer mentioned on this page. If your first pipeline hangs anyway with the message that no parallel job is available, check under Organization settings, Pipelines, Parallel jobs what your organization has; the page names this place. There you also see the running jobs.
Further rules of this page that matter for the exercises:
- A run consumes a parallel job only while it is actively running on an agent. A run that is waiting for an approval does not consume one.
- There is no splitting of the capacity across projects or pools: It belongs to the organization and applies to all projects.
- Whoever pays at first only buys away the time limit of the first job: The first purchased Microsoft-hosted job leaves the number of jobs at one. For two simultaneous jobs, two are needed.
- For new organizations, the page names an upper limit of 25 parallel jobs on Microsoft-hosted agents.
- Cost and price per job are on the Azure DevOps pricing page. The course does not state them.
Public projects: Microsoft has retired them. New ones can no longer be created, and existing ones will become private later; the same free allocation as for private ones then applies to them too. Whoever works on open source uses GitHub, according to Microsoft's note. For this course, that changes nothing: Your project is private.
What that means for the course: On average, your pipelines run briefly. With one job at a time and 30 hours a month, you get through all the exercises as long as you do not let them run in a continuous loop. Just plan for this: Several runs share the one job and wait in the queue. If you start three pipelines at once, you are not waiting by accident, but by the rule above.
The Azure DevOps tool on your machine
For az devops you need the Azure CLI extension:
az extension add --name azure-devops
az login
az devops configure --defaults organization=https://dev.azure.com/<organisation> project=<projekt>
Microsoft's guide names az extension add --name azure-devops, az login and az devops configure with --defaults for organization and project; without default values, you give --org and --project in every command. With az login, the Azure CLI supports only interactive sign-in with user name and password; for signing in with a personal access token (PAT), Microsoft describes a separate way. Microsoft advises preferring Microsoft Entra tokens to PATs, which as long-lived secrets carry the highest risk. That is also why this course creates no PATs where there is another way.
You create a project like this:
az devops project create --name nordlicht-az400 --visibility private
--name is required; --visibility accepts private (default) and public, --process sets the process (default is Agile), --source-control sets git (default) or tfvc. Project names must not contain special characters such as / : \ ~ & % ; @ ' " ? < > | # $ * } { , + = [ ], must not begin with an underscore, must not begin or end with a period and may be at most 64 characters long.
Azure: the free account and the resource group per exercise
For deploying to Azure and for billing the organization, you need a subscription. The Azure free account from AZ-104, lesson 0.1, is suitable for that. According to Microsoft's page "Avoid charges with your Azure free account", eligible new users get a credit in the billing currency for the first 30 days and a limited amount of free services for twelve months. As long as you have unused credit or use only free services within the limits, nothing is charged. The page further states:
- If the credit is used up or the 30 days are over, the subscription and services are disabled. To keep using them, you must upgrade the account; after that, you pay by consumption only what exceeds the free amounts.
- The free amount expires at the end of the month and is not carried over to the next month.
- Only certain tiers of a service are free. What is free is listed in the frequently asked questions about the free account.
- You check the usage of free services in the portal; the page "Check usage of free services" explains how.
- Whoever does not want to use any service can cancel the subscription.
Note: The credit itself is an amount of money; the course does not state it, you read it when you sign up. Whoever does not get a free account uses their own empty subscription with a budget (K3, lesson 3.1.2; AZ-104, lesson 1.6).
What costs money over time and what does not
| Costs money over time or storage as long as it exists | Costs hardly anything or nothing when empty |
|---|---|
| virtual machines, including stopped ones with an allocated host (lessons 4.2, 4.5) | resource groups, tags, policy assignments |
| scale sets for agents (lesson 4.2) | Boards, wiki, Issues, Projects (within the free tier) |
| Azure Load Testing (lesson 4.6) | repositories, branches, pull requests |
| Container Registry, Container Apps, App Service (lessons 4.8 to 4.11) | service connections and variable groups |
| Log Analytics data (lessons 6.1, 6.2), Defender for Cloud (lesson 5.4) | |
| GitHub Advanced Security in private repositories (lesson 5.4) | |
| Azure Artifacts beyond the free amount (lesson 3.1) |
Every exercise says before the first step what costs money over time in it. This course does not state amounts; the services' pricing pages and the Azure pricing calculator show them. Keep to the rule from K3, lesson 3.1.2: estimate, record with a date, and do not treat a value you have not checked as fact.
The resource group per exercise
Every exercise that touches Azure begins with its own resource group rg-az400-<exercise>, for example rg-az400-konten ("konten" is German for accounts). The advantage: Everything the exercise creates lives there, and az group delete removes it with one command.
az group create --name rg-az400-konten --location germanywestcentral
az group delete --name rg-az400-konten --yes --no-wait
az group exists --name rg-az400-konten
You check regions and quotas as in AZ-104, lesson 0.1. After the deletion, az group exists answers false after a few minutes; that is the check that nothing is running anymore. What the group does not contain is covered in the next section.
Cleaning up: what does not disappear with the group
A resource group cleans up only Azure. Everything you created on GitHub and Azure DevOps stays, and some of it has access to your account. That is why cleaning up in this course is an explicit step of every exercise.
GitHub
| Object | Removing | Note |
|---|---|---|
| Repository | In the repository Settings, in the section Danger Zone Delete this repository, type the name; with gh repo delete owner/name --yes |
Owner or administrator rights are required. According to GitHub, some deleted repositories can be restored within 90 days, not all. |
| Only retiring a repository | Archive this repository (read-only for everyone); undo with Unarchive this repository | An archived repository is not a clean-up if it still has access or secrets. |
| Personal access token | Settings, Developer settings, Personal access tokens, Fine-grained tokens or Tokens (classic), Delete | GitHub removes tokens that have not been used for a year on its own. A deleted token also deletes a deploy key created with it. |
| Self-hosted runner | In the repository Settings, Actions, Runners, select the runner, Remove; if the machine is not reachable, Force remove this runner | Then remove the service on the machine itself (lesson 4.2). |
| GitHub Project | In the project the menu ⋯, Settings, in the section Danger zone Delete this project; only Close this project is reversible | Deleting removes the project permanently. |
gh repo delete requires the permission delete_repo, which gh auth login does not bring along by itself. Request it before deleting:
gh auth refresh -s delete_repo
gh repo delete <besitzer>/nordlicht-buchungsportal --yes
--yes skips the confirmation prompt. The documentation of gh adds: Without a repository name, the option is ignored and the prompt appears anyway, so you name the repository explicitly. Whoever prefers not to delete with a command takes the way through Settings.
Azure DevOps
| Object | Removing | Note |
|---|---|---|
| Project | Organization settings, Projects, tick the project, Delete, type the name; or az devops project delete --id <projekt-id> --yes |
Deleted projects can be restored for 28 days (Organization settings, Overview, "Recently deleted projects"); after that they are gone for good. In rare cases, according to Microsoft, deletion can take up to 70 days. |
| Organization | Organization settings, Overview, Delete, type the name | Remove billing first (detach the link to the Azure subscription). Paid users and services continue until the end of the billing period. The organization can be restored for 28 days. |
| Service connection | Project settings, Service connections, the connection, in the menu More options Delete; with Disable you only block it | Azure Pipelines can disable a connection by itself after 100 days without use. For Azure connections, afterwards delete the associated service principal in the app registrations of Microsoft Entra by hand, if the deletion reports that it could not remove it. |
| Personal access token | In the user settings Personal access tokens, select the token, Revoke | Microsoft advises revoking a token as soon as it is no longer needed. Create only tokens limited to one organization. |
| Agent in a pool | See lesson 4.2; the exercises of D0 to D1 need none | A PAT for registering needs the scope "Agent Pools (Read & manage)". |
| Wiki, Boards, Repos | disappear with the project | Copy beforehand what you want to keep: Microsoft describes "Download as Zip" and cloning the repository. |
When deleting an organization, the following also applies: The organization is locked for everyone immediately, its name becomes free after the deletion (that can take up to an hour), and users' access to their Azure accounts remains untouched.
The check applies here too: az devops project list no longer shows the project after it has been deleted. The list of "Recently deleted projects" shows that it is within the 28-day period; that is not a sign of an error, but the intended state.
A note on the platforms' access tokens
For the access tokens of both sides, retirements are currently pending. Azure DevOps is discontinuing global PATs (those that apply to all organizations); the page on PATs gives different dates for this in two places. Read on the page what applies, and create only PATs limited to one organization, with the smallest scopes and the shortest lifetime. The Azure DevOps issuer for workload identity federation in service connections is also deprecated according to Microsoft; new connections use the Microsoft Entra issuer. Lesson 5.1 covers both.
Decisions
| If ... | then ... | because ... |
|---|---|---|
| the exercise needs only sample code and no secret | a public GitHub repository with standard runners | according to GitHub they run free in public repositories and do not consume the allowance |
| the exercise needs a private repository | a private repository, no payment method on file as long as the included allowance lasts | without a payment method, GitHub blocks after the allowance is used up instead of billing |
| the first pipeline in Azure DevOps does not start | check whether the organization is linked to an Azure subscription | the free tier for Microsoft-hosted jobs applies only once billing is set up |
| several runs are to start at the same time | plan them one after the other | the free tier has one parallel job; further runs wait in the queue |
| an exercise needs a machine or a service with a time-based price | create the group rg-az400-<exercise> and delete it at the end |
a group deletes all resources in it with one command |
| a token is no longer needed | delete or revoke it immediately | a token is a long-lived secret and hangs on your account |
Cost and clean-up
- No amount of money in this course. Where a service costs money over time, the exercise says beforehand what it is and where you read the price.
- The things in this lesson cost nothing if you remove them again: an Azure DevOps project within the free tier, a repository on GitHub, an empty resource group.
- The Azure free account runs out after 30 days (credit) or, after twelve months, is without the free amounts. Set yourself a reminder before you start, so that nothing keeps being charged unnoticed.
- The organization in Azure DevOps hangs on the subscription. Whoever deletes the organization removes the billing beforehand.
- Cleaning up comes at the end of every exercise and always ends with a check, not with the assumption that it worked.
Exercise 1: Create accounts and record the limits
Note on all commands and menu paths of this course: They were not run; they come from the reference of Microsoft and GitHub. Menus change. If a step looks different from the description, the current page under "Further reading" applies, and you note the deviation for yourself.
You set up the accounts and record what they may cost.
- Create a GitHub account (or use your own for exercises), sign in with
gh auth login --weband read withgh auth statuswhere the token is stored. - Have an Azure subscription ready (the free account from AZ-104, lesson 0.1, or your own empty subscription with a budget) and sign in with
az login. - In the browser, create your own Azure DevOps organization and link it to the subscription when creating it. With
az devops project create, create the projectnordlicht-az400, private. - Open Organization settings, Pipelines, Parallel jobs and read how many parallel jobs your organization has.
- With
az group create, create the grouprg-az400-konten. - In a file
docs/kosten.md("kosten" is German for costs) of your practice environment, write down, with the date you read them, where you read these four pieces of information: the included allowance of GitHub Actions for your plan, the limits of the free pipelines in Azure DevOps, the duration of the Azure credit and when it ends.
How to check: az devops project list names nordlicht-az400; az group exists --name rg-az400-konten answers true; gh auth status names your account; and docs/kosten.md names a source and a reading date for each of the four pieces of information.
Solution 1
gh auth login --web
gh auth status
az login
az extension add --name azure-devops
az devops configure --defaults organization=https://dev.azure.com/<organisation> project=nordlicht-az400
az devops project create --name nordlicht-az400 --visibility private
az devops project list
az group create --name rg-az400-konten --location germanywestcentral
az group exists --name rg-az400-konten
You create the organization in the browser; before the command az devops configure, you know its name. Mind the order of the default values: az devops configure --defaults project=nordlicht-az400 can only make sense after the project has been created; until then, you give --org.
The file docs/kosten.md can look like this (the sources stay, you enter the values yourself):
# Kosten und Grenzen der Übungen (gelesen am <Datum>)
- GitHub Actions, private Repositories: enthaltenes Kontingent laut Seite "GitHub Actions billing": <Wert aus der Seite>.
- Azure DevOps, Microsoft-gehostete Aufträge: ein Auftrag, 60 Minuten je Lauf, 1.800 Minuten im Monat (Seite "Configure and pay for parallel jobs").
- Azure-Testkonto: Guthaben 30 Tage, freie Dienste zwölf Monate (Seite "Avoid charges with your Azure free account").
- Ende des Guthabens: <Datum der Anmeldung plus 30 Tage>.
Exercise 2: Clean up and the check
You create three things and remove them again, the way every later exercise ends.
- With
gh repo create, create a private repositorynordlicht-aufraeumen("aufraeumen" is German for cleaning up) and withaz group createthe grouprg-az400-aufraeumen. - In the Azure DevOps project, create a pipeline variable or a personal access token with the smallest scope and the shortest lifetime (for example, read-only for work items). Do not write the value down anywhere.
- Delete the repository (first
gh auth refresh -s delete_repo), revoke the token in the Azure DevOps user settings and delete the group. - Check every single object, not just one.
How to check: gh repo view <besitzer>/nordlicht-aufraeumen reports an error because the repository no longer exists; az group exists --name rg-az400-aufraeumen answers false; in Personal access tokens, the token is no longer listed or has the state revoked.
Solution 2
gh repo create nordlicht-aufraeumen --private
az group create --name rg-az400-aufraeumen --location germanywestcentral
gh auth refresh -s delete_repo
gh repo delete <besitzer>/nordlicht-aufraeumen --yes
az group delete --name rg-az400-aufraeumen --yes --no-wait
gh repo view <besitzer>/nordlicht-aufraeumen
az group exists --name rg-az400-aufraeumen
You create and revoke the access token in the browser under the user menu, Personal access tokens, with + New Token and Revoke. When creating it, Microsoft names: the name, exactly one organization, an expiration date and only the necessary scopes. Do not copy the value into files; it is shown only once.
Deleting the group runs in the background; az group exists answers false only after a few minutes. If you still see true after the first call, wait and ask again. The control question about the repository returns an error; that is the desired check. What wording the error message of gh repo view has, this course did not read from an account.
Typical mistakes
- The first pipeline does not start because there is no parallel job. The organization is not linked to an Azure subscription; the free tier for Microsoft-hosted jobs applies only once billing is set up. Check Organization settings, Pipelines, Parallel jobs.
- The name of the organization is rejected. Microsoft names the message
VS850015: The specified name is not allowed to be used: {Organization name}. Use only English letters, digits and hyphens, begin and end with a letter or digit, fewer than 50 characters. - The organization cannot be deleted. If it is linked to an Azure subscription for billing, you must remove the billing beforehand. Paid users and services of the current period are still charged.
- Deleting an Azure service connection reports "Failed to remove Azure permission" or "Failed to remove the service principal from Microsoft Entra ID". According to Microsoft, the connection disappears in Azure DevOps anyway; if the deletion fails completely, Microsoft names the REST interface with
deep=false. Afterwards, remove the service principal in the app registrations by hand. gh repo deleteasks anyway despite--yes. Without a repository name, the option is ignored according to the documentation ofgh. Give the name in the formatowner/name.- The free account stands still after 30 days. Credit used up or expired: The subscription and services are disabled until you upgrade the account. That is not an error, but the behavior described.
What you can do now
You choose the three accounts of the exercises and name the limits of their free tiers with a source: GitHub Free with its amount of money for private repositories, Azure DevOps with one job at a time, 60 minutes per run and 1,800 minutes a month after linking to an Azure subscription, and the Azure free account with its credit for 30 days. You create organization, project and resource group and begin every exercise in a group rg-az400-<exercise>. You know what costs money over time. And you clean up all three worlds: Azure with az group delete, GitHub by deleting repositories, tokens and runners, Azure DevOps by deleting the project and organization, revoking tokens and removing service connections, every time with a check.
Further reading
- GitHub Actions billing:
https://docs.github.com/en/billing/concepts/product-billing/github-actions - GitHub's plans:
https://docs.github.com/en/get-started/learning-about-github/githubs-plans - Deleting a repository:
https://docs.github.com/en/repositories/creating-and-managing-repositories/deleting-a-repository - Archiving repositories:
https://docs.github.com/en/repositories/archiving-a-github-repository/archiving-repositories - Managing your personal access tokens:
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens - Removing self-hosted runners:
https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/removing-self-hosted-runners - gh repo create:
https://cli.github.com/manual/gh_repo_create - gh repo delete:
https://cli.github.com/manual/gh_repo_delete - Create an organization:
https://learn.microsoft.com/azure/devops/organizations/accounts/create-organization - Configure and pay for parallel jobs:
https://learn.microsoft.com/azure/devops/pipelines/licensing/concurrent-jobs - Create a project in Azure DevOps:
https://learn.microsoft.com/azure/devops/organizations/projects/create-project - Delete a project:
https://learn.microsoft.com/azure/devops/organizations/projects/delete-project - Delete an organization:
https://learn.microsoft.com/azure/devops/organizations/accounts/delete-your-organization - Use personal access tokens:
https://learn.microsoft.com/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate - Manage service connections:
https://learn.microsoft.com/azure/devops/pipelines/library/service-endpoints - Get started with Azure DevOps CLI:
https://learn.microsoft.com/azure/devops/cli/ - Avoid charges with your Azure free account:
https://learn.microsoft.com/azure/cost-management-billing/manage/avoid-charges-free-account