Martin Cairney
Martin Cairney

Reputation: 1767

ARM Template - Set dependency on RBAC Role existing

I am able to grant my SQL Server Identity membership of the Storage Blob Contributor role on my storage account using an ARM template.

However, when creating my Audit resource (which has dependsOn references to both the Storage Account and to the SQL Server) it looks like it is trying to create this before the RBAC permission has been granted as I get

{
    "status": "Failed",
    "error": {
        "code": "ResourceDeploymentFailure",
        "message": "The resource operation completed with terminal provisioning state 'Failed'.",
        "details": [
            {
                "code": "BlobAuditingInsufficientStorageAccountPermissions",
                "message": "Insufficient read or write permissions on storage account 'myStorage'. Add permissions to the server Identity to the storage account."
            }
        ]
    }
}

Oddly, I have this for Vulnerability Scans from the same server to another Storage Account and this succeeded. I don't know if that was simply a quirk of the way that Azure decided to order the deployment.

Anyway, is there a way to add a dependsOn reference to the RBAC permission so that I don't try to create the Audit destination to Storage until it has permissions?

To be clear, the RBAC assignment is a sub-resource of the Storage Account:

{
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-06-01",
            "name": "[variables('auditStorageAccount')]",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[variables('uniqueSQLName')]"
            ],
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "kind": "StorageV2",
            "properties": {
                "isHnsEnabled": false,
                "networkAcls": {
                    "bypass": "AzureServices",
                    "virtualNetworkRules": [],
                    "ipRules": [],
                    "defaultAction": "Deny"
                },
                "supportsHttpsTrafficOnly": true,
                "encryption": {
                    "services": {
                        "file": {
                            "keyType": "Account",
                            "enabled": true
                        },
                        "blob": {
                            "keyType": "Account",
                            "enabled": true
                        }
                    },
                    "keySource": "Microsoft.Storage"
                },
                "accessTier": "Hot"
            },
            "resources": [
                {
                    "type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
                    "apiVersion":  "2020-03-01-preview",
                    "name": "[concat(variables('auditStorageAccount'), '/Microsoft.Authorization/', guid(uniqueString(variables('auditStorageAccount'))))]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Storage/storageAccounts', variables('auditStorageAccount'))]",
                        "[resourceId('Microsoft.Sql/servers', variables('uniqueSQLName'))]"
                    ],
                    "properties": {
                        "roleDefinitionId": "[variables('StorageBlobContributor')]",
                        "principalId": "[reference(resourceId('Microsoft.Sql/servers', variables('uniqueSQLName')), '2019-06-01-preview', 'Full').identity.principalId]",
                        "scope": "[resourceId('Microsoft.Storage/storageAccounts', variables('auditStorageAccount'))]",
                        "principalType": "ServicePrincipal"
                    }
                }
            ]
        }

My assumption was that having it as a sub-resource would ensure that it was in place for anything that then had a dependsOn to the Storage Account

Upvotes: 0

Views: 1308

Answers (1)

Jim Xu
Jim Xu

Reputation: 23111

According to the error, I think when you deploy the SQL audit log resource, the assigned role action does not complete successfully.So We need to define in the template that the audit log resource depends on the role assignment actions

For example

  1. My template.json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "sqlServerName": {
            "type": "string",
            "defaultValue": "[concat('sql-', uniqueString(resourceGroup().id))]",
            "metadata": {
                "description": "Name of the SQL server"
            }
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",
            "metadata": {
                "description": "Location for all resources."
            }
        },
        "sqlAdministratorLogin": {
            "type": "string",
            "defaultValue":"sqladmin",
            "metadata": {
                "description": "The administrator username of the SQL Server."
            }
        },
        "sqlAdministratorLoginPassword": {
            "type": "securestring",
            "defaultValue":"Password0123!",
            "metadata": {
                "description": "The administrator password of the SQL Server."
            }
        },
        "storageAccountName": {
            "type": "string",
            "defaultValue": "[concat('sqlaudit', uniqueString(resourceGroup().id))]",
            "metadata": {
                "description": "The name of the auditing storage account."
            }
        }
    },
    "variables": {
        "StorageBlobContributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
        "uniqueRoleGuid": "[guid(resourceId('Microsoft.Storage/storageAccounts',  parameters('storageAccountName')), variables('storageBlobContributor'), resourceId('Microsoft.Sql/servers', parameters('sqlServerName')))]"
    },
    "resources": [{
            "type": "Microsoft.Storage/storageAccounts",
            "name": "[parameters('storageAccountName')]",
            "apiVersion": "2019-06-01",
            "location": "[parameters('location')]",
            "sku": {
                "name": "Standard_LRS"
            },
            "kind": "StorageV2",
            "properties": {
                "networkAcls": {
                    "bypass": "AzureServices",
                    "defaultAction": "Allow"
                }
            },
            "resources": [{
                   
                    "type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
                    "apiVersion": "2020-03-01-preview",
                    "name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', variables('uniqueRoleGuid'))]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Sql/servers', parameters('sqlServerName'))]",
                        "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
                    ],
                    "properties": {
                        "roleDefinitionId": "[variables('StorageBlobContributor')]",
                        "principalId": "[reference(resourceId('Microsoft.Sql/servers', parameters('sqlServerName')), '2019-06-01-preview', 'Full').identity.principalId]",
                        "scope": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
                        "principalType": "ServicePrincipal"
                    }
                }
            ]
        }, {
            "type": "Microsoft.Sql/servers",
            "apiVersion": "2019-06-01-preview",
            "location": "[parameters('location')]",
            "name": "[parameters('sqlServerName')]",
            "identity": {
                "type": "SystemAssigned"
            },
            "properties": {
                "administratorLogin": "[parameters('sqlAdministratorLogin')]",
                "administratorLoginPassword": "[parameters('sqlAdministratorLoginPassword')]",
                "version": "12.0"
            },
            "tags": {
                "displayName": "[parameters('sqlServerName')]"
            },
            "resources": [{
                    "type": "auditingSettings",
                    "apiVersion": "2019-06-01-preview",
                    "name": "DefaultAuditingSettings",
                    "dependsOn": [
                        "[parameters('sqlServerName')]",
                        "[parameters('storageAccountName')]",
                        "[extensionResourceId(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), 'Microsoft.Authorization/roleAssignments/', variables('uniqueRoleGuid'))]"
                    ],
                    "properties": {
                        "state": "Enabled",
                        "storageEndpoint": "[reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').PrimaryEndpoints.Blob]",
                        "storageAccountAccessKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2019-06-01').keys[0].value]",
                        "auditActionsAndGroups": [
                            "SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP",
                            "FAILED_DATABASE_AUTHENTICATION_GROUP",
                            "BATCH_COMPLETED_GROUP"
                         ],
                        "storageAccountSubscriptionId": "[subscription().subscriptionId]",
                        "isStorageSecondaryKeyInUse": false
                    }
                }
            ]
        }
    ]
}

  1. Deploy
New-AzResourceGroupDeployment -ResourceGroupName <resource-group-name> -TemplateFile <path-to-template>

enter image description here enter image description here

For more details, please refer to the sample

Upvotes: 1

Related Questions