Search code examples
pythonazure-active-directoryazure-keyvault

Create an Azure Keyvault with Python


I'm trying to create a keyvault with Python by running the code from here:

import os
import json
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.keyvault import KeyVaultManagementClient
from azure.mgmt.resource.resources import ResourceManagementClient
from haikunator import Haikunator

haikunator = Haikunator()

WEST_US = "westus"
GROUP_NAME = "azure-sample-group"
KV_NAME = haikunator.haikunate()
# The object ID of the User or Application for access policies. Find this number in the portal
OBJECT_ID = "401e9294-xxxx-xxxx-xxxx-xxxx"

# Manage resources and resource groups - create, update and delete a resource group,
# deploy a solution into a resource group, export an ARM template. Create, read, update
# and delete a resource
#
# This script expects that the following environment vars are set:
#
# AZURE_TENANT_ID: with your Azure Active Directory tenant id or domain
# AZURE_CLIENT_ID: with your Azure Active Directory Application Client ID
# AZURE_CLIENT_SECRET: with your Azure Active Directory Application Secret
# AZURE_SUBSCRIPTION_ID: with your Azure Subscription Id
#


def run_example():
    """Resource Group management example."""
    #
    # Create the Resource Manager Client with an Application (service principal) token provider
    #
    subscription_id = os.environ["AZURE_SUBSCRIPTION_ID"]

    credentials = ServicePrincipalCredentials(
        client_id=os.environ["AZURE_CLIENT_ID"],
        secret=os.environ["AZURE_CLIENT_SECRET"],
        tenant=os.environ["AZURE_TENANT_ID"],
    )
    kv_client = KeyVaultManagementClient(credentials, subscription_id)
    resource_client = ResourceManagementClient(credentials, subscription_id)

    # You MIGHT need to add KeyVault as a valid provider for these credentials
    # If so, this operation has to be done only once for each credentials
    resource_client.providers.register("Microsoft.KeyVault")

    # Create Resource group
    print("\nCreate Resource Group")
    resource_group_params = {"location": WEST_US}
    print_item(
        resource_client.resource_groups.create_or_update(
            GROUP_NAME, resource_group_params
        )
    )

    # Create a vault
    print("\nCreate a vault")
    vault = kv_client.vaults.create_or_update(
        GROUP_NAME,
        KV_NAME,
        {
            "location": WEST_US,
            "properties": {
                "sku": {"name": "standard"},
                "tenant_id": os.environ["AZURE_TENANT_ID"],
                "access_policies": [
                    {
                        "tenant_id": os.environ["AZURE_TENANT_ID"],
                        "object_id": OBJECT_ID,
                        "permissions": {"keys": ["all"], "secrets": ["all"]},
                    }
                ],
            },
        },
    )
    print_item(vault)

    # List the Key vaults
    print("\nList KeyVault")
    for vault in kv_client.vaults.list():
        print_item(vault)

    # Delete Resource group and everything in it
    print("\nDelete Resource Group")
    delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME)
    delete_async_operation.wait()
    print("\nDeleted: {}".format(GROUP_NAME))


def print_item(group):
    """Print an instance."""
    print("\tName: {}".format(group.name))
    print("\tId: {}".format(group.id))
    print("\tLocation: {}".format(group.location))
    print("\tTags: {}".format(group.tags))


if __name__ == "__main__":
    run_example()


After receiving comments, I assigned the Contributerole to the service principal.

My only modification to the code was substituting the object id with the one from my service principal (see image below):

enter image description here

I'm receiving following error: enter image description here

EDIT: Accepted @sridev's answer because I solved the issue by running the sample code which was linked in the comments

Here's my working code, based on this GitHub repo, assuming that the Azure environment variables are set properly:

import os

from azure.identity import DefaultAzureCredential
from azure.mgmt.keyvault import KeyVaultManagementClient
from azure.mgmt.resource import ResourceManagementClient


def main():
    TENANT_ID = os.environ.get("AZURE_TENANT_ID", None)
    SUBSCRIPTION_ID = os.environ.get("AZURE_SUBSCRIPTION_ID", None)
    GROUP_NAME = "resource_group_name"
    VAULT = "vault_name"
    LOCATION = "azure_location_eg_westus"
    OBJECT_ID = "service_principal_object_id"

    # Create client
    # Other authentication approaches: https://pypi.org/project/azure-identity/
    resource_client = ResourceManagementClient(
        credential=DefaultAzureCredential(), subscription_id=SUBSCRIPTION_ID
    )
    keyvault_client = KeyVaultManagementClient(
        credential=DefaultAzureCredential(), subscription_id=SUBSCRIPTION_ID
    )

    # Create resource group
    resource_client.resource_groups.create_or_update(GROUP_NAME, {"location": LOCATION})

    # Create vault
    vault = keyvault_client.vaults.begin_create_or_update(
        GROUP_NAME,
        VAULT,
        {
            "location": LOCATION,
            "properties": {
                "tenant_id": TENANT_ID,
                "sku": {"family": "A", "name": "standard"},
                "access_policies": [
                    {
                        "tenant_id": TENANT_ID,
                        "object_id": OBJECT_ID,
                        "permissions": {
                            "keys": [
                                "encrypt",
                                "decrypt",
                                "wrapKey",
                                "unwrapKey",
                                "sign",
                                "verify",
                                "get",
                                "list",
                                "create",
                                "update",
                                "import",
                                "delete",
                                "backup",
                                "restore",
                                "recover",
                                "purge",
                            ],
                            "secrets": [
                                "get",
                                "list",
                                "set",
                                "delete",
                                "backup",
                                "restore",
                                "recover",
                                "purge",
                            ],
                        },
                    }
                ],
                "enabled_for_deployment": True,
                "enabled_for_disk_encryption": True,
                "enabled_for_template_deployment": True,
            },
        },
    ).result()
    print("Create vault:\n{}".format(vault))

    # Get vault
    vault = keyvault_client.vaults.get(GROUP_NAME, VAULT)
    print("Get vault:\n{}".format(vault))

    # Update vault
    vault = keyvault_client.vaults.update(
        GROUP_NAME, VAULT, {"tags": {"category": "Marketing"}}
    )
    print("Update vault:\n{}".format(vault))

    # Delete vault
    keyvault_client.vaults.delete(GROUP_NAME, VAULT)
    print("Delete vault.\n")

    # Purge a deleted vault
    keyvault_client.vaults.begin_purge_deleted(VAULT, LOCATION).result()
    print("Purge a deleted vault.\n")

    # Delete Group
    resource_client.resource_groups.begin_delete(GROUP_NAME).result()


if __name__ == "__main__":
    main()


Solution

  • The error usually occurs if your service principal don't have sufficient permissions or roles to perform the operation.

    I tried to reproduce the same in my environment and got below results:

    I registered one web application named SriApp in my Azure AD tenant like below:

    enter image description here

    When I ran the below code without assigning any RBAC role to above servie principal, I got same error as below:

    import os
    import json
    from azure.common.credentials import ServicePrincipalCredentials
    from azure.mgmt.keyvault import KeyVaultManagementClient
    from azure.mgmt.resource.resources import ResourceManagementClient
    from haikunator import Haikunator
    
    haikunator = Haikunator()
    
    WEST_US = 'Germany West Central'
    GROUP_NAME = 'azure-sample-resources'
    KV_NAME = haikunator.haikunate()
    # The object ID of the User or Application for access policies. Find this number in the portal
    OBJECT_ID = 'bfc7171d-8c25-4b41-9764-xxxxxxxxxxx'
    
    def run_example():
        """Resource Group management example."""
        #
        # Create the Resource Manager Client with an Application (service principal) token provider
        #
        subscription_id = '124c97c3-58a7-4eb5-ac50-xxxxxxxxxx'
    
        credentials = ServicePrincipalCredentials(
            client_id='794cb163-ca1c-4fec-b7df-xxxxxxxxxxxx',
            secret='xxxxxxxxxxxxxxxxxxxxxx',
            tenant='6c3f1c39-b84c-4188-b49f-xxxxxxxxx'
        )
        kv_client = KeyVaultManagementClient(credentials, subscription_id)
        resource_client = ResourceManagementClient(credentials, subscription_id)
    
        # You MIGHT need to add KeyVault as a valid provider for these credentials
        # If so, this operation has to be done only once for each credentials
        resource_client.providers.register('Microsoft.KeyVault')
    
        # Create Resource group
        print('\nCreate Resource Group')
        resource_group_params = {'location': WEST_US}
        print_item(resource_client.resource_groups.create_or_update(
            GROUP_NAME, resource_group_params))
    
        # Create a vault
        print('\nCreate a vault')
        vault = kv_client.vaults.create_or_update(
            GROUP_NAME,
            KV_NAME,
            {
                'location': WEST_US,
                'properties': {
                    'sku': {
                        'name': 'standard'
                    },
                    'tenant_id': '6c3f1c39-b84c-4188-b49f-xxxxxxxxx',
                    'access_policies': [{
                        'tenant_id': '6c3f1c39-b84c-4188-b49f-xxxxxxxx',
                        'object_id': OBJECT_ID,
                        'permissions': {
                            'keys': ['all'],
                            'secrets': ['all']
                        }
                    }]
                }
            }
        )
        print_item(vault)
    
        # List the Key vaults
        print('\nList KeyVault')
        for vault in kv_client.vaults.list():
            print_item(vault)
    
        # Delete Resource group and everything in it
        # print('\nDelete Resource Group')
        # delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME)
        # delete_async_operation.wait()
        # print("\nDeleted: {}".format(GROUP_NAME))
    
    
    def print_item(group):
        """Print an instance."""
        print("\tName: {}".format(group.name))
        print("\tId: {}".format(group.id))
        print("\tLocation: {}".format(group.location))
        print("\tTags: {}".format(group.tags))
    
    
    if __name__ == "__main__":
        run_example()
    

    Response

    enter image description here

    To resolve the error, you need to assign Contributor role to the service principal under your subscription like below:

    Go to Azure Portal -> Your Subscription -> Access control (IAM) -> Add role assignment -> Contributor

    enter image description here

    After assigning the role, I ran the code again and got response successfully like below:

    import os
    import json
    from azure.common.credentials import ServicePrincipalCredentials
    from azure.mgmt.keyvault import KeyVaultManagementClient
    from azure.mgmt.resource.resources import ResourceManagementClient
    from haikunator import Haikunator
    
    haikunator = Haikunator()
    
    WEST_US = 'Germany West Central'
    GROUP_NAME = 'azure-sample-resources'
    KV_NAME = haikunator.haikunate()
    # The object ID of the User or Application for access policies. Find this number in the portal
    OBJECT_ID = 'bfc7171d-8c25-4b41-9764-xxxxxxxxxxx'
    
    def run_example():
        """Resource Group management example."""
        #
        # Create the Resource Manager Client with an Application (service principal) token provider
        #
        subscription_id = '124c97c3-58a7-4eb5-ac50-xxxxxxxxxx'
    
        credentials = ServicePrincipalCredentials(
            client_id='794cb163-ca1c-4fec-b7df-xxxxxxxxxxxx',
            secret='xxxxxxxxxxxxxxxxxxxxxx',
            tenant='6c3f1c39-b84c-4188-b49f-xxxxxxxxx'
        )
        kv_client = KeyVaultManagementClient(credentials, subscription_id)
        resource_client = ResourceManagementClient(credentials, subscription_id)
    
        # You MIGHT need to add KeyVault as a valid provider for these credentials
        # If so, this operation has to be done only once for each credentials
        resource_client.providers.register('Microsoft.KeyVault')
    
        # Create Resource group
        print('\nCreate Resource Group')
        resource_group_params = {'location': WEST_US}
        print_item(resource_client.resource_groups.create_or_update(
            GROUP_NAME, resource_group_params))
    
        # Create a vault
        print('\nCreate a vault')
        vault = kv_client.vaults.create_or_update(
            GROUP_NAME,
            KV_NAME,
            {
                'location': WEST_US,
                'properties': {
                    'sku': {
                        'name': 'standard'
                    },
                    'tenant_id': '6c3f1c39-b84c-4188-b49f-xxxxxxxxx',
                    'access_policies': [{
                        'tenant_id': '6c3f1c39-b84c-4188-b49f-xxxxxxxx',
                        'object_id': OBJECT_ID,
                        'permissions': {
                            'keys': ['all'],
                            'secrets': ['all']
                        }
                    }]
                }
            }
        )
        print_item(vault)
    
        # List the Key vaults
        print('\nList KeyVault')
        for vault in kv_client.vaults.list():
            print_item(vault)
    
        # Delete Resource group and everything in it
        # print('\nDelete Resource Group')
        # delete_async_operation = resource_client.resource_groups.delete(GROUP_NAME)
        # delete_async_operation.wait()
        # print("\nDeleted: {}".format(GROUP_NAME))
    
    
    def print_item(group):
        """Print an instance."""
        print("\tName: {}".format(group.name))
        print("\tId: {}".format(group.id))
        print("\tLocation: {}".format(group.location))
        print("\tTags: {}".format(group.tags))
    
    
    if __name__ == "__main__":
        run_example()
    

    Response:

    enter image description here

    To confirm that, I checked the same in Portal where key vault created successfully under new resource group with Location as Germany West Central like below:

    enter image description here

    When I checked Access policies of this key vault, it has application added to it with permissions as below:

    enter image description here