Sign in or create a free account to continue.

MSAZ-282 · Lesson 2 of 12 · 21 min

Automation with PowerShell, CLI and runbooks

▶ Video coming soon A video version of this lesson is on the way.

Some of the work Walt automated on HF-SQL01 never touched a database at all. Every spring he asked the infrastructure team to add memory to the virtual host before the busy weeks; every quarter he copied the production database to the test server for the developers; every January he checked that the backup drives had space for another year. In Azure those tasks become operations on Azure resources: change the vCores of a managed instance, copy a database to another server, raise a storage limit. They are done through the Azure Resource Manager API, and the DP-300 outline expects a DBA to drive that API with Azure PowerShell, the Azure CLI and ARM or Bicep templates, and to monitor and troubleshoot what those tools deploy.

Three ways to talk to Azure Resource Manager

Every change to an Azure resource, whether made in the portal or a script, is a request to Azure Resource Manager. The Azure CLI, a cross-platform command-line tool whose SQL commands begin with az sql, and Azure PowerShell, a set of PowerShell modules whose SQL cmdlets live in the Az.Sql module, are two front ends to the same API. Neither can do something the other cannot in any way that matters for the exam; teams choose by habit. Walt has written PowerShell for years and will use it. Renata Quist, who builds the deployment pipelines, prefers the CLI because her build agents run Linux. Templates are the third front end: instead of a list of commands, a Bicep or ARM template describes the resources that should exist, and Resource Manager works out what to create or change.

The distinction that matters is imperative versus declarative. A script is imperative: it says do this, then this. If it runs twice, it may try to create something that already exists and fail, so scripts need checks. A template is declarative: it says this is how things should be. Deploying the same template twice changes nothing the second time, which makes templates the better choice for building environments. Scripts remain the better choice for actions, such as scaling for a weekend, starting a copy or triggering a failover test, where there is no lasting end state to describe.

Matching common database operations tasks to an automation approach.
TaskBest fitWhy
Build the non-production managed instance and its networkBicep template in a pipelineRepeatable, reviewable, safe to redeploy
Scale StockLine up before the spring rush and down afterScheduled runbook calling PowerShellAn action at a time, with no lasting end state
Copy Finch Rewards production to test each quarterScript or runbookA one-off action with parameters
Add a new store database to the elastic poolBicep template (database resource)The database is a lasting part of the estate
Check that every database has the right backup redundancyAzure Policy or a Resource Graph queryAn audit across many resources

PowerShell and CLI for database tasks

Walt's first script copies the Finch Rewards production database to the test server, the job he used to do by restoring a backup. In Azure SQL Database a copy is a single operation that creates a transactionally consistent copy on the same or another logical server. Because the test copy holds real customer data, the same script then runs a masking step against the copy before the developers get access to it.

Copying the production loyalty database to the test server as a small serverless database, then checking the operation.
Connect-AzAccount -Identity          # in a runbook; interactively, Connect-AzAccount on its own
$src = @{ ResourceGroupName = 'rg-rewards-prod-cc'; ServerName = 'hf-sql-rewards-prod-cc'; DatabaseName = 'finchrewards' }
New-AzSqlDatabaseCopy @src -CopyResourceGroupName 'rg-rewards-test-cc' `
    -CopyServerName 'hf-sql-rewards-test-cc' -CopyDatabaseName 'finchrewards-qtr' `
    -ServiceObjectiveName 'GP_S_Gen5_2'
Get-AzSqlDatabaseActivity -ResourceGroupName 'rg-rewards-test-cc' -ServerName 'hf-sql-rewards-test-cc' `
    -DatabaseName 'finchrewards-qtr'
The same copy in the Azure CLI, and a managed instance scale-up started without waiting, then checked with the operations list.
az sql db copy --resource-group rg-rewards-prod-cc --server hf-sql-rewards-prod-cc --name finchrewards \
    --dest-resource-group rg-rewards-test-cc --dest-server hf-sql-rewards-test-cc --dest-name finchrewards-qtr \
    --service-objective GP_S_Gen5_2

az sql mi update --resource-group rg-stockline-prod-cc --name hf-mi-stockline --capacity 16 --no-wait
az sql mi op list --resource-group rg-stockline-prod-cc --mi hf-mi-stockline --output table

The second command shows a habit worth copying. Scaling a managed instance can take a long time, so the CLI's no-wait option starts the operation and returns at once, and the operations list shows its progress. A script that waits for a long-running operation inside a runbook may hit the runbook's time limit, so long operations are better started, then checked by a later step or a separate schedule.

Azure Automation runbooks

A script on Walt's laptop is not automation; it is a reminder he has to remember. To run unattended, the script must live somewhere that has a schedule, an identity and a log. Azure Automation provides that. An Automation account holds runbooks, which are PowerShell or Python scripts, plus schedules, shared variables and encrypted credentials, and it can run each runbook in Azure or on a hybrid worker inside the company network. Each run produces a job with its output, errors and status, which can be sent to a Log Analytics workspace and alerted on.

The runbook's identity is the key design choice. Microsoft's guidance is to give the Automation account a managed identity and grant that identity only the Azure roles it needs, rather than storing a password or using the old Run As accounts, which were retired in 2023. For the spring scale-up, the identity needs permission to change one managed instance, so Walt assigns the SQL Managed Instance Contributor role scoped to that instance alone. For tasks inside a database, such as running a stored procedure, the identity also needs a database user created from its Microsoft Entra name, just as the elastic job agent did in lesson 1.

A flow from left to right. A schedule box labelled Wednesday 22:00 local starts a runbook box inside an Automation account. The runbook carries a badge for a managed identity. An arrow from the runbook goes to Azure Resource Manager, which then changes a managed instance box from 8 vCores to 16 vCores. Below, the runbook sends job output to a Log Analytics workspace, which feeds an alert rule that notifies an action group with email and a Teams message if the job fails.
A scheduled runbook signs in with a managed identity, asks Resource Manager to scale the instance, and logs its result so failures raise an alert.
The scale runbook. It checks the current size first, so running it twice is harmless, and starts the change as a background job.
param([int]$VCores = 16)
Connect-AzAccount -Identity | Out-Null
$mi = Get-AzSqlInstance -ResourceGroupName 'rg-stockline-prod-cc' -Name 'hf-mi-stockline'
if ($mi.VCores -eq $VCores) { Write-Output "Already at $VCores vCores; nothing to do."; return }
Set-AzSqlInstance -ResourceGroupName $mi.ResourceGroupName -Name $mi.ManagedInstanceName `
    -VCore $VCores -Force -AsJob | Out-Null
Write-Output "Scale to $VCores vCores started at $(Get-Date -Format o)."

Schedules in Azure Automation have a time zone of their own. Walt sets both spring schedules in Central Time (Winnipeg), so they follow daylight saving time: the scale-up on the Wednesday at 22:00 and the scale-down the Tuesday after the long weekend at 22:00. Because Azure stores and reports job times in UTC, the job history will show a scale-up started at 03:00 UTC on Thursday.

Worked problem

Reading the schedule in UTC

In 2027, the spring scale-up is planned for 22:00 local time on Wednesday, May 19, in Winnipeg, which is on Central Daylight Time (five hours behind UTC) by then. The scale takes about 2 hours 40 minutes. At what UTC time will the job history show the start, and when should the instance be ready in local time?

Show solution
  1. Central Daylight Time is UTC minus 5 hours, so 22:00 local is 22:00 + 5:00 = 03:00 UTC.
  2. Adding 5 hours carries the time past midnight, so the UTC date is Thursday, May 20.
  3. Finish in local time: 22:00 + 2 hours 40 minutes = 00:40 on Thursday, May 20.
  4. The first deliveries are scanned at 05:30, leaving 4 hours 50 minutes of margin.

Answer: The job history will show a start at 03:00 UTC on Thursday, May 20, 2027, and the instance should be ready around 00:40 local time, well before the first scans.

Other places automation can run

Azure Automation is not the only home for scripts. Azure Functions run code in response to a timer or an event, and suit small tasks that developers already maintain. Azure Logic Apps are workflows built from connectors, useful when a task joins several services, for example: when a long-term backup finishes, post a message in Teams and add a row to a spreadsheet. Azure DevOps or GitHub Actions pipelines run deployments when code changes. The study guide does not ask you to master each of these, but it does expect you to know that database tasks can be automated with several Azure services and to choose one that matches who will maintain it.

Automating deployments with templates

MSAZ-280 introduced Bicep for building databases. This course adds what happens around the deployment. A deployment can be scoped to a resource group, a subscription, a management group or a tenant, and it runs in one of two modes. Incremental mode, the default, adds and updates the resources in the template and leaves others alone. Complete mode deletes resources in the resource group that are not in the template, which is powerful and dangerous; Renata's pipelines never use it for database resource groups. Before any change reaches production, the pipeline runs a what-if operation and saves its output, so a reviewer can see that a template will update one database's settings and not delete a server.

Previewing a new store database, deploying it with a named deployment, and listing any failed operations.
az deployment group what-if --resource-group rg-stores-prod-cc --template-file storedb.bicep \
    --parameters storeNumber=24 poolName=pool-storesched-prod

az deployment group create --resource-group rg-stores-prod-cc --template-file storedb.bicep \
    --parameters storeNumber=24 poolName=pool-storesched-prod --name store24-$(date +%Y%m%d)

az deployment operation group list --resource-group rg-stores-prod-cc --name store24-20261120 \
    --query "[?properties.provisioningState=='Failed'].properties.statusMessage"

Monitoring a deployment means reading its operations. Every deployment has a name, a history in the resource group and an entry in the activity log, and each resource it touches is an operation with its own status and message. When a deployment fails, the overall error often says only that one or more operations failed; the useful message is in the failed operation, which might say that a name is already taken, that a quota is exhausted, that a policy denied the request, or that the identity running the pipeline lacks a role. Walt's first deployment of a test pool failed with a policy denial, because the company's Azure Policy requires every database to use zone-redundant or geo-redundant backup storage and his template had asked for locally redundant storage. The fix was in the template, not the policy.

Common deployment and runbook failures and where to find the real error.
SymptomLikely causeWhere to look
Deployment fails at once with an authorization errorThe pipeline or runbook identity lacks a roleActivity log, role assignments
RequestDisallowedByPolicyAn Azure Policy assignment denies a settingThe failed operation message, Policy compliance
Name not availableA server or database name is already usedThe failed operation message
Deployment succeeds but nothing changesThe template matches the current stateWhat-if output shows no changes
Runbook job suspended or stoppedUnhandled error, or the fair-share time limitAutomation job output and streams

Python example

Elena asks Walt to check how much the scale-up schedule really saves, using the figures from MSAZ-280, before he presents the runbook to the change board. He writes the calculation as a short Python script so he can change the assumptions in front of the board.

Python Example · Comparing the yearly compute cost of scheduled scaling with sizing for the peak all year.
price = 0.55          # illustrative dollars per vCore hour
hours_per_week = 168
base, peak = 8, 16
for peak_weeks in (1, 2, 3):
    scaled = (52 - peak_weeks) * hours_per_week * base * price + peak_weeks * hours_per_week * peak * price
    always_peak = 52 * hours_per_week * peak * price
    print(f"{peak_weeks} peak week(s): scaled {scaled:,.2f}  always 16 vCores {always_peak:,.2f}  saving {always_peak - scaled:,.2f}")
Output
1 peak week(s): scaled 39,177.60  always 16 vCores 76,876.80  saving 37,699.20
2 peak week(s): scaled 39,916.80  always 16 vCores 76,876.80  saving 36,960.00
3 peak week(s): scaled 40,656.00  always 16 vCores 76,876.80  saving 36,220.80

What this code does

The script takes an illustrative price per vCore hour, the hours in a week and the two instance sizes, and works out the year's compute cost two ways for one, two and three busy weeks: scaling up only for the busy weeks, or keeping the larger size all year. Each line of output shows both totals and the difference. The difference is the money the scheduled runbook saves, which shrinks slightly as the busy period gets longer.

The output shows that the saving hardly depends on how long the busy period lasts, because most of the year is spent at the smaller size either way. That gives Walt an easy answer when the change board asks whether to scale up a week earlier for safety.

Common mistakes

  • MistakeScripts and templates are interchangeable for every task.

    InsteadTemplates suit lasting resources; scripts and runbooks suit actions such as scaling, copying and failover tests.

  • MistakeA runbook should sign in with a stored administrator password.

    InsteadGive the Automation account a managed identity with the narrowest role it needs.

  • MistakeComplete mode is a safer way to deploy database templates.

    InsteadComplete mode deletes resources not in the template; incremental mode with what-if is the safe default.

  • MistakeA failed deployment's top-level error explains the problem.

    InsteadThe useful message is usually in the failed operation inside the deployment.

Summary and key terms

  • Azure PowerShell, the Azure CLI and templates all call Azure Resource Manager; templates are declarative and safe to redeploy, scripts are imperative actions.
  • Use scripts or runbooks for actions such as scaling, copying and failover tests, and templates for lasting resources.
  • Azure Automation runbooks add schedules, a managed identity and job logs; grant the identity the narrowest role.
  • Start long operations without waiting and check them later, and make runbooks check the current state first.
  • Preview deployments with what-if, avoid complete mode for database resource groups, and read failed operations to troubleshoot.
Azure Resource Manager
The Azure service that receives and carries out every request to create, change or delete resources.
Runbook
A script stored and run by Azure Automation, on a schedule or on demand.
Automation account
The Azure Automation resource that holds runbooks, schedules, variables and credentials.
What-if
A deployment preview that lists the changes a template would make.
Incremental mode
The default deployment mode, which adds and updates resources but deletes nothing.
Complete mode
A deployment mode that deletes resources in the scope that are not in the template.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Why is a template better than a script for building the non-production instance?

    Show answer

    It is declarative, so deploying it again changes nothing unless the template changed.

  2. Which identity should a runbook use to scale a managed instance?

    Show answer

    The Automation account's managed identity with a narrowly scoped role.

  3. What does the CLI's no-wait option do?

    Show answer

    Starts a long operation and returns immediately, so its progress is checked later.

  4. What does what-if show?

    Show answer

    The resources a deployment would create, change or delete.

  5. Where is the useful error when a deployment fails?

    Show answer

    In the failed operation's status message inside the deployment.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz