apache-airflow-providers-microsoft-azure 8.1.0rc1__tar.gz → 8.2.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {apache-airflow-providers-microsoft-azure-8.1.0rc1/apache_airflow_providers_microsoft_azure.egg-info → apache-airflow-providers-microsoft-azure-8.2.0rc1}/PKG-INFO +7 -9
  2. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/README.rst +4 -6
  3. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/__init__.py +1 -1
  4. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/get_provider_info.py +1 -0
  5. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/adx.py +12 -2
  6. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/asb.py +31 -6
  7. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/base_azure.py +11 -2
  8. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/batch.py +20 -10
  9. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/container_instance.py +7 -1
  10. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/container_registry.py +14 -3
  11. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/container_volume.py +25 -17
  12. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/cosmos.py +15 -4
  13. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/data_factory.py +18 -2
  14. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/data_lake.py +30 -14
  15. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/fileshare.py +37 -21
  16. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/synapse.py +17 -3
  17. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/wasb.py +22 -4
  18. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/container_instances.py +5 -6
  19. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/secrets/key_vault.py +22 -3
  20. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/utils.py +80 -4
  21. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1/apache_airflow_providers_microsoft_azure.egg-info}/PKG-INFO +7 -9
  22. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/pyproject.toml +4 -6
  23. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/setup.cfg +2 -2
  24. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/setup.py +1 -1
  25. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/LICENSE +0 -0
  26. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/MANIFEST.in +0 -0
  27. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/NOTICE +0 -0
  28. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/fs/__init__.py +0 -0
  29. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/fs/adls.py +0 -0
  30. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/hooks/__init__.py +0 -0
  31. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/log/__init__.py +0 -0
  32. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/log/wasb_task_handler.py +0 -0
  33. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/__init__.py +0 -0
  34. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/adls.py +0 -0
  35. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/adx.py +0 -0
  36. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/asb.py +0 -0
  37. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/batch.py +0 -0
  38. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/cosmos.py +0 -0
  39. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/data_factory.py +0 -0
  40. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/synapse.py +0 -0
  41. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/operators/wasb_delete_blob.py +0 -0
  42. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/secrets/__init__.py +0 -0
  43. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/sensors/__init__.py +0 -0
  44. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/sensors/cosmos.py +0 -0
  45. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/sensors/data_factory.py +0 -0
  46. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/sensors/wasb.py +0 -0
  47. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/__init__.py +0 -0
  48. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/azure_blob_to_gcs.py +0 -0
  49. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/local_to_adls.py +0 -0
  50. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/local_to_wasb.py +0 -0
  51. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/oracle_to_azure_data_lake.py +0 -0
  52. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/transfers/sftp_to_wasb.py +0 -0
  53. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/triggers/__init__.py +0 -0
  54. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/triggers/data_factory.py +0 -0
  55. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/airflow/providers/microsoft/azure/triggers/wasb.py +0 -0
  56. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/SOURCES.txt +0 -0
  57. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/dependency_links.txt +0 -0
  58. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/entry_points.txt +0 -0
  59. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/not-zip-safe +0 -0
  60. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/requires.txt +0 -0
  61. {apache-airflow-providers-microsoft-azure-8.1.0rc1 → apache-airflow-providers-microsoft-azure-8.2.0rc1}/apache_airflow_providers_microsoft_azure.egg-info/top_level.txt +0 -0
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apache-airflow-providers-microsoft-azure
3
- Version: 8.1.0rc1
3
+ Version: 8.2.0rc1
4
4
  Summary: Provider for Apache Airflow. Implements apache-airflow-providers-microsoft-azure package
5
5
  Home-page: https://airflow.apache.org/
6
6
  Download-URL: https://archive.apache.org/dist/airflow/providers
7
7
  Author: Apache Software Foundation
8
8
  Author-email: dev@airflow.apache.org
9
9
  License: Apache License 2.0
10
- Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/
11
- Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/changelog.html
10
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/
11
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/changelog.html
12
12
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
13
13
  Project-URL: Source Code, https://github.com/apache/airflow
14
14
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
@@ -52,8 +52,7 @@ License-File: NOTICE
52
52
  KIND, either express or implied. See the License for the
53
53
  specific language governing permissions and limitations
54
54
  under the License.
55
-
56
- .. Licensed to the Apache Software Foundation (ASF) under one
55
+ .. Licensed to the Apache Software Foundation (ASF) under one
57
56
  or more contributor license agreements. See the NOTICE file
58
57
  distributed with this work for additional information
59
58
  regarding copyright ownership. The ASF licenses this file
@@ -73,7 +72,7 @@ License-File: NOTICE
73
72
 
74
73
  Package ``apache-airflow-providers-microsoft-azure``
75
74
 
76
- Release: ``8.1.0rc1``
75
+ Release: ``8.2.0rc1``
77
76
 
78
77
 
79
78
  `Microsoft Azure <https://azure.microsoft.com/>`__
@@ -86,8 +85,7 @@ This is a provider package for ``microsoft.azure`` provider. All classes for thi
86
85
  are in ``airflow.providers.microsoft.azure`` python package.
87
86
 
88
87
  You can find package information and changelog for the provider
89
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/>`_.
90
-
88
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/>`_.
91
89
 
92
90
  Installation
93
91
  ------------
@@ -149,4 +147,4 @@ Dependent package
149
147
  ==================================================================================================== ==========
150
148
 
151
149
  The changelog for the provider package can be found in the
152
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/changelog.html>`_.
150
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/changelog.html>`_.
@@ -15,8 +15,7 @@
15
15
  KIND, either express or implied. See the License for the
16
16
  specific language governing permissions and limitations
17
17
  under the License.
18
-
19
- .. Licensed to the Apache Software Foundation (ASF) under one
18
+ .. Licensed to the Apache Software Foundation (ASF) under one
20
19
  or more contributor license agreements. See the NOTICE file
21
20
  distributed with this work for additional information
22
21
  regarding copyright ownership. The ASF licenses this file
@@ -36,7 +35,7 @@
36
35
 
37
36
  Package ``apache-airflow-providers-microsoft-azure``
38
37
 
39
- Release: ``8.1.0rc1``
38
+ Release: ``8.2.0rc1``
40
39
 
41
40
 
42
41
  `Microsoft Azure <https://azure.microsoft.com/>`__
@@ -49,8 +48,7 @@ This is a provider package for ``microsoft.azure`` provider. All classes for thi
49
48
  are in ``airflow.providers.microsoft.azure`` python package.
50
49
 
51
50
  You can find package information and changelog for the provider
52
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/>`_.
53
-
51
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/>`_.
54
52
 
55
53
  Installation
56
54
  ------------
@@ -112,4 +110,4 @@ Dependent package
112
110
  ==================================================================================================== ==========
113
111
 
114
112
  The changelog for the provider package can be found in the
115
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.1.0/changelog.html>`_.
113
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-microsoft-azure/8.2.0/changelog.html>`_.
@@ -28,7 +28,7 @@ import packaging.version
28
28
 
29
29
  __all__ = ["__version__"]
30
30
 
31
- __version__ = "8.1.0"
31
+ __version__ = "8.2.0"
32
32
 
33
33
  try:
34
34
  from airflow import __version__ as airflow_version
@@ -29,6 +29,7 @@ def get_provider_info():
29
29
  "description": "`Microsoft Azure <https://azure.microsoft.com/>`__\n",
30
30
  "suspended": False,
31
31
  "versions": [
32
+ "8.2.0",
32
33
  "8.1.0",
33
34
  "8.0.0",
34
35
  "7.0.0",
@@ -29,12 +29,15 @@ import warnings
29
29
  from functools import cached_property
30
30
  from typing import TYPE_CHECKING, Any
31
31
 
32
- from azure.identity import DefaultAzureCredential
33
32
  from azure.kusto.data import ClientRequestProperties, KustoClient, KustoConnectionStringBuilder
34
33
  from azure.kusto.data.exceptions import KustoServiceError
35
34
 
36
35
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
37
36
  from airflow.hooks.base import BaseHook
37
+ from airflow.providers.microsoft.azure.utils import (
38
+ add_managed_identity_connection_widgets,
39
+ get_sync_default_azure_credential,
40
+ )
38
41
 
39
42
  if TYPE_CHECKING:
40
43
  from azure.kusto.data.response import KustoResponseDataSetV2
@@ -80,6 +83,7 @@ class AzureDataExplorerHook(BaseHook):
80
83
  hook_name = "Azure Data Explorer"
81
84
 
82
85
  @classmethod
86
+ @add_managed_identity_connection_widgets
83
87
  def get_connection_form_widgets(cls) -> dict[str, Any]:
84
88
  """Returns connection widgets to add to connection form."""
85
89
  from flask_appbuilder.fieldwidgets import BS3PasswordFieldWidget, BS3TextFieldWidget
@@ -192,9 +196,15 @@ class AzureDataExplorerHook(BaseHook):
192
196
  elif auth_method == "AAD_DEVICE":
193
197
  kcsb = KustoConnectionStringBuilder.with_aad_device_authentication(cluster)
194
198
  elif auth_method == "AZURE_TOKEN_CRED":
199
+ managed_identity_client_id = conn.extra_dejson.get("managed_identity_client_id")
200
+ workload_identity_tenant_id = conn.extra_dejson.get("workload_identity_tenant_id")
201
+ credential = get_sync_default_azure_credential(
202
+ managed_identity_client_id=managed_identity_client_id,
203
+ workload_identity_tenant_id=workload_identity_tenant_id,
204
+ )
195
205
  kcsb = KustoConnectionStringBuilder.with_azure_token_credential(
196
206
  connection_string=cluster,
197
- credential=DefaultAzureCredential(),
207
+ credential=credential,
198
208
  )
199
209
  else:
200
210
  raise AirflowException(f"Unknown authentication method: {auth_method}")
@@ -16,14 +16,20 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
- from typing import Any
19
+ from typing import TYPE_CHECKING, Any
20
20
 
21
- from azure.identity import DefaultAzureCredential
22
21
  from azure.servicebus import ServiceBusClient, ServiceBusMessage, ServiceBusSender
23
22
  from azure.servicebus.management import QueueProperties, ServiceBusAdministrationClient
24
23
 
25
24
  from airflow.hooks.base import BaseHook
26
- from airflow.providers.microsoft.azure.utils import get_field
25
+ from airflow.providers.microsoft.azure.utils import (
26
+ add_managed_identity_connection_widgets,
27
+ get_field,
28
+ get_sync_default_azure_credential,
29
+ )
30
+
31
+ if TYPE_CHECKING:
32
+ from azure.identity import DefaultAzureCredential
27
33
 
28
34
 
29
35
  class BaseAzureServiceBusHook(BaseHook):
@@ -40,6 +46,7 @@ class BaseAzureServiceBusHook(BaseHook):
40
46
  hook_name = "Azure Service Bus"
41
47
 
42
48
  @staticmethod
49
+ @add_managed_identity_connection_widgets
43
50
  def get_connection_form_widgets() -> dict[str, Any]:
44
51
  """Returns connection widgets to add to connection form."""
45
52
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -64,7 +71,7 @@ class BaseAzureServiceBusHook(BaseHook):
64
71
  "<Resource group>.servicebus.windows.net (for Azure AD authenticaltion)"
65
72
  ),
66
73
  "credential": "credential",
67
- "schema": "Endpoint=sb://<Resource group>.servicebus.windows.net/;SharedAccessKeyName=<AccessKeyName>;SharedAccessKey=<SharedAccessKey>", # noqa
74
+ "schema": "Endpoint=sb://<Resource group>.servicebus.windows.net/;SharedAccessKeyName=<AccessKeyName>;SharedAccessKey=<SharedAccessKey>",
68
75
  },
69
76
  }
70
77
 
@@ -106,7 +113,16 @@ class AdminClientHook(BaseAzureServiceBusHook):
106
113
  credential: str | DefaultAzureCredential = self._get_field(extras=extras, field_name="credential")
107
114
  fully_qualified_namespace = self._get_field(extras=extras, field_name="fully_qualified_namespace")
108
115
  if not credential:
109
- credential = DefaultAzureCredential()
116
+ managed_identity_client_id = self._get_field(
117
+ extras=extras, field_name="managed_identity_client_id"
118
+ )
119
+ workload_identity_tenant_id = self._get_field(
120
+ extras=extras, field_name="workload_identity_tenant_id"
121
+ )
122
+ credential = get_sync_default_azure_credential(
123
+ managed_identity_client_id=managed_identity_client_id,
124
+ workload_identity_tenant_id=workload_identity_tenant_id,
125
+ )
110
126
  client = ServiceBusAdministrationClient(
111
127
  fully_qualified_namespace=fully_qualified_namespace,
112
128
  credential=credential, # type: ignore[arg-type]
@@ -190,7 +206,16 @@ class MessageHook(BaseAzureServiceBusHook):
190
206
  credential: str | DefaultAzureCredential = self._get_field(extras=extras, field_name="credential")
191
207
  fully_qualified_namespace = self._get_field(extras=extras, field_name="fully_qualified_namespace")
192
208
  if not credential:
193
- credential = DefaultAzureCredential()
209
+ managed_identity_client_id = self._get_field(
210
+ extras=extras, field_name="managed_identity_client_id"
211
+ )
212
+ workload_identity_tenant_id = self._get_field(
213
+ extras=extras, field_name="workload_identity_tenant_id"
214
+ )
215
+ credential = get_sync_default_azure_credential(
216
+ managed_identity_client_id=managed_identity_client_id,
217
+ workload_identity_tenant_id=workload_identity_tenant_id,
218
+ )
194
219
  client = ServiceBusClient(
195
220
  fully_qualified_namespace=fully_qualified_namespace,
196
221
  credential=credential, # type: ignore[arg-type]
@@ -24,7 +24,10 @@ from azure.common.credentials import ServicePrincipalCredentials
24
24
 
25
25
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
26
26
  from airflow.hooks.base import BaseHook
27
- from airflow.providers.microsoft.azure.utils import AzureIdentityCredentialAdapter
27
+ from airflow.providers.microsoft.azure.utils import (
28
+ AzureIdentityCredentialAdapter,
29
+ add_managed_identity_connection_widgets,
30
+ )
28
31
 
29
32
 
30
33
  class AzureBaseHook(BaseHook):
@@ -45,6 +48,7 @@ class AzureBaseHook(BaseHook):
45
48
  hook_name = "Azure"
46
49
 
47
50
  @staticmethod
51
+ @add_managed_identity_connection_widgets
48
52
  def get_connection_form_widgets() -> dict[str, Any]:
49
53
  """Returns connection widgets to add to connection form."""
50
54
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -133,7 +137,12 @@ class AzureBaseHook(BaseHook):
133
137
  )
134
138
  else:
135
139
  self.log.info("Using DefaultAzureCredential as credential")
136
- credentials = AzureIdentityCredentialAdapter()
140
+ managed_identity_client_id = conn.extra_dejson.get("managed_identity_client_id")
141
+ workload_identity_tenant_id = conn.extra_dejson.get("workload_identity_tenant_id")
142
+ credentials = AzureIdentityCredentialAdapter(
143
+ managed_identity_client_id=managed_identity_client_id,
144
+ workload_identity_tenant_id=workload_identity_tenant_id,
145
+ )
137
146
 
138
147
  return self.sdk_client(
139
148
  credentials=credentials,
@@ -26,7 +26,11 @@ from azure.batch import BatchServiceClient, batch_auth, models as batch_models
26
26
 
27
27
  from airflow.exceptions import AirflowException
28
28
  from airflow.hooks.base import BaseHook
29
- from airflow.providers.microsoft.azure.utils import AzureIdentityCredentialAdapter, get_field
29
+ from airflow.providers.microsoft.azure.utils import (
30
+ AzureIdentityCredentialAdapter,
31
+ add_managed_identity_connection_widgets,
32
+ get_field,
33
+ )
30
34
  from airflow.utils import timezone
31
35
 
32
36
  if TYPE_CHECKING:
@@ -46,15 +50,8 @@ class AzureBatchHook(BaseHook):
46
50
  conn_type = "azure_batch"
47
51
  hook_name = "Azure Batch Service"
48
52
 
49
- def _get_field(self, extras, name):
50
- return get_field(
51
- conn_id=self.conn_id,
52
- conn_type=self.conn_type,
53
- extras=extras,
54
- field_name=name,
55
- )
56
-
57
53
  @classmethod
54
+ @add_managed_identity_connection_widgets
58
55
  def get_connection_form_widgets(cls) -> dict[str, Any]:
59
56
  """Returns connection widgets to add to connection form."""
60
57
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -80,6 +77,14 @@ class AzureBatchHook(BaseHook):
80
77
  super().__init__()
81
78
  self.conn_id = azure_batch_conn_id
82
79
 
80
+ def _get_field(self, extras, name):
81
+ return get_field(
82
+ conn_id=self.conn_id,
83
+ conn_type=self.conn_type,
84
+ extras=extras,
85
+ field_name=name,
86
+ )
87
+
83
88
  @cached_property
84
89
  def connection(self) -> BatchServiceClient:
85
90
  """Get the Batch client connection (cached)."""
@@ -101,8 +106,13 @@ class AzureBatchHook(BaseHook):
101
106
  if all([conn.login, conn.password]):
102
107
  credentials = batch_auth.SharedKeyCredentials(conn.login, conn.password)
103
108
  else:
109
+ managed_identity_client_id = conn.extra_dejson.get("managed_identity_client_id")
110
+ workload_identity_tenant_id = conn.extra_dejson.get("workload_identity_tenant_id")
104
111
  credentials = AzureIdentityCredentialAdapter(
105
- None, resource_id="https://batch.core.windows.net/.default"
112
+ None,
113
+ resource_id="https://batch.core.windows.net/.default",
114
+ managed_identity_client_id=managed_identity_client_id,
115
+ workload_identity_tenant_id=workload_identity_tenant_id,
106
116
  )
107
117
 
108
118
  batch_client = BatchServiceClient(credentials, batch_url=batch_account_url)
@@ -27,6 +27,7 @@ from azure.mgmt.containerinstance import ContainerInstanceManagementClient
27
27
 
28
28
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
29
29
  from airflow.providers.microsoft.azure.hooks.base_azure import AzureBaseHook
30
+ from airflow.providers.microsoft.azure.utils import get_sync_default_azure_credential
30
31
 
31
32
  if TYPE_CHECKING:
32
33
  from azure.mgmt.containerinstance.models import (
@@ -92,7 +93,12 @@ class AzureContainerInstanceHook(AzureBaseHook):
92
93
  )
93
94
  else:
94
95
  self.log.info("Using DefaultAzureCredential as credential")
95
- credential = DefaultAzureCredential()
96
+ managed_identity_client_id = conn.extra_dejson.get("managed_identity_client_id")
97
+ workload_identity_tenant_id = conn.extra_dejson.get("workload_identity_tenant_id")
98
+ credential = get_sync_default_azure_credential(
99
+ managed_identity_client_id=managed_identity_client_id,
100
+ workload_identity_tenant_id=workload_identity_tenant_id,
101
+ )
96
102
 
97
103
  subscription_id = cast(str, conn.extra_dejson.get("subscriptionId"))
98
104
  return ContainerInstanceManagementClient(
@@ -21,12 +21,15 @@ from __future__ import annotations
21
21
  from functools import cached_property
22
22
  from typing import Any
23
23
 
24
- from azure.identity import DefaultAzureCredential
25
24
  from azure.mgmt.containerinstance.models import ImageRegistryCredential
26
25
  from azure.mgmt.containerregistry import ContainerRegistryManagementClient
27
26
 
28
27
  from airflow.hooks.base import BaseHook
29
- from airflow.providers.microsoft.azure.utils import get_field
28
+ from airflow.providers.microsoft.azure.utils import (
29
+ add_managed_identity_connection_widgets,
30
+ get_field,
31
+ get_sync_default_azure_credential,
32
+ )
30
33
 
31
34
 
32
35
  class AzureContainerRegistryHook(BaseHook):
@@ -44,6 +47,7 @@ class AzureContainerRegistryHook(BaseHook):
44
47
  hook_name = "Azure Container Registry"
45
48
 
46
49
  @staticmethod
50
+ @add_managed_identity_connection_widgets
47
51
  def get_connection_form_widgets() -> dict[str, Any]:
48
52
  """Returns connection widgets to add to connection form."""
49
53
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -103,8 +107,15 @@ class AzureContainerRegistryHook(BaseHook):
103
107
  extras = conn.extra_dejson
104
108
  subscription_id = self._get_field(extras, "subscription_id")
105
109
  resource_group = self._get_field(extras, "resource_group")
110
+ managed_identity_client_id = self._get_field(extras, "managed_identity_client_id")
111
+ workload_identity_tenant_id = self._get_field(extras, "workload_identity_tenant_id")
112
+ credential = get_sync_default_azure_credential(
113
+ managed_identity_client_id=managed_identity_client_id,
114
+ workload_identity_tenant_id=workload_identity_tenant_id,
115
+ )
106
116
  client = ContainerRegistryManagementClient(
107
- credential=DefaultAzureCredential(), subscription_id=subscription_id
117
+ credential=credential,
118
+ subscription_id=subscription_id,
108
119
  )
109
120
  credentials = client.registries.list_credentials(resource_group, conn.login).as_dict()
110
121
  password = credentials["passwords"][0]["value"]
@@ -1,4 +1,3 @@
1
- #
2
1
  # Licensed to the Apache Software Foundation (ASF) under one
3
2
  # or more contributor license agreements. See the NOTICE file
4
3
  # distributed with this work for additional information
@@ -19,12 +18,15 @@ from __future__ import annotations
19
18
 
20
19
  from typing import Any
21
20
 
22
- from azure.identity import DefaultAzureCredential
23
21
  from azure.mgmt.containerinstance.models import AzureFileVolume, Volume
24
22
  from azure.mgmt.storage import StorageManagementClient
25
23
 
26
24
  from airflow.hooks.base import BaseHook
27
- from airflow.providers.microsoft.azure.utils import get_field
25
+ from airflow.providers.microsoft.azure.utils import (
26
+ add_managed_identity_connection_widgets,
27
+ get_field,
28
+ get_sync_default_azure_credential,
29
+ )
28
30
 
29
31
 
30
32
  class AzureContainerVolumeHook(BaseHook):
@@ -41,19 +43,8 @@ class AzureContainerVolumeHook(BaseHook):
41
43
  conn_type = "azure_container_volume"
42
44
  hook_name = "Azure Container Volume"
43
45
 
44
- def __init__(self, azure_container_volume_conn_id: str = "azure_container_volume_default") -> None:
45
- super().__init__()
46
- self.conn_id = azure_container_volume_conn_id
47
-
48
- def _get_field(self, extras, name):
49
- return get_field(
50
- conn_id=self.conn_id,
51
- conn_type=self.conn_type,
52
- extras=extras,
53
- field_name=name,
54
- )
55
-
56
46
  @staticmethod
47
+ @add_managed_identity_connection_widgets
57
48
  def get_connection_form_widgets() -> dict[str, Any]:
58
49
  """Returns connection widgets to add to connection form."""
59
50
  from flask_appbuilder.fieldwidgets import BS3PasswordFieldWidget, BS3TextFieldWidget
@@ -92,6 +83,18 @@ class AzureContainerVolumeHook(BaseHook):
92
83
  },
93
84
  }
94
85
 
86
+ def __init__(self, azure_container_volume_conn_id: str = "azure_container_volume_default") -> None:
87
+ super().__init__()
88
+ self.conn_id = azure_container_volume_conn_id
89
+
90
+ def _get_field(self, extras, name):
91
+ return get_field(
92
+ conn_id=self.conn_id,
93
+ conn_type=self.conn_type,
94
+ extras=extras,
95
+ field_name=name,
96
+ )
97
+
95
98
  def get_storagekey(self, *, storage_account_name: str | None = None) -> str:
96
99
  """Get Azure File Volume storage key."""
97
100
  conn = self.get_connection(self.conn_id)
@@ -106,8 +109,13 @@ class AzureContainerVolumeHook(BaseHook):
106
109
  subscription_id = self._get_field(extras, "subscription_id")
107
110
  resource_group = self._get_field(extras, "resource_group")
108
111
  if subscription_id and storage_account_name and resource_group:
109
- credentials = DefaultAzureCredential()
110
- storage_client = StorageManagementClient(credentials, subscription_id)
112
+ managed_identity_client_id = self._get_field(extras, "managed_identity_client_id")
113
+ workload_identity_tenant_id = self._get_field(extras, "workload_identity_tenant_id")
114
+ credential = get_sync_default_azure_credential(
115
+ managed_identity_client_id=managed_identity_client_id,
116
+ workload_identity_tenant_id=workload_identity_tenant_id,
117
+ )
118
+ storage_client = StorageManagementClient(credential, subscription_id)
111
119
  storage_account_list_keys_result = storage_client.storage_accounts.list_keys(
112
120
  resource_group, storage_account_name
113
121
  )
@@ -31,12 +31,15 @@ from urllib.parse import urlparse
31
31
 
32
32
  from azure.cosmos.cosmos_client import CosmosClient
33
33
  from azure.cosmos.exceptions import CosmosHttpResponseError
34
- from azure.identity import DefaultAzureCredential
35
34
  from azure.mgmt.cosmosdb import CosmosDBManagementClient
36
35
 
37
36
  from airflow.exceptions import AirflowBadRequest, AirflowException
38
37
  from airflow.hooks.base import BaseHook
39
- from airflow.providers.microsoft.azure.utils import get_field
38
+ from airflow.providers.microsoft.azure.utils import (
39
+ add_managed_identity_connection_widgets,
40
+ get_field,
41
+ get_sync_default_azure_credential,
42
+ )
40
43
 
41
44
 
42
45
  class AzureCosmosDBHook(BaseHook):
@@ -57,6 +60,7 @@ class AzureCosmosDBHook(BaseHook):
57
60
  hook_name = "Azure CosmosDB"
58
61
 
59
62
  @staticmethod
63
+ @add_managed_identity_connection_widgets
60
64
  def get_connection_form_widgets() -> dict[str, Any]:
61
65
  """Returns connection widgets to add to connection form."""
62
66
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -126,9 +130,16 @@ class AzureCosmosDBHook(BaseHook):
126
130
  if conn.password:
127
131
  master_key = conn.password
128
132
  elif resource_group_name:
133
+ managed_identity_client_id = self._get_field(extras, "managed_identity_client_id")
134
+ workload_identity_tenant_id = self._get_field(extras, "workload_identity_tenant_id")
135
+ subscritption_id = self._get_field(extras, "subscription_id")
136
+ credential = get_sync_default_azure_credential(
137
+ managed_identity_client_id=managed_identity_client_id,
138
+ workload_identity_tenant_id=workload_identity_tenant_id,
139
+ )
129
140
  management_client = CosmosDBManagementClient(
130
- credential=DefaultAzureCredential(),
131
- subscription_id=self._get_field(extras, "subscription_id"),
141
+ credential=credential,
142
+ subscription_id=subscritption_id,
132
143
  )
133
144
 
134
145
  database_account = urlparse(conn.login).netloc.split(".")[0]
@@ -48,6 +48,11 @@ from azure.mgmt.datafactory.aio import DataFactoryManagementClient as AsyncDataF
48
48
 
49
49
  from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
50
50
  from airflow.hooks.base import BaseHook
51
+ from airflow.providers.microsoft.azure.utils import (
52
+ add_managed_identity_connection_widgets,
53
+ get_async_default_azure_credential,
54
+ get_sync_default_azure_credential,
55
+ )
51
56
 
52
57
  if TYPE_CHECKING:
53
58
  from azure.core.polling import LROPoller
@@ -152,6 +157,7 @@ class AzureDataFactoryHook(BaseHook):
152
157
  hook_name: str = "Azure Data Factory"
153
158
 
154
159
  @staticmethod
160
+ @add_managed_identity_connection_widgets
155
161
  def get_connection_form_widgets() -> dict[str, Any]:
156
162
  """Returns connection widgets to add to connection form."""
157
163
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -205,7 +211,12 @@ class AzureDataFactoryHook(BaseHook):
205
211
  client_id=conn.login, client_secret=conn.password, tenant_id=tenant
206
212
  )
207
213
  else:
208
- credential = DefaultAzureCredential()
214
+ managed_identity_client_id = get_field(extras, "managed_identity_client_id")
215
+ workload_identity_tenant_id = get_field(extras, "workload_identity_tenant_id")
216
+ credential = get_sync_default_azure_credential(
217
+ managed_identity_client_id=managed_identity_client_id,
218
+ workload_identity_tenant_id=workload_identity_tenant_id,
219
+ )
209
220
  self._conn = self._create_client(credential, subscription_id)
210
221
 
211
222
  return self._conn
@@ -1140,7 +1151,12 @@ class AzureDataFactoryAsyncHook(AzureDataFactoryHook):
1140
1151
  client_id=conn.login, client_secret=conn.password, tenant_id=tenant
1141
1152
  )
1142
1153
  else:
1143
- credential = AsyncDefaultAzureCredential()
1154
+ managed_identity_client_id = get_field(extras, "managed_identity_client_id")
1155
+ workload_identity_tenant_id = get_field(extras, "workload_identity_tenant_id")
1156
+ credential = get_async_default_azure_credential(
1157
+ managed_identity_client_id=managed_identity_client_id,
1158
+ workload_identity_tenant_id=workload_identity_tenant_id,
1159
+ )
1144
1160
 
1145
1161
  self._async_conn = AsyncDataFactoryManagementClient(
1146
1162
  credential=credential,
@@ -34,7 +34,11 @@ from azure.storage.filedatalake import (
34
34
 
35
35
  from airflow.exceptions import AirflowException
36
36
  from airflow.hooks.base import BaseHook
37
- from airflow.providers.microsoft.azure.utils import AzureIdentityCredentialAdapter, get_field
37
+ from airflow.providers.microsoft.azure.utils import (
38
+ AzureIdentityCredentialAdapter,
39
+ add_managed_identity_connection_widgets,
40
+ get_field,
41
+ )
38
42
 
39
43
  Credentials = Union[ClientSecretCredential, AzureIdentityCredentialAdapter]
40
44
 
@@ -62,6 +66,7 @@ class AzureDataLakeHook(BaseHook):
62
66
  hook_name = "Azure Data Lake"
63
67
 
64
68
  @staticmethod
69
+ @add_managed_identity_connection_widgets
65
70
  def get_connection_form_widgets() -> dict[str, Any]:
66
71
  """Returns connection widgets to add to connection form."""
67
72
  from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
@@ -118,7 +123,12 @@ class AzureDataLakeHook(BaseHook):
118
123
  if tenant:
119
124
  credential = lib.auth(tenant_id=tenant, client_secret=conn.password, client_id=conn.login)
120
125
  else:
121
- credential = AzureIdentityCredentialAdapter()
126
+ managed_identity_client_id = self._get_field(extras, "managed_identity_client_id")
127
+ workload_identity_tenant_id = self._get_field(extras, "workload_identity_tenant_id")
128
+ credential = AzureIdentityCredentialAdapter(
129
+ managed_identity_client_id=managed_identity_client_id,
130
+ workload_identity_tenant_id=workload_identity_tenant_id,
131
+ )
122
132
  self._conn = core.AzureDLFileSystem(credential, store_name=self.account_name)
123
133
  self._conn.connect()
124
134
  return self._conn
@@ -265,6 +275,7 @@ class AzureDataLakeStorageV2Hook(BaseHook):
265
275
  hook_name = "Azure Date Lake Storage V2"
266
276
 
267
277
  @classmethod
278
+ @add_managed_identity_connection_widgets
268
279
  def get_connection_form_widgets(cls) -> dict[str, Any]:
269
280
  """Returns connection widgets to add to connection form."""
270
281
  from flask_appbuilder.fieldwidgets import BS3PasswordFieldWidget, BS3TextFieldWidget
@@ -305,6 +316,17 @@ class AzureDataLakeStorageV2Hook(BaseHook):
305
316
  self.conn_id = adls_conn_id
306
317
  self.public_read = public_read
307
318
 
319
+ def _get_field(self, extra_dict, field_name):
320
+ prefix = "extra__adls__"
321
+ if field_name.startswith("extra__"):
322
+ raise ValueError(
323
+ f"Got prefixed name {field_name}; please remove the '{prefix}' prefix "
324
+ f"when using this method."
325
+ )
326
+ if field_name in extra_dict:
327
+ return extra_dict[field_name] or None
328
+ return extra_dict.get(f"{prefix}{field_name}") or None
329
+
308
330
  @cached_property
309
331
  def service_client(self) -> DataLakeServiceClient:
310
332
  """Return the DataLakeServiceClient object (cached)."""
@@ -330,7 +352,12 @@ class AzureDataLakeStorageV2Hook(BaseHook):
330
352
  elif conn.password:
331
353
  credential = conn.password
332
354
  else:
333
- credential = AzureIdentityCredentialAdapter()
355
+ managed_identity_client_id = self._get_field(extra, "managed_identity_client_id")
356
+ workload_identity_tenant_id = self._get_field(extra, "workload_identity_tenant_id")
357
+ credential = AzureIdentityCredentialAdapter(
358
+ managed_identity_client_id=managed_identity_client_id,
359
+ workload_identity_tenant_id=workload_identity_tenant_id,
360
+ )
334
361
 
335
362
  return DataLakeServiceClient(
336
363
  account_url=f"https://{conn.host}.dfs.core.windows.net",
@@ -338,17 +365,6 @@ class AzureDataLakeStorageV2Hook(BaseHook):
338
365
  **extra,
339
366
  )
340
367
 
341
- def _get_field(self, extra_dict, field_name):
342
- prefix = "extra__adls__"
343
- if field_name.startswith("extra__"):
344
- raise ValueError(
345
- f"Got prefixed name {field_name}; please remove the '{prefix}' prefix "
346
- f"when using this method."
347
- )
348
- if field_name in extra_dict:
349
- return extra_dict[field_name] or None
350
- return extra_dict.get(f"{prefix}{field_name}") or None
351
-
352
368
  def create_file_system(self, file_system_name: str) -> None:
353
369
  """Create a new file system under the specified account.
354
370