88using System . Net ;
99using System . Net . Http ;
1010using System . Security . Cryptography . X509Certificates ;
11+ using Azure ;
12+ using Azure . Core ;
13+ using Azure . Identity ;
1114using Azure . Storage . Blobs ;
1215using Azure . Storage . Queues ;
1316using Microsoft . Extensions . Logging ;
@@ -28,6 +31,7 @@ namespace Ng
2831{
2932 public static class CommandHelpers
3033 {
34+ private const string DefaultStorageSuffix = "core.windows.net" ;
3135 private static readonly int DefaultKeyVaultSecretCachingTimeout = 60 * 60 * 6 ; // 6 hours;
3236 private static readonly HashSet < string > NotInjectedKeys = new HashSet < string > ( StringComparer . OrdinalIgnoreCase )
3337 {
@@ -211,16 +215,14 @@ private static CatalogStorageFactory CreateStorageFactoryImpl(
211215
212216 if ( Arguments . AzureStorageType . Equals ( storageType , StringComparison . InvariantCultureIgnoreCase ) )
213217 {
214- var storageAccountName = arguments . GetOrThrow < string > ( argumentNameMap [ Arguments . StorageAccountName ] ) ;
215218 var storageContainer = arguments . GetOrThrow < string > ( argumentNameMap [ Arguments . StorageContainer ] ) ;
216219 var storagePath = arguments . GetOrDefault < string > ( argumentNameMap [ Arguments . StoragePath ] ) ;
217- var storageSuffix = arguments . GetOrDefault ( argumentNameMap [ Arguments . StorageSuffix ] , "core.windows.net" ) ;
218220 var storageOperationMaxExecutionTime = MaxExecutionTime ( arguments . GetOrDefault < int > ( argumentNameMap [ Arguments . StorageOperationMaxExecutionTimeInSeconds ] ) ) ;
219221 var storageServerTimeout = MaxExecutionTime ( arguments . GetOrDefault < int > ( argumentNameMap [ Arguments . StorageServerTimeoutInSeconds ] ) ) ;
220222 var storageUseServerSideCopy = arguments . GetOrDefault < bool > ( argumentNameMap [ Arguments . StorageUseServerSideCopy ] ) ;
221223 var storageInitializeContainer = arguments . GetOrDefault ( argumentNameMap [ Arguments . StorageInitializeContainer ] , defaultValue : true ) ;
222224
223- BlobServiceClient account = GetBlobServiceClient ( storageAccountName , storageSuffix , arguments , argumentNameMap ) ;
225+ BlobServiceClient account = GetBlobServiceClient ( arguments , argumentNameMap ) ;
224226
225227 return new CatalogAzureStorageFactory (
226228 account ,
@@ -283,19 +285,28 @@ public static Func<HttpMessageHandler> GetHttpMessageHandlerFactory(
283285
284286 public static EndpointConfiguration GetEndpointConfiguration ( IDictionary < string , string > arguments )
285287 {
288+ var clientId = arguments . GetOrDefault < string > ( Arguments . ClientId ) ;
289+
286290 var registrationCursorUri = arguments . GetOrThrow < Uri > ( Arguments . RegistrationCursorUri ) ;
287291 var flatContainerCursorUri = arguments . GetOrThrow < Uri > ( Arguments . FlatContainerCursorUri ) ;
288292
289- var instanceNameToSearchBaseUri = GetSuffixToUri ( arguments , Arguments . SearchBaseUriPrefix ) ;
290- var instanceNameToSearchCursorUri = GetSuffixToUri ( arguments , Arguments . SearchCursorUriPrefix ) ;
293+ var instanceNameToSearchBaseUri = GetSuffixToValue < Uri > ( arguments , Arguments . SearchBaseUriPrefix ) ;
294+ var instanceNameToSearchCursorUri = GetSuffixToValue < Uri > ( arguments , Arguments . SearchCursorUriPrefix ) ;
295+ var instanceNameToSearchCursorSasValue = GetSuffixToValue < string > ( arguments , Arguments . SearchCursorSasValuePrefix ) ;
296+ var instanceNameToSearchCursorUseManagedIdentity = GetSuffixToValue < bool > ( arguments , Arguments . SearchCursorUseManagedIdentityPrefix ) ;
291297 var instanceNameToSearchConfig = new Dictionary < string , SearchEndpointConfiguration > ( ) ;
298+
292299 foreach ( var pair in instanceNameToSearchBaseUri )
293300 {
294301 var instanceName = pair . Key ;
295302
296303 // Find all cursors with an instance name starting with the search base URI instance name. We do this
297304 // because there may be multiple potential cursors representing the state of a search service.
298- var matchingCursors = instanceNameToSearchCursorUri . Keys . Where ( x => x . StartsWith ( instanceName ) ) . ToList ( ) ;
305+ var matchingCursors = instanceNameToSearchCursorUri
306+ . Keys
307+ . Where ( x => x . StartsWith ( instanceName ) )
308+ . OrderBy ( x => x )
309+ . ToList ( ) ;
299310
300311 if ( ! matchingCursors . Any ( ) )
301312 {
@@ -304,9 +315,51 @@ public static EndpointConfiguration GetEndpointConfiguration(IDictionary<string,
304315 $ "-{ Arguments . SearchCursorUriPrefix } { instanceName } * arguments.") ;
305316 }
306317
307- instanceNameToSearchConfig [ instanceName ] = new SearchEndpointConfiguration (
308- matchingCursors . Select ( x => instanceNameToSearchCursorUri [ x ] ) . ToList ( ) ,
309- pair . Value ) ;
318+ var cursors = new List < SearchCursorConfiguration > ( ) ;
319+
320+ foreach ( var suffix in matchingCursors )
321+ {
322+ var cursorUri = instanceNameToSearchCursorUri [ suffix ] ;
323+ SearchCursorCredentialType credentialType ;
324+
325+ BlobClient blobClient = null ;
326+ if ( instanceNameToSearchCursorUseManagedIdentity . TryGetValue ( suffix , out var useManagedIdentity )
327+ && useManagedIdentity )
328+ {
329+ TokenCredential credential ;
330+ if ( string . IsNullOrEmpty ( clientId ) )
331+ {
332+ credential = new DefaultAzureCredential ( ) ;
333+ credentialType = SearchCursorCredentialType . DefaultAzureCredential ;
334+ }
335+ else
336+ {
337+ credential = new ManagedIdentityCredential ( clientId ) ;
338+ credentialType = SearchCursorCredentialType . ManagedIdentityCredential ;
339+ }
340+
341+ blobClient = new BlobClient ( cursorUri , credential ) ;
342+ }
343+ else if ( instanceNameToSearchCursorSasValue . TryGetValue ( suffix , out var sas ) )
344+ {
345+ if ( sas . StartsWith ( "?" ) )
346+ {
347+ // workaround for https://github.com/Azure/azure-sdk-for-net/issues/44373
348+ sas = sas . Substring ( 1 ) ;
349+ }
350+
351+ blobClient = new BlobClient ( cursorUri , new AzureSasCredential ( sas ) ) ;
352+ credentialType = SearchCursorCredentialType . AzureSasCredential ;
353+ }
354+ else
355+ {
356+ credentialType = SearchCursorCredentialType . Anonymous ;
357+ }
358+
359+ cursors . Add ( new SearchCursorConfiguration ( cursorUri , blobClient , credentialType ) ) ;
360+ }
361+
362+ instanceNameToSearchConfig [ instanceName ] = new SearchEndpointConfiguration ( cursors , pair . Value ) ;
310363
311364 foreach ( var key in matchingCursors )
312365 {
@@ -329,13 +382,13 @@ public static EndpointConfiguration GetEndpointConfiguration(IDictionary<string,
329382 instanceNameToSearchConfig ) ;
330383 }
331384
332- private static Dictionary < string , Uri > GetSuffixToUri ( IDictionary < string , string > arguments , string prefix )
385+ private static Dictionary < string , T > GetSuffixToValue < T > ( IDictionary < string , string > arguments , string prefix )
333386 {
334- var suffixToUri = new Dictionary < string , Uri > ( ) ;
387+ var suffixToUri = new Dictionary < string , T > ( ) ;
335388 foreach ( var key in arguments . Keys . Where ( x => x . StartsWith ( prefix ) ) )
336389 {
337390 var suffix = key . Substring ( prefix . Length ) ;
338- suffixToUri [ suffix ] = arguments . GetOrThrow < Uri > ( key ) ;
391+ suffixToUri [ suffix ] = arguments . GetOrThrow < T > ( key ) ;
339392 }
340393
341394 return suffixToUri ;
@@ -354,10 +407,8 @@ public static IStorageQueue<T> CreateStorageQueue<T>(IDictionary<string, string>
354407
355408 if ( Arguments . AzureStorageType . Equals ( storageType , StringComparison . InvariantCultureIgnoreCase ) )
356409 {
357- var storageAccountName = arguments . GetOrThrow < string > ( Arguments . StorageAccountName ) ;
358410 var storageQueueName = arguments . GetOrDefault < string > ( Arguments . StorageQueueName ) ;
359-
360- QueueServiceClient account = GetQueueServiceClient ( storageAccountName , endpointSuffix : null , arguments , ArgumentNames ) ;
411+ QueueServiceClient account = GetQueueServiceClient ( arguments , ArgumentNames ) ;
361412 return new StorageQueue < T > ( new AzureStorageQueue ( account , storageQueueName ) ,
362413 new JsonMessageSerializer < T > ( JsonSerializerUtility . SerializerSettings ) , version ) ;
363414 }
@@ -367,8 +418,34 @@ public static IStorageQueue<T> CreateStorageQueue<T>(IDictionary<string, string>
367418 }
368419 }
369420
370- private static BlobServiceClient GetBlobServiceClient ( string storageAccountName , string endpointSuffix , IDictionary < string , string > arguments , IDictionary < string , string > argumentNameMap )
421+ private static BlobServiceClient GetBlobServiceClient (
422+ IDictionary < string , string > arguments ,
423+ IDictionary < string , string > argumentNameMap )
424+ {
425+ string connectionString = GetConnectionString ( arguments , argumentNameMap , "BlobEndpoint" , "blob" ) ;
426+ return new BlobServiceClient ( connectionString ) ;
427+ }
428+
429+ private static QueueServiceClient GetQueueServiceClient (
430+ IDictionary < string , string > arguments ,
431+ IDictionary < string , string > argumentNameMap )
432+ {
433+ string connectionString = GetConnectionString ( arguments , argumentNameMap , "QueueEndpoint" , "queue" ) ;
434+ return new QueueServiceClient ( connectionString , new QueueClientOptions
435+ {
436+ // We use base64 encoding for compatibility with the older SDK
437+ MessageEncoding = QueueMessageEncoding . Base64 ,
438+ } ) ;
439+ }
440+
441+ private static string GetConnectionString (
442+ IDictionary < string , string > arguments ,
443+ IDictionary < string , string > argumentNameMap ,
444+ string endpointKey ,
445+ string endpointDomain )
371446 {
447+ var storageAccountName = arguments . GetOrThrow < string > ( argumentNameMap [ Arguments . StorageAccountName ] ) ;
448+ var storageSuffix = arguments . GetOrDefault ( argumentNameMap [ Arguments . StorageSuffix ] , DefaultStorageSuffix ) ;
372449 var storageKeyValue = arguments . GetOrDefault < string > ( argumentNameMap [ Arguments . StorageKeyValue ] ) ;
373450
374451 string connectionString ;
@@ -382,34 +459,14 @@ private static BlobServiceClient GetBlobServiceClient(string storageAccountName,
382459 storageSasValue = storageSasValue . Substring ( 1 ) ;
383460 }
384461
385- connectionString = $ "BlobEndpoint=https://{ storageAccountName } .blob.{ endpointSuffix } /;SharedAccessSignature={ storageSasValue } ";
386- }
387- else
388- {
389- connectionString = $ "DefaultEndpointsProtocol=https;AccountName={ storageAccountName } ;AccountKey={ storageKeyValue } ;EndpointSuffix={ endpointSuffix } ";
390- }
391-
392- return new BlobServiceClient ( connectionString ) ;
393- }
394-
395- private static QueueServiceClient GetQueueServiceClient ( string storageAccountName , string endpointSuffix , IDictionary < string , string > arguments , IDictionary < string , string > argumentNameMap )
396- {
397- var storageKeyValue = arguments . GetOrDefault < string > ( argumentNameMap [ Arguments . StorageKeyValue ] ) ;
398-
399- string connectionString ;
400-
401- if ( string . IsNullOrEmpty ( storageKeyValue ) )
402- {
403- var storageSasValue = arguments . GetOrThrow < string > ( argumentNameMap [ Arguments . StorageSasValue ] ) ;
404- connectionString = $ "BlobEndpoint=https://{ storageAccountName } .blob.{ endpointSuffix } /;SharedAccessSignature={ storageSasValue } ";
462+ connectionString = $ "{ endpointKey } =https://{ storageAccountName } .{ endpointDomain } .{ storageSuffix } /;SharedAccessSignature={ storageSasValue } ";
405463 }
406464 else
407465 {
408- connectionString = $ "DefaultEndpointsProtocol=https;AccountName={ storageAccountName } ;AccountKey={ storageKeyValue } ;EndpointSuffix={ endpointSuffix } ";
466+ connectionString = $ "DefaultEndpointsProtocol=https;AccountName={ storageAccountName } ;AccountKey={ storageKeyValue } ;EndpointSuffix={ storageSuffix } ";
409467 }
410468
411- return new QueueServiceClient ( connectionString ) ;
469+ return connectionString ;
412470 }
413-
414471 }
415472}
0 commit comments