Skip to content

Commit

Permalink
Managed Identity support for containerinstance module (ansible-collec…
Browse files Browse the repository at this point in the history
  • Loading branch information
p3ck authored and Justwmz committed Nov 4, 2024
1 parent 1ccc535 commit 28831a6
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 4 deletions.
92 changes: 89 additions & 3 deletions plugins/modules/azure_rm_containerinstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@
description:
- The password to log in container image registry server.
type: str
acr_identity:
description:
- The Identity to use for access to the registry server.
type: str
version_added: '2.5.0'
containers:
description:
- List of containers.
Expand Down Expand Up @@ -234,6 +239,36 @@
description:
- Commit hash for the specified revision
type: str
identity:
description:
- Identity for the Server.
type: dict
version_added: '2.5.0'
suboptions:
type:
description:
- Type of the managed identity
required: false
choices:
- SystemAssigned
- SystemAssigned, UserAssigned
- UserAssigned
default: None
type: str
user_assigned_identities:
description:
- User Assigned Managed Identities and its options
required: false
type: dict
default: {}
suboptions:
id:
description:
- List of the user assigned identities IDs associated to the VM
required: false
type: list
elements: str
default: []
force_update:
description:
- Force update of existing container instance. Any update will result in deletion and recreation of existing containers.
Expand Down Expand Up @@ -421,12 +456,15 @@
}
'''

from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt
from ansible.module_utils.common.dict_transformations import _snake_to_camel

try:
from azure.core.exceptions import ResourceNotFoundError
from azure.core.polling import LROPoller
from azure.mgmt.containerinstance.models import (
ContainerGroupIdentity,
)
except ImportError:
# This is handled in azure_rm_common
pass
Expand Down Expand Up @@ -520,7 +558,31 @@ def create_container_dict_from_obj(container):
)


class AzureRMContainerInstance(AzureRMModuleBase):
user_assigned_identities_spec = dict(
id=dict(
type='list',
default=[],
elements='str'
),
)

managed_identity_spec = dict(
type=dict(
type='str',
choices=['SystemAssigned',
'UserAssigned',
'SystemAssigned, UserAssigned'],
default='None'
),
user_assigned_identities=dict(
type='dict',
options=user_assigned_identities_spec,
default={}
),
)


class AzureRMContainerInstance(AzureRMModuleBaseExt):
"""Configuration class for an Azure RM container instance resource"""

def __init__(self):
Expand Down Expand Up @@ -572,6 +634,10 @@ def __init__(self):
default=None,
no_log=True
),
acr_identity=dict(
type='str',
default=None,
),
containers=dict(
type='list',
elements='dict',
Expand All @@ -594,6 +660,10 @@ def __init__(self):
type='list',
elements='str',
),
identity=dict(
type='dict',
options=managed_identity_spec
),
)

self.resource_group = None
Expand All @@ -605,11 +675,13 @@ def __init__(self):
self.containers = None
self.restart_policy = None
self.subnet_ids = None
self.identity = None

self.tags = None

self.results = dict(changed=False, state=dict())
self.cgmodels = None
self._managed_identity = None

required_if = [
('state', 'present', ['containers']), ('ip_address', 'private', ['subnet_ids'])
Expand All @@ -620,6 +692,14 @@ def __init__(self):
supports_tags=True,
required_if=required_if)

@property
def managed_identity(self):
if not self._managed_identity:
self._managed_identity = {"identity": ContainerGroupIdentity,
"user_assigned": dict
}
return self._managed_identity

def exec_module(self, **kwargs):
"""Main module execution method"""

Expand All @@ -640,6 +720,10 @@ def exec_module(self, **kwargs):

response = self.get_containerinstance()

# Format identities
if self.identity:
update_identity, self.identity = self.update_identities({})

if not response:
self.log("Container Group doesn't exist")

Expand Down Expand Up @@ -717,7 +801,8 @@ def create_update_containerinstance(self):
if self.registry_login_server is not None:
registry_credentials = [self.cgmodels.ImageRegistryCredential(server=self.registry_login_server,
username=self.registry_username,
password=self.registry_password)]
password=self.registry_password,
identity=self.acr_identity)]

ip_address = None

Expand Down Expand Up @@ -776,6 +861,7 @@ def create_update_containerinstance(self):
subnet_ids = [self.cgmodels.ContainerGroupSubnetId(id=item) for item in self.subnet_ids]

parameters = self.cgmodels.ContainerGroup(location=self.location,
identity=self.identity,
containers=containers,
image_registry_credentials=registry_credentials,
restart_policy=_snake_to_camel(self.restart_policy, True) if self.restart_policy else None,
Expand Down
27 changes: 26 additions & 1 deletion plugins/modules/azure_rm_containerinstance_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,30 @@
"name": "myvolume1"
}
]
identity:
description:
- Identity for the Server.
type: complex
returned: when available
contains:
type:
description:
- Type of the managed identity
returned: always
sample: UserAssigned
type: str
user_assigned_identities:
description:
- User Assigned Managed Identities and its options
returned: always
type: complex
contains:
id:
description:
- Dict of the user assigned identities IDs associated to the Resource
returned: always
type: dict
elements: dict
tags:
description: Tags assigned to the resource. Dictionary of string:string pairs.
type: dict
Expand Down Expand Up @@ -346,7 +370,8 @@ def format_item(self, item):
'restart_policy': _camel_to_snake(d.get('restart_policy')) if d.get('restart_policy') else None,
'tags': d.get('tags', None),
'subnet_ids': d.get('subnet_ids', None),
'volumes': d['volumes'] if 'volumes' in d else []
'volumes': d['volumes'] if 'volumes' in d else [],
'identity': d.get('identity', None)
}
return d

Expand Down
112 changes: 112 additions & 0 deletions tests/integration/targets/azure_rm_containerinstance/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@
ansible.builtin.set_fact:
rpfx: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}"

- name: Gather Resource Group info
azure.azcollection.azure_rm_resourcegroup_info:
name: "{{ resource_group }}"
register: __rg_info

- name: Set Azure Region based on resource group location
ansible.builtin.set_fact:
location: "{{ __rg_info.resourcegroups.0.location }}"

- name: Create User Managed Identities
azure_rm_resource:
resource_group: "{{ resource_group }}"
provider: ManagedIdentity
resource_type: userAssignedIdentities
resource_name: "{{ item }}"
api_version: "2023-01-31"
body:
location: "{{ location }}"
state: present
loop:
- "ansible-test-aci-identity"
- "ansible-test-aci-identity-2"

- name: Set identities IDs to test. Identities ansible-test-aci-identity and ansible-test-aci-identity-2 have to be created previously
ansible.builtin.set_fact:
user_identity_1: "/subscriptions/{{ azure_subscription_id }}/resourcegroups/{{ resource_group }}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ansible-test-aci-identity"
user_identity_2: "/subscriptions/{{ azure_subscription_id }}/resourcegroups/{{ resource_group }}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/ansible-test-aci-identity-2"

- name: Create sample container instance
azure_rm_containerinstance:
resource_group: "{{ resource_group }}"
Expand Down Expand Up @@ -69,9 +97,15 @@
ports:
- 80
- 81
identity:
type: SystemAssigned
force_update: true
register: output

- name: Print the created results
ansible.builtin.debug:
var: output

- name: Assert the container instance is well created
ansible.builtin.assert:
that:
Expand Down Expand Up @@ -195,6 +229,72 @@
- output.containerinstances[1]['ports'] != None
- output.containerinstances[1]['containers'] != None

- name: Create sample container instance -- User Identity
azure_rm_containerinstance:
resource_group: "{{ resource_group }}"
name: "aci{{ rpfx }}fourth"
os_type: linux
ip_address: public
location: eastus
ports:
- 80
containers:
- name: mycontainer1
image: httpd
memory: 1.5
ports:
- 80
- 81
identity:
type: UserAssigned
user_assigned_identities:
id:
- "{{ user_identity_1 }}"
- "{{ user_identity_2 }}"
register: output

- name: Gather facts for single Container Instance
azure_rm_containerinstance_info:
resource_group: "{{ resource_group }}"
name: "aci{{ rpfx }}fourth"
register: output

- name: Print the container instance facts
ansible.builtin.debug:
var: output

- name: Assert that user identities are used
ansible.builtin.assert:
that:
- output.changed == False
- user_identity_1 in output.containerinstances[0].identity.user_assigned_identities
- user_identity_2 in output.containerinstances[0].identity.user_assigned_identities

- name: Remove container instance -- User Identity
azure_rm_containerinstance:
resource_group: "{{ resource_group }}"
name: "aci{{ rpfx }}fourth"
os_type: linux
ip_address: public
location: eastus
ports:
- 80
containers:
- name: mycontainer1
image: httpd
memory: 1.5
ports:
- 80
- 81
identity:
type: UserAssigned
user_assigned_identities:
id:
- "{{ user_identity_1 }}"
- "{{ user_identity_2 }}"
state: absent
register: output

- name: Create sample container instance with volume
azure_rm_containerinstance:
resource_group: "{{ resource_group }}"
Expand Down Expand Up @@ -362,3 +462,15 @@
ansible.builtin.assert:
that:
- output.changed == False

- name: Destroy User Managed Identities
azure_rm_resource:
resource_group: "{{ resource_group }}"
provider: ManagedIdentity
resource_type: userAssignedIdentities
resource_name: "{{ item }}"
api_version: "2023-01-31"
state: absent
loop:
- "ansible-test-aci-identity"
- "ansible-test-aci-identity-2"

0 comments on commit 28831a6

Please sign in to comment.