8

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"
Tony
  • 1,394
  • 5
  • 22
  • 48

5 Answers5

8

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)

Alessar
  • 149
  • 2
  • 4
  • Thanks for this. At work, I have contributor rights to our tenant but the service principal that the VSTS release runs as does not. So, I am able to execute the above without the -BypassObjectIdValidation parameter. However, the VSTS release was failing to run it. Thanks to your answer it makes sense now that it was trying to validate the OID against AAD which it does not have rights to do. Thus, I don't think your answer is a hack. Thanks again. – Jim Speaker Apr 17 '19 at 22:06
3

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?

Tony
  • 1,394
  • 5
  • 22
  • 48
  • i understand that ARM templates are the way to go. but i'm in a scenario where i want to share a key vault between a couple of web apps. these web apps are deployed separately so i want to be able to add a new web app to an existing key vault. so using powershell seems the only option. is there any way to get this working? @Walter-MSFT do you know? i did get it to work when i manually execute the powershell command but when the command is executed from a VSTS release it doesnt work because of insufficient permissions. is this because the script is being executed by a service principal then? – GeertvdC Sep 22 '17 at 12:45
2

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
Sam
  • 669
  • 1
  • 13
  • 26
0

According to your description, I test in my lab, I also use my Service Principal to login my Azure subscription. Your cmdlet works for me.

enter image description here

Do you check my Service Principal role? You could check it on Azure Portal.

enter image description here

Please ensure your Service Principal has Contributor or Owner permission. More information about this please refer to this link.

Update:

I test in my lab, your PowerShell script works fine for you. I suggest you could use Power Shell to create key vault and give permissions.

Community
  • 1
  • 1
Shui shengbao
  • 18,746
  • 3
  • 27
  • 45
  • it only works if use the admin account (owner the subscription). I will prepare some scripts and update this post in a few days or maybe open a support case with azure dev support – Tony Jul 16 '17 at 09:14
  • @TonyFabian Yes, it is requires `Owner` permissions. You could set the permissions with CLI 2.0. `az role assignment create --assignee d7d167ca-ad2a-4b31-ab64-7d5b714b7d8d --role Owner` Please refer to this [link](https://learn.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?toc=%2fazure%2fazure-resource-manager%2ftoc.json). – Shui shengbao Jul 17 '17 at 01:33
  • I have updated the post with complete scripts that will replicate the issue. Can you spot what is wrong? – Tony Jul 17 '17 at 14:36
  • @TonyFabian Your script works for me. If you could share your e-mail. I could send detailed result to you. What is Azure PowerShell version? You could use the cmdlet to get the version. `Get-Module -ListAvailable -Name Azure -Refresh` You could download the latest version from the [link](https://github.com/Azure/azure-powershell/releases). – Shui shengbao Jul 18 '17 at 02:23
  • @TonyFabian Also, `Set-AzureRmKeyVaultAccessPolicy -VaultName $VaultName -ServicePrincipalName abfa0a7c-a6b6-4736-8310-5855508787cd -PermissionsToSecrets get` If you only give `get` permission. You could not `Set-AzureKeyVaultSecret `. – Shui shengbao Jul 18 '17 at 02:26
  • @TonyFabian If you Azure PowerShell is not the latest version. I suggest you could upgrade it and test again. Your script has no problem. – Shui shengbao Jul 18 '17 at 09:22
  • Thank you for you help Walter! I was using 2.1.0. I have updated to version 4.2.0, but the issue persists. I will update the post with the latest version of the script. You are welcome to contact me offline if you can : tony at fabian dot dk – Tony Jul 18 '17 at 11:20
  • Post updated. I have added ARM template for creation of the vault so permissions are being set but i still get the same errors. I have updated Azure PowerShell version af suggest. I have run an Update-Module also and restarted the computer. – Tony Jul 18 '17 at 12:09
  • I cannot Walter. The answer to my problem i found myself and posted here. It did help also to update PowerShell, but that was only part of the solution and it might have worked even without updating PowerShell. I think my answer is the correct answer to this problem as I never got it to work using PowerShell alone. Also ARM deployment is a much better solution when deploying azure ressources. – Tony Jul 19 '17 at 03:30
0

2022 refer to https://learn.microsoft.com/en-us/azure/key-vault/general/assign-access-policy?tabs=azure-cli

ran on Azure Cloud shell 2.43.0

az keyvault set-policy --name myKeyVault --object-id --secret-permissions --key-permissions --certificate-permissions

remove flags you don't want

anon
  • 1