Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Service Principal : Set-AzureRmKeyVaultAccessPolicy : Insufficient privileges to complete the operation

Post updated. Issue has been solved. The scripts below will create a resource group, create a service principal, deploy a key vault, configure permissions and write a secret to the vault. Hopes this help! :)

Problem: I am logged into PowerShell as a Service Principal that has Owner permissions on a resource group. I get permission errors when i try to create a vault, set permission on the vault and when i try to write secrets.

permission errors

Solution: Step 1: Create resource group and Service Principal. You must be logged in as an administrator to execute this script

Clear-Host
Import-Module Azure
Import-Module AzureRM.Resources

Add-AzureRmAccount
Get-AzureRmSubscription
Set-AzureRmContext -SubscriptionId <Your subscription id goes here>

$ServicePrincipalDisplayName = "myServicePrincipalName"
$CertificateName = "CN=SomeCertName" 

$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" -Subject $CertificateName -KeySpec KeyExchange
$keyValue = [Convert]::ToBase64String($cert.GetRawCertData())

$ResouceGroupName = "myRessourceGroup"
$location = "North Central US"

# Create the resource group
New-AzureRmResourceGroup -Name $ResouceGroupName -Location $location

$ResouceGroupNameScope = (Get-AzureRmResourceGroup -Name $ResouceGroupName -ErrorAction Stop).ResourceId

# Create the Service Principal that logs in with a certificate
New-AzureRMADServicePrincipal -DisplayName $ServicePrincipalDisplayName -CertValue $keyValue -EndDate $cert.NotAfter -StartDate $cert.NotBefore

$myServicePrincipal = Get-AzureRmADServicePrincipal -SearchString $ServicePrincipalDisplayName
Write-Host "myServicePrincipal.ApplicationId " $myServicePrincipal.ApplicationId -ForegroundColor Green
Write-Host "myServicePrincipal.DisplayName " $myServicePrincipal.DisplayName

# Sleep here for a few seconds to allow the service principal application to become active (should only take a couple of seconds normally)
Write-Host "Waiting 10 seconds"
Start-Sleep -s 10

Write-Host "Make the Service Principal owner of the resource group " $ResouceGroupName

$NewRole = $null
$Retries = 0
 While ($NewRole -eq $null -and $Retries -le 6)
 {  
    New-AzureRMRoleAssignment -RoleDefinitionName Owner -ServicePrincipalName $myServicePrincipal.ApplicationId  -Scope $ResouceGroupNameScope -ErrorAction SilentlyContinue    
    $NewRole = Get-AzureRMRoleAssignment -ServicePrincipalName $myServicePrincipal.ApplicationId
    Write-Host "NewRole.DisplayName " $NewRole.DisplayName
    Write-Host "NewRole.Scope: " $NewRole.Scope
    $Retries++

    Start-Sleep -s 10
 }

Write-Host "Service principal created" -ForegroundColor Green

Step 2 : ARM deployment of the vault. Create a filenamed keyvault2.parameters.json Update the id's to reflect your installation (5479eaf6-31a3-4be3-9fb6-c2cdadc31735 is the service principal used by azure web apps when accessing the vault.)

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
  "parameters": {
    "vaultName": {
      "value": "valueFromParameterFile"
    },
    "vaultlocation": {
      "value": "valueFromParameterFile"
    },
    "skumode": {
      "value": "Standard"
    },
    "accessPolicyList": {
      "value": [
        {
          "objectId": "The object ID for your AAD user goes here so that you can read secrets etc",
          "tenantId": "Your Tenant Id goes here",
          "permissions": {
            "keys": [
              "Get",
              "List"
            ],
            "secrets": [
              "Get",
              "List"
            ],
            "certificates": [
              "Get",
              "List"
            ]
          }
        },
        {
          "objectId": "The object ID for the service principal goes here Get-AzureRmADServicePrincipal -ServicePrincipalName <Service Principal Application ID>",
          "tenantId": "Your Tenant Id goes here",
          "permissions": {
            "keys": [
              "Get",
              "List",
              "Update",
              "Create",
              "Import",
              "Delete",
              "Recover",
              "Backup",
              "Restore"
            ],
            "secrets": [
              "Get",
              "List",
              "Set",
              "Delete",
              "Recover",
              "Backup",
              "Restore"
            ],
            "certificates": [
              "Get",
              "List",
              "Update",
              "Create",
              "Import",
              "Delete",
              "ManageContacts",
              "ManageIssuers",
              "GetIssuers",
              "ListIssuers",
              "SetIssuers",
              "DeleteIssuers"
            ]
          },
          "applicationId": null
        },
        {
        "objectId": "5479eaf6-31a3-4be3-9fb6-c2cdadc31735",
        "tenantId": "Your Tenant Id goes here",
        "permissions": {
            "keys": [],
            "secrets": [
                "Get"
            ],
            "certificates": []
        },
        "applicationId": null
    }
      ]
    },
    "tenant": {
      "value": "Your Tenant Id goes here"
    },
    "isenabledForDeployment": {
      "value": true
    },
    "isenabledForTemplateDeployment": {
      "value": false
    },
    "isenabledForDiskEncryption": {
      "value": false
    }
  }
}

Step 3 : ARM deployment of the vault. Create a filenamed keyvault2.template.json

{
    "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
  "parameters": {
    "vaultName": {
      "type": "string"
    },
    "vaultlocation": {
      "type": "string"
    },
    "skumode": {
      "type": "string",
      "defaultValue": "Standard",
      "allowedValues": [
        "Standard",
        "standard",
        "Premium",
        "premium"
      ],
      "metadata": {
        "description": "SKU for the vault"
      }
    },
    "accessPolicyList": {
      "type": "array",
      "defaultValue": [],
      "metadata": {
        "description": "The access policies defined for this vault."
      }
    },
    "tenant": {
      "type": "string"
    },
    "isenabledForDeployment": {
      "type": "bool"
    },
    "isenabledForTemplateDeployment": {
      "type": "bool"
    },
    "isenabledForDiskEncryption": {
      "type": "bool"
    }
  },
    "resources": [
      {
        "apiVersion": "2015-06-01",
        "name": "[parameters('vaultName')]",
        "location": "[parameters('vaultlocation')]",
        "type": "Microsoft.KeyVault/vaults",
        "properties": {
          "enabledForDeployment": "[parameters('isenabledForDeployment')]",
          "enabledForTemplateDeployment": "[parameters('isenabledForTemplateDeployment')]",
          "enabledForDiskEncryption": "[parameters('isenabledForDiskEncryption')]",
          "accessPolicies": "[parameters('accessPolicyList')]",
          "tenantId": "[parameters('tenant')]",
          "sku": {
            "name": "[parameters('skumode')]",
            "family": "A"
          }
        }
      }
    ]
}

Step 4 : Deploy vault. Start a new powershell window and execute this script. Update 3 x id's

Clear-Host

Import-Module Azure
Import-Module AzureRM.Resources    

$ServicePrincipalApplicationId = "xxx"
$TenantId = "yyy"
$SubscriptionId = "zzz"
$CertificateName = "CN=SomeCertName"
$ResouceGroupName = "myRessourceGroup"
$location = "North Central US"
$VaultName = "MyVault" + (Get-Random -minimum 10000000 -maximum 1000000000)
$MySecret = ConvertTo-SecureString -String "MyValue" -AsPlainText -Force

$Cert = Get-ChildItem cert:\CurrentUser\My\ | Where-Object {$_.Subject -match $CertificateName }
Write-Host "cert.Thumbprint " $cert.Thumbprint
Write-Host "cert.Subject " $cert.Subject

Add-AzureRmAccount -ServicePrincipal -CertificateThumbprint $cert.Thumbprint -ApplicationId $ServicePrincipalApplicationId -TenantId $TenantId
Get-AzureRmSubscription
Set-AzureRmContext -SubscriptionId $SubscriptionId

Write-Host ""
Write-Host "Creating vault" -ForegroundColor Yellow

New-AzureRmResourceGroupDeployment -ResourceGroupName $ResouceGroupName -vaultName $vaultName -vaultlocation $location -isenabledForDeployment $true -TemplateFile ".\keyvault2.template.json"  -TemplateParameterFile ".\keyvault2.parameters.json"

Write-Host ""
Write-Host "Key Vault " $vaultName " deployed" -ForegroundColor green

Write-Host "Wait 5 seconds"
Start-Sleep -Seconds 5

Write-Host "Write Secret" -ForegroundColor Yellow    
Set-AzureKeyVaultSecret -VaultName $VaultName -Name "MyKey" -SecretValue $MySecret

Write-Host "Wait 10 seconds"
Start-Sleep -Seconds 10

Write-Host "Read secret"
Get-AzureKeyVaultSecret -VaultName $VaultName -Name "MyKey"
like image 568
Tony Avatar asked Jul 13 '17 12:07

Tony


3 Answers

Set-AzureRmKeyVaultAccessPolicy -VaultName $name -ObjectId $oId -PermissionsToSecrets get
returns error
Set-AzureRmKeyVaultAccessPolicy : Insufficient privileges to complete the operation.

Solution is to add additional parameter -BypassObjectIdValidation

Set-AzureRmKeyVaultAccessPolicy -BypassObjectIdValidation -VaultName $name -ObjectId $oId -PermissionsToSecrets get

Solution looks like a hack, but it works for me. After this, object with $oId have got access to keyVault. (For checks access polices use Get-AzureRmKeyVault -VaultName $vaultName)

like image 121
Alessar Avatar answered Nov 17 '22 08:11

Alessar


The solution was to move the configuration of the permission to the ARM template instead of trying to do it using PowerShell. As soon as i did that all permission issues got solved.

In the ARM template the object Id i had specified for the Service Principal was wrong. It thought it as the Object Id you can find in the portal under app registrations, but no, it is actually the object ID of the service principal of the Azure AD application it wants.

It will let you deploy the ARM template just fine even if you use the wrong Id and everything like too correct configured, until you start wondering about why the icon looks different for you service principal compared to the other users. This of course you will not notice until much later if you like me only had one user ...

Wrong id (This icon is different): wrong id

Correct id: enter image description here

This post gave me that final solution.

How do I fix an "Operation 'set' not allowed" error when creating an Azure KeyVault secret programmatically?

like image 3
Tony Avatar answered Nov 17 '22 09:11

Tony


I struggled with this issue a lot this week, as I don't have permission in my AAD to add API permissions for the service principal. I found a solution using the ARM Output marketplace item. Using the ARM output task, I can retrieve the Principal ID's of my objects from the ARM template and convert them to pipeline variables, which in turn can be consumed by an Azure PowerShell script to successfully update the Key vault access policies .

In the ARM template, I added this output variable, to return the website principal id - this is the information I couldn't query AD with.

"outputs": {
  "websitePrincipalId": {
    "type": "string",
    "value": "[reference(concat(resourceId('Microsoft.Web/sites', variables('webSiteName')), '/providers/Microsoft.ManagedIdentity/Identities/default'), '2015-08-31-PREVIEW').principalId]"
  }
}

Then I used the ARM Output task, to return the output as pipeline variables, which is useful in a Azure PowerShell script where I was able to use this to populate my key vault with the correct access policies:

Set-AzKeyVaultAccessPolicy -VaultName "$(KeyVaultName)" -ObjectId "$(servicePrincipalId)" -PermissionsToSecrets list,get -PassThru -BypassObjectIdValidation
like image 1
Sam Avatar answered Nov 17 '22 08:11

Sam