Azure: add support for User-assigned Managed Identities
Facilitates means for passwordless use-cases in Azure. Similar to iam-instance-profile in AWS. Change-Id: I0de170161cef78acd8016a0f98cd5a91c1d4999e
This commit is contained in:
parent
f4941d4f03
commit
50d0fb788c
|
@ -749,6 +749,33 @@ section of the configuration.
|
|||
|
||||
If given, the size of the operating system disk, in GiB.
|
||||
|
||||
.. attr:: user-assigned-identities
|
||||
:type: dict
|
||||
:default: None
|
||||
|
||||
`User-assigned Managed Identities`_ to assign to the VM.
|
||||
Useful for giving access to services without needing any secrets.
|
||||
|
||||
.. attr:: name
|
||||
:required:
|
||||
:type: str
|
||||
|
||||
The name of the User-assigned Managed Identity.
|
||||
|
||||
.. attr:: resource-group
|
||||
:type: str
|
||||
:default: The provider's resource group
|
||||
|
||||
Overrides :attr:`providers.[azure].resource-group`.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
user-assigned-identities:
|
||||
- name: myLocalIdentity
|
||||
- name: myRemoteIdentity
|
||||
resource-group: remote-rg
|
||||
|
||||
.. _`Azure CLI`: https://docs.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest
|
||||
|
||||
|
@ -757,3 +784,5 @@ section of the configuration.
|
|||
.. _`Azure User Data`: https://docs.microsoft.com/en-us/azure/virtual-machines/user-data
|
||||
|
||||
.. _`Azure Custom Data`: https://docs.microsoft.com/en-us/azure/virtual-machines/custom-data
|
||||
|
||||
.. _`User-assigned Managed Identities`: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/qs-configure-rest-vm
|
||||
|
|
|
@ -22,6 +22,7 @@ import string
|
|||
|
||||
import cachetools.func
|
||||
|
||||
from urllib.parse import urlparse
|
||||
from nodepool.driver.utils import QuotaInformation, RateLimiter
|
||||
from nodepool.driver import statemachine
|
||||
from nodepool import exceptions
|
||||
|
@ -652,6 +653,23 @@ class AzureAdapter(statemachine.Adapter):
|
|||
if label.user_data:
|
||||
spec['properties']['userData'] = label.user_data
|
||||
|
||||
# build resource id for all configured User-assigned Identities
|
||||
uai_resource_ids = set()
|
||||
for uai in label.user_assigned_identities:
|
||||
uai_rg_name = uai.get("resource-group", self.resource_group)
|
||||
uai_url = self.azul.managed_identities.url(
|
||||
resourceGroupName=uai_rg_name, resourceName=uai['name'])
|
||||
uai_resource_ids.add(urlparse(uai_url).path)
|
||||
|
||||
# adding empty userAssignedIdentities is not allowed by Azure
|
||||
if uai_resource_ids:
|
||||
spec['identity'] = {
|
||||
'type': 'UserAssigned',
|
||||
'userAssignedIdentities': {
|
||||
rid: {} for rid in uai_resource_ids
|
||||
},
|
||||
}
|
||||
|
||||
with self.rate_limiter:
|
||||
self.log.debug(f"Creating VM {hostname}")
|
||||
return self.azul.virtual_machines.create(
|
||||
|
|
|
@ -268,6 +268,11 @@ class AzureCloud:
|
|||
providerId='Microsoft.Compute',
|
||||
resource='skus',
|
||||
apiVersion='2019-04-01')
|
||||
self.managed_identities = AzureResourceProviderCRUD(
|
||||
self,
|
||||
providerId='Microsoft.ManagedIdentity',
|
||||
resource='userAssignedIdentities',
|
||||
apiVersion='2023-01-31')
|
||||
|
||||
def get(self, url, codes=[200]):
|
||||
return self.request('GET', url, None, codes)
|
||||
|
|
|
@ -193,6 +193,8 @@ class AzureLabel(ConfigValue):
|
|||
self.custom_data = self._encodeData(label.get('custom-data', None))
|
||||
self.host_key_checking = self.pool.host_key_checking
|
||||
self.volume_size = label.get('volume-size')
|
||||
self.user_assigned_identities = label.get(
|
||||
'user-assigned-identities', [])
|
||||
|
||||
def _encodeData(self, s):
|
||||
if not s:
|
||||
|
@ -205,6 +207,11 @@ class AzureLabel(ConfigValue):
|
|||
v.Required('vm-size'): str,
|
||||
}
|
||||
|
||||
user_assigned_identities = {
|
||||
v.Required('name'): str,
|
||||
'resource-group': str,
|
||||
}
|
||||
|
||||
return {
|
||||
v.Required('name'): str,
|
||||
'cloud-image': str,
|
||||
|
@ -215,6 +222,7 @@ class AzureLabel(ConfigValue):
|
|||
'user-data': str,
|
||||
'custom-data': str,
|
||||
'volume-size': int,
|
||||
'user-assigned-identities': [user_assigned_identities],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -93,6 +93,10 @@ providers:
|
|||
dynamic-tenant: "Tenant is {{request.tenant_name}}"
|
||||
user-data: "This is the user data"
|
||||
custom-data: "This is the custom data"
|
||||
user-assigned-identities:
|
||||
- name: localid
|
||||
- name: otherid
|
||||
resource-group: othergroup
|
||||
- name: image-by-name
|
||||
cloud-image: image-by-name
|
||||
hardware-profile:
|
||||
|
|
|
@ -99,6 +99,22 @@ class TestDriverAzure(tests.DBTestCase):
|
|||
self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['properties']['userData'],
|
||||
'VGhpcyBpcyB0aGUgdXNlciBkYXRh') # This is the user data
|
||||
self.assertEqual(
|
||||
self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['identity'],
|
||||
{
|
||||
"type": "UserAssigned",
|
||||
"userAssignedIdentities": {
|
||||
f"/subscriptions/{self.fake_azure.subscription_id}/"
|
||||
"resourceGroups/nodepool/"
|
||||
"providers/Microsoft.ManagedIdentity/"
|
||||
"userAssignedIdentities/localid": {},
|
||||
f"/subscriptions/{self.fake_azure.subscription_id}/"
|
||||
"resourceGroups/othergroup/"
|
||||
"providers/Microsoft.ManagedIdentity/"
|
||||
"userAssignedIdentities/otherid": {},
|
||||
}
|
||||
})
|
||||
tags = (self.fake_azure.crud['Microsoft.Compute/virtualMachines'].
|
||||
requests[0]['tags'])
|
||||
self.assertEqual(tags.get('team'), 'DevOps')
|
||||
|
|
Loading…
Reference in New Issue