Skip to content

Commit a89e7ad

Browse files
committed
Support blob storage token store authenticate with MSI
1 parent ad5bcba commit a89e7ad

File tree

7 files changed

+3399
-5
lines changed

7 files changed

+3399
-5
lines changed

src/containerapp/HISTORY.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Release History
44
===============
55
upcoming
66
++++++
7+
* 'az containerapp auth update': Support authenticating blob storage token store using managed identity with `--blob-container-uri` and `--blob-container-identity`.
78

89
1.1.0b4
910
++++++

src/containerapp/azext_containerapp/_help.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,12 @@
715715
- name: Configure the app to listen to the forward headers X-FORWARDED-HOST and X-FORWARDED-PROTO.
716716
text: |
717717
az containerapp auth update -g myResourceGroup --name my-containerapp --proxy-convention Standard
718+
- name: Configure the blob storage token store using system assigned managed identity to authenticate.
719+
text: |
720+
az containerapp auth update -g myResourceGroup --name my-containerapp --token-store true --blob-container-uri https://storageAccount1.blob.core.windows.net/container1
721+
- name: Configure the blob storage token store using user assigned managed identity to authenticate.
722+
text: |
723+
az containerapp auth update -g myResourceGroup --name my-containerapp --token-store true --blob-container-uri https://storageAccount1.blob.core.windows.net/container1 --blob-container-identity managedIdentityResourceId
718724
"""
719725

720726
helps['containerapp env workload-profile set'] = """

src/containerapp/azext_containerapp/_params.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,11 @@ def load_arguments(self, _):
276276
c.argument('user_assigned', nargs='+', help="Space-separated user identities to be assigned.")
277277
c.argument('system_assigned', help="Boolean indicating whether to assign system-assigned identity.", action='store_true')
278278

279+
with self.argument_context('containerapp auth') as c:
280+
c.argument('blob_container_uri', help='The URI of the blob storage containing the tokens. Should not be used along with sas_url_secret and sas_url_secret_name.', is_preview=True)
281+
c.argument('blob_container_identity', options_list=['--blob-container-identity', '--bci'],
282+
help='Resource ID of a managed identity to authenticate with Azure blob storage, or Empty to use a system-assigned identity.', is_preview=True)
283+
279284
with self.argument_context('containerapp env workload-profile set') as c:
280285
c.argument('workload_profile_type', help="The type of workload profile to add or update. Run `az containerapp env workload-profile list-supported -l <region>` to check the options for your region.")
281286
c.argument('min_nodes', help="The minimum node count for the workload profile")

src/containerapp/azext_containerapp/containerapp_auth_decorator.py

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,110 @@
33
# Licensed under the MIT License. See License.txt in the project root for license information.
44
# --------------------------------------------------------------------------------------------
55
# pylint: disable=line-too-long, useless-parent-delegation
6-
6+
from azure.cli.core.azclierror import ArgumentUsageError
7+
from azure.cli.core.commands.client_factory import get_subscription_id
78
from azure.cli.command_modules.containerapp.containerapp_auth_decorator import ContainerAppAuthDecorator
8-
9+
from azure.cli.command_modules.containerapp._utils import safe_set, set_field_in_auth_settings, update_http_settings_in_auth_settings, _ensure_identity_resource_id
10+
from azure.cli.command_modules.containerapp._constants import BLOB_STORAGE_TOKEN_STORE_SECRET_SETTING_NAME
911

1012
# decorator for preview auth show/update
1113
class ContainerAppPreviewAuthDecorator(ContainerAppAuthDecorator):
14+
15+
def parent_construct_payload(self):
16+
self.existing_auth = {}
17+
try:
18+
self.existing_auth = self.client.get(cmd=self.cmd, resource_group_name=self.get_argument_resource_group_name(), container_app_name=self.get_argument_name(), auth_config_name="current")["properties"]
19+
except:
20+
self.existing_auth["platform"] = {}
21+
self.existing_auth["platform"]["enabled"] = True
22+
self.existing_auth["globalValidation"] = {}
23+
self.existing_auth["login"] = {}
24+
25+
self.existing_auth = set_field_in_auth_settings(self.existing_auth, self.get_argument_set_string())
26+
27+
if self.get_argument_enabled() is not None:
28+
if "platform" not in self.existing_auth:
29+
self.existing_auth["platform"] = {}
30+
self.existing_auth["platform"]["enabled"] = self.get_argument_enabled()
31+
32+
if self.get_argument_runtime_version() is not None:
33+
if "platform" not in self.existing_auth:
34+
self.existing_auth["platform"] = {}
35+
self.existing_auth["platform"]["runtimeVersion"] = self.get_argument_runtime_version()
36+
37+
if self.get_argument_config_file_path() is not None:
38+
if "platform" not in self.existing_auth:
39+
self.existing_auth["platform"] = {}
40+
self.existing_auth["platform"]["configFilePath"] = self.get_argument_config_file_path()
41+
42+
if self.get_argument_unauthenticated_client_action() is not None:
43+
if "globalValidation" not in self.existing_auth:
44+
self.existing_auth["globalValidation"] = {}
45+
self.existing_auth["globalValidation"]["unauthenticatedClientAction"] = self.get_argument_unauthenticated_client_action()
46+
47+
if self.get_argument_redirect_provider() is not None:
48+
if "globalValidation" not in self.existing_auth:
49+
self.existing_auth["globalValidation"] = {}
50+
self.existing_auth["globalValidation"]["redirectToProvider"] = self.get_argument_redirect_provider()
51+
52+
if self.get_argument_excluded_paths() is not None:
53+
if "globalValidation" not in self.existing_auth:
54+
self.existing_auth["globalValidation"] = {}
55+
self.existing_auth["globalValidation"]["excludedPaths"] = self.get_argument_excluded_paths().split(",")
56+
57+
self.existing_auth = update_http_settings_in_auth_settings(self.existing_auth, self.get_argument_require_https(),
58+
self.get_argument_proxy_convention(), self.get_argument_proxy_custom_host_header(),
59+
self.get_argument_proxy_custom_proto_header())
60+
1261
def construct_payload(self):
13-
super().construct_payload()
62+
self.parent_construct_payload()
63+
self.set_up_token_store()
64+
65+
def set_up_token_store(self):
66+
if self.get_argument_token_store() is None:
67+
return
68+
69+
if self.get_argument_token_store() is False:
70+
safe_set(self.existing_auth, "login", "tokenStore", "enabled", value=False)
71+
return
72+
73+
safe_set(self.existing_auth, "login", "tokenStore", "enabled", value=True)
74+
safe_set(self.existing_auth, "login", "tokenStore", "azureBlobStorage", value={})
75+
76+
param_provided = 0
77+
if self.get_argument_sas_url_secret() is not None:
78+
param_provided += 1
79+
if self.get_argument_sas_url_secret_name() is not None:
80+
param_provided += 1
81+
if self.get_argument_blob_container_uri() is not None:
82+
param_provided += 1
83+
84+
if param_provided != 1:
85+
raise ArgumentUsageError(
86+
'Usage Error: only blob storage token store is supported. --sas-url-secret, --sas-url-secret-name and --blob-container-uri should provide exactly one when token store is enabled')
87+
88+
if self.get_argument_blob_container_uri() is not None:
89+
safe_set(self.existing_auth, "login", "tokenStore", "azureBlobStorage", "blobContainerUri",
90+
value=self.get_argument_blob_container_uri())
91+
92+
identity = self.get_argument_blob_container_identity()
93+
if identity is None:
94+
identity = ""
95+
identity = identity.lower()
96+
if identity != "":
97+
subscription_id = get_subscription_id(self.cmd.cli_ctx)
98+
identity = _ensure_identity_resource_id(subscription_id, self.get_argument_resource_group_name(), identity)
99+
safe_set(self.existing_auth, "login", "tokenStore", "azureBlobStorage", "managedIdentityResourceId",
100+
value=identity)
101+
return
102+
103+
sas_url_setting_name = BLOB_STORAGE_TOKEN_STORE_SECRET_SETTING_NAME
104+
if self.get_argument_sas_url_secret_name() is not None:
105+
sas_url_setting_name = self.get_argument_sas_url_secret_name()
106+
safe_set(self.existing_auth, "login", "tokenStore", "azureBlobStorage", "sasUrlSettingName", value=sas_url_setting_name)
107+
108+
def get_argument_blob_container_uri(self):
109+
return self.get_param("blob_container_uri")
110+
111+
def get_argument_blob_container_identity(self):
112+
return self.get_param("blob_container_identity")

src/containerapp/azext_containerapp/custom.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,7 @@ def update_auth_config(cmd, resource_group_name, name, set_string=None, enabled=
14011401
proxy_convention=None, proxy_custom_host_header=None,
14021402
proxy_custom_proto_header=None, excluded_paths=None,
14031403
token_store=None, sas_url_secret=None, sas_url_secret_name=None,
1404+
blob_container_uri=None, blob_container_identity=None,
14041405
yes=False):
14051406
raw_parameters = locals()
14061407
containerapp_auth_decorator = ContainerAppPreviewAuthDecorator(

0 commit comments

Comments
 (0)