Skip to content

Commit 6c6eba3

Browse files
authored
Merge pull request #1475 from claestom/sql-arc-lt-policy
Add custom Azure Policy and scripts for Arc-enabled SQL Server license type configuration
2 parents 5908084 + 90195f1 commit 6c6eba3

File tree

11 files changed

+608
-243
lines changed

11 files changed

+608
-243
lines changed
Lines changed: 95 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,120 @@
1-
# LicenseType-SQLArc
1+
# Arc-enabled SQL Server license type configuration with Azure Policy
22

3-
This Azure Policy ensures that all SQL Arc servers have the required `LicenseType` value. Servers that do not match the required license type are marked as non-compliant. The remediation task sets `LicenseType` to the value specified by the `requiredLicenseType` parameter.
3+
This repo deploys and remediates a custom Azure Policy that configures and enforces Arc-enabled SQL Server extension `LicenseType` to a selected target value (for example `Paid` or `PAYG`).
44

5-
Use Azure CLI or PowerShell to create the policy definition.
5+
## What Is In This Repo
66

7-
## Artifacts
7+
- `policy/azurepolicy.json`: Custom policy definition (DeployIfNotExists).
8+
- `scripts/deployment.ps1`: Creates/updates the policy definition and policy assignment.
9+
- `scripts/start-remediation.ps1`: Starts a remediation task for the created assignment.
10+
- `docs/screenshots/`: Visual references.
811

9-
- **policy.json**: Main policy definition referencing external parameter and rule files.
10-
- **params.json**: Defines policy parameters.
11-
- **rules.json**: Contains the policy rule logic.
12+
## Prerequisites
1213

13-
## Copy policy artifacts to your environment
14+
- PowerShell with Az modules installed (`Az.Resources`).
15+
- Logged in to Azure (`Connect-AzAccount`).
16+
- Permissions to create policy definitions/assignments and remediation tasks at target scope.
1417

15-
```PowerShell
18+
## Deploy Policy
1619

17-
curl https://raw.githubusercontent.com/microsoft/sql-server-samples/refs/heads/master/samples/manage/azure-arc-enabled-sql-server/compliance/arc-sql-license-type-compliance/params.json -o params.json
18-
curl https://raw.githubusercontent.com/microsoft/sql-server-samples/refs/heads/master/samples/manage/azure-arc-enabled-sql-server/compliance/arc-sql-license-type-compliance/rules.json -o rules.json
20+
Parameter reference:
1921

20-
```
22+
| Parameter | Required | Default | Allowed values | Description |
23+
|---|---|---|---|---|
24+
| `ManagementGroupId` | Yes | N/A | Any valid management group ID | Scope where the policy definition is created. |
25+
| `ExtensionType` | No | `Both` | `Windows`, `Linux`, `Both` | Targets the Arc SQL extension platform. When `Both` (default), a single policy definition and assignment covers both platforms. When a specific type is selected, the naming and scope are tailored to that platform. |
26+
| `SubscriptionId` | No | Not set | Any valid subscription ID | If provided, policy assignment scope is the subscription. |
27+
| `TargetLicenseType` | Yes | N/A | `Paid`, `PAYG` | Target `LicenseType` value to enforce. |
28+
| `LicenseTypesToOverwrite` | No | All | `Unspecified`, `Paid`, `PAYG`, `LicenseOnly` | Select which current license states are eligible for update. Use `Unspecified` to include resources with no `LicenseType` configured. |
2129

22-
## Create policy
30+
Definition and assignment creation:
2331

24-
Use the following command to create policy
32+
1. Clone the repo.
2533

26-
```PowerShell
34+
```powershell
35+
git clone https://github.com/microsoft/sql-server-samples.git
36+
cd sql-server-samples/samples/manage/azure-arc-enabled-sql-server/compliance/arc-sql-license-type-compliance
37+
```
2738

28-
$SubId = "<your-subscription-id>"
29-
$PolicyName = "LicenseType-SQLArc"
39+
2. Login to Azure.
3040

31-
az policy definition create `
32-
--name $PolicyName `
33-
--display-name $PolicyName `
34-
--description "This Azure Policy ensures that all SQL Arc servers have the required LicenseType. Servers that do not match are marked as non-compliant and remediated to the required license type." `
35-
--rules "@rules.json" `
36-
--params "@params.json" `
37-
--mode Indexed `
38-
--subscription $SubId `
39-
--only-show-errors | Out-Null
41+
```powershell
42+
Connect-AzAccount
4043
```
4144

42-
## Assign policy
45+
```powershell
46+
# Example: target both platforms (default)
47+
.\scripts\deployment.ps1 -ManagementGroupId "<management-group-id>" -SubscriptionId "<subscription-id>" -TargetLicenseType "PAYG" -LicenseTypesToOverwrite @("Paid")
4348
44-
Use the following command to assign policy
49+
# Example: target only Linux
50+
.\scripts\deployment.ps1 -ManagementGroupId "<management-group-id>" -ExtensionType "Linux" -SubscriptionId "<subscription-id>" -TargetLicenseType "PAYG" -LicenseTypesToOverwrite @("Paid")
51+
```
52+
The first example (without `-ExtensionType`) will:
53+
* Create/update a single policy definition and assignment covering **both** Windows and Linux.
54+
* Assign that policy at the specified subscription scope.
55+
* Enforce LicenseType = PAYG.
56+
* Update only resources where current `LicenseType` is `Paid`.
4557

46-
```PowerShell
58+
The second example creates a Linux-specific definition and assignment, with platform-tailored naming.
4759

48-
$SubId = "<your-subscription-id>"
49-
$RgName = "<your-resource-group>" # optional; set to "" to target subscription scope
50-
$Location = "<your-azure-region>" # e.g., eastus, westus2
51-
$RequiredLicenseType = "PAYG" # e.g., PAYG, LicenseOnly
60+
Scenario examples:
5261

53-
if ([string]::IsNullOrWhiteSpace($RgName)) {
54-
$Scope = "/subscriptions/$SubId"
55-
} else {
56-
$Scope = "/subscriptions/$SubId/resourceGroups/$RgName"
57-
}
62+
```powershell
63+
# Target Paid, both Linux and Windows, but only for resources with missing LicenseType or LicenseOnly (do not target PAYG)
64+
.\scripts\deployment.ps1 -ManagementGroupId "<management-group-id>" -TargetLicenseType "Paid" -LicenseTypesToOverwrite @("Unspecified","LicenseOnly")
5865
59-
az account set --subscription $SubId
66+
# Target PAYG, but only where current LicenseType is Paid (do not target missing or LicenseOnly)
67+
.\scripts\deployment.ps1 -ManagementGroupId "<management-group-id>" -ExtensionType "Linux" -TargetLicenseType "PAYG" -LicenseTypesToOverwrite @("Paid")
6068
61-
az policy assignment create `
62-
--name "LicenseType-SQLArc-Assign" `
63-
--policy "LicenseType-SQLArc" `
64-
--scope "$Scope" `
65-
--params "{ \"effect\": { \"value\": \"DeployIfNotExists\" }, \"requiredLicenseType\": { \"value\": \"$RequiredLicenseType\" } }" `
66-
--mi-system-assigned `
67-
--role "Contributor" `
68-
--identity-scope "$Scope" `
69-
--location "$Location" `
70-
--only-show-errors | Out-Null
69+
# Overwrite all known existing LicenseType values (Paid, PAYG, LicenseOnly), but not missing
70+
.\scripts\deployment.ps1 -ManagementGroupId "<management-group-id>" -ExtensionType "Linux" -TargetLicenseType "Paid" -LicenseTypesToOverwrite @("Paid","PAYG","LicenseOnly")
7171
```
7272

73-
## Create remediation task
74-
75-
Use the following command to create a remediation task
76-
77-
```PowerShell
78-
79-
$RemediationName = "Remediate-LicenseType-SQLArc"
80-
$PolicyAssignmentName = "LicenseType-SQLArc-Assign"
81-
$SubId = "<your-subscription-id>"
82-
$RgName = "<your-resource-group>"
83-
84-
az account set --subscription $SubId
85-
86-
if ([string]::IsNullOrWhiteSpace($RgName)) {
87-
az policy remediation create `
88-
--name $RemediationName `
89-
--policy-assignment $PolicyAssignmentName `
90-
--resource-discovery-mode ReEvaluateCompliance `
91-
--only-show-errors | Out-Null
92-
} else {
93-
az policy remediation create `
94-
--name $RemediationName `
95-
--policy-assignment $PolicyAssignmentName `
96-
--resource-group "$RgName" `
97-
--resource-discovery-mode ReEvaluateCompliance `
98-
--only-show-errors | Out-Null
99-
}
100-
```
73+
Note: `scripts/deployment.ps1` automatically grants required roles to the policy assignment managed identity at assignment scope, preventing common `PolicyAuthorizationFailed` errors during DeployIfNotExists deployments.
74+
75+
## Start Remediation
10176

102-
## Remove remediation task
103-
104-
```PowerShell
105-
106-
$RemediationName = "Remediate-LicenseType-SQLArc"
107-
$RgName = "<your-resource-group>"
108-
$SubId = "<your-subscription-id>"
109-
110-
if ([string]::IsNullOrWhiteSpace($RgName)) {
111-
az policy remediation cancel `
112-
--name $RemediationName `
113-
--subscription $SubId `
114-
--only-show-errors | Out-Null
115-
az policy remediation delete `
116-
--name $RemediationName `
117-
--subscription $SubId `
118-
--only-show-errors | Out-Null
119-
} else {
120-
az policy remediation cancel `
121-
--name $RemediationName `
122-
--resource-group $RgName `
123-
--subscription $SubId `
124-
--only-show-errors | Out-Null
125-
az policy remediation delete `
126-
--name $RemediationName `
127-
--resource-group $RgName `
128-
--subscription $SubId `
129-
--only-show-errors | Out-Null
130-
}
77+
Parameter reference:
78+
79+
| Parameter | Required | Default | Allowed values | Description |
80+
|---|---|---|---|---|
81+
| `ManagementGroupId` | Yes | N/A | Any valid management group ID | Used to resolve the policy definition/assignment naming context. |
82+
| `ExtensionType` | No | `Both` | `Windows`, `Linux`, `Both` | Must match the platform used for the assignment. When `Both` (default), remediates the combined assignment. |
83+
| `SubscriptionId` | No | Not set | Any valid subscription ID | If provided, remediation runs at subscription scope. |
84+
| `TargetLicenseType` | Yes | N/A | `Paid`, `PAYG` | Must match the assignment target license type. |
85+
| `GrantMissingPermissions` | No | `false` | Switch (`present`/`not present`) | If set, checks and assigns missing required roles before remediation. |
86+
87+
```powershell
88+
# Example: remediate both platforms (default)
89+
.\scripts\start-remediation.ps1 -ManagementGroupId "<management-group-id>" -SubscriptionId "<subscription-id>" -TargetLicenseType "PAYG" -GrantMissingPermissions
90+
91+
# Example: remediate only Linux
92+
.\scripts\start-remediation.ps1 -ManagementGroupId "<management-group-id>" -ExtensionType "Linux" -SubscriptionId "<subscription-id>" -TargetLicenseType "PAYG" -GrantMissingPermissions
13193
```
94+
95+
## Managed Identity And Roles
96+
97+
The policy assignment is created with `-IdentityType SystemAssigned`. Azure creates a managed identity on the assignment and uses it to apply DeployIfNotExists changes during enforcement and remediation.
98+
99+
Required roles:
100+
101+
- `Azure Extension for SQL Server Deployment` (`7392c568-9289-4bde-aaaa-b7131215889d`)
102+
- `Reader` (`acdd72a7-3385-48ef-bd42-f606fba81ae7`)
103+
- `Resource Policy Contributor` (required so DeployIfNotExists can create template deployments)
104+
105+
## Troubleshooting
106+
107+
If you see `PolicyAuthorizationFailed`, the policy assignment identity is missing one or more required roles at assignment scope (or inherited scope), often causing missing `Microsoft.HybridCompute/machines/extensions/write` permission.
108+
109+
Use one of these options:
110+
111+
- Re-run `scripts/deployment.ps1` (default behavior assigns `Resource Policy Contributor` automatically).
112+
- Re-run `scripts/deployment.ps1` (default behavior assigns required roles automatically).
113+
- Run `scripts/start-remediation.ps1 -GrantMissingPermissions` (checks and assigns missing required roles before remediation).
114+
115+
## Screenshots
116+
117+
![overview](./docs/screenshots/overview.png)
118+
![pre-policy](./docs/screenshots/pre-policy.png)
119+
![remediation](./docs/screenshots/remediation.png)
120+
![postpolicy](./docs/screenshots/post-policy.png)
173 KB
Loading
86.7 KB
Loading
86.5 KB
Loading
115 KB
Loading

samples/manage/azure-arc-enabled-sql-server/compliance/arc-sql-license-type-compliance/params.json

Lines changed: 0 additions & 27 deletions
This file was deleted.

samples/manage/azure-arc-enabled-sql-server/compliance/arc-sql-license-type-compliance/policy.json

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)