@@ -27,6 +27,19 @@ import (
2727// Round-robin counter for service principal selection
2828var servicePrincipalCounter uint64
2929
30+ const (
31+ // SubscriptionID defines the azure credentials key for subscription id
32+ SubscriptionID = "subscriptionId"
33+ // TenantID defines the azure credentials key for tenant id
34+ TenantID = "tenantId"
35+ // ClientID defines the azure credentials key for client id
36+ ClientID = "clientId"
37+ // ClientSecret defines the azure credentials key for client secret
38+ ClientSecret = "clientSecret"
39+ // WorkloadIdentityCredentialPath defines the azure credentials key for federated token file path
40+ WorkloadIdentityCredentialPath = "federatedTokenFile"
41+ )
42+
3043// AzureQueryInterface defines the methods required for querying Azure resources.
3144type AzureQueryInterface interface {
3245 azQuery (ctx context.Context , azureCreds interface {}, in * v1beta1.Input , log logging.Logger ) (armresourcegraph.ClientResourcesResponse , error )
@@ -371,7 +384,7 @@ func (a *AzureQuery) handleSingleServicePrincipal(creds map[string]string, log l
371384 allSubscriptionIDs := []string {}
372385
373386 // Extract subscription ID if present
374- if subID , exists := creds ["subscriptionId" ]; exists && subID != "" {
387+ if subID , exists := creds [SubscriptionID ]; exists && subID != "" {
375388 allSubscriptionIDs = append (allSubscriptionIDs , subID )
376389 }
377390
@@ -392,7 +405,7 @@ func (a *AzureQuery) handleMultipleServicePrincipals(creds []map[string]string,
392405
393406 // Extract subscription IDs from all service principals
394407 for _ , cred := range creds {
395- if subID , exists := cred ["subscriptionId" ]; exists && subID != "" {
408+ if subID , exists := cred [SubscriptionID ]; exists && subID != "" {
396409 allSubscriptionIDs = append (allSubscriptionIDs , subID )
397410 }
398411 }
@@ -436,16 +449,24 @@ func (a *AzureQuery) setupQueryRequest(in *v1beta1.Input, allSubscriptionIDs []s
436449}
437450
438451// azQuery is a concrete implementation that interacts with Azure Resource Graph API.
439- func (a * AzureQuery ) azQuery (ctx context.Context , azureCreds interface {}, in * v1beta1.Input , log logging.Logger ) (armresourcegraph.ClientResourcesResponse , error ) {
452+ func (a * AzureQuery ) azQuery (ctx context.Context , azureCreds interface {}, in * v1beta1.Input , log logging.Logger ) (results armresourcegraph.ClientResourcesResponse , err error ) { //nolint:gocyclo // complexity can not be reduced as it's a result of choosing correct identity and credentials for it
440453 var selectedCreds map [string ]string
441454 var allSubscriptionIDs []string
455+ var client * armresourcegraph.Client
456+ identityType := v1beta1 .IdentityTypeAzureServicePrincipalCredentials
457+
458+ if in .Identity != nil && in .Identity .Type != "" {
459+ identityType = in .Identity .Type
460+ }
442461
443462 // Handle different credential formats and extract subscription IDs
444463 switch v := azureCreds .(type ) {
445464 case map [string ]string :
446465 selectedCreds , allSubscriptionIDs , _ = a .handleSingleServicePrincipal (v , log )
447466 case []map [string ]string :
448- var err error
467+ if identityType == v1beta1 .IdentityTypeAzureWorkloadIdentityCredentials {
468+ return armresourcegraph.ClientResourcesResponse {}, errors .New ("invalid credential format: workload identity support only one credentials entry" )
469+ }
449470 selectedCreds , allSubscriptionIDs , _ , err = a .handleMultipleServicePrincipals (v , log )
450471 if err != nil {
451472 return armresourcegraph.ClientResourcesResponse {}, err
@@ -454,35 +475,91 @@ func (a *AzureQuery) azQuery(ctx context.Context, azureCreds interface{}, in *v1
454475 return armresourcegraph.ClientResourcesResponse {}, errors .New ("invalid credential format" )
455476 }
456477
457- tenantID := selectedCreds ["tenantId" ]
458- clientID := selectedCreds ["clientId" ]
459- clientSecret := selectedCreds ["clientSecret" ]
478+ switch identityType {
479+ case v1beta1 .IdentityTypeAzureServicePrincipalCredentials :
480+ log .Info ("Using authentication method" , "identityType" , v1beta1 .IdentityTypeAzureServicePrincipalCredentials )
481+ client , err = a .initializeClientSecretProvider (selectedCreds , log )
482+ if err != nil {
483+ return armresourcegraph.ClientResourcesResponse {}, errors .Wrap (err , "failed to initialize service principal provider" )
484+ }
485+ case v1beta1 .IdentityTypeAzureWorkloadIdentityCredentials :
486+ log .Info ("Using authentication method" , "identityType" , v1beta1 .IdentityTypeAzureWorkloadIdentityCredentials )
487+ client , err = a .initializeWorkloadIdentityProvider (selectedCreds , log )
488+ if err != nil {
489+ return armresourcegraph.ClientResourcesResponse {}, errors .Wrap (err , "failed to initialize workload identity provider" )
490+ }
491+ }
492+
493+ // Setup the query request
494+ queryRequest := a .setupQueryRequest (in , allSubscriptionIDs , log )
495+
496+ // Create the query request, Run the query and get the results.
497+ results , err = client .Resources (ctx , queryRequest , nil )
498+ if err != nil {
499+ return armresourcegraph.ClientResourcesResponse {}, errors .Wrap (err , "failed to finish the request" )
500+ }
501+ return results , nil
502+ }
503+
504+ func (a * AzureQuery ) initializeWorkloadIdentityProvider (azureCreds map [string ]string , log logging.Logger ) (* armresourcegraph.Client , error ) {
505+ tokenFilePath := azureCreds [WorkloadIdentityCredentialPath ]
506+
507+ options := & azidentity.WorkloadIdentityCredentialOptions {
508+ TokenFilePath : tokenFilePath ,
509+ }
510+
511+ // Defaults to the value of the environment variable AZURE_TENANT_ID
512+ tenantID , found := azureCreds [TenantID ]
513+ if found {
514+ options .TenantID = tenantID
515+ }
516+
517+ // Defaults to the value of the environment variable AZURE_CLIENT_ID
518+ clientID , found := azureCreds [ClientID ]
519+ if found {
520+ options .ClientID = clientID
521+ }
522+
523+ // Create Azure credential
524+ log .Info ("Initializing workload identity provider" , "tokenFile" , tokenFilePath )
525+
526+ cred , err := azidentity .NewWorkloadIdentityCredential (options )
527+ if err != nil {
528+ return nil , errors .Wrap (err , "failed to obtain workloadidentity credentials" )
529+ }
530+
531+ // Create and authorize a ResourceGraph client
532+ client , err := armresourcegraph .NewClient (cred , nil )
533+ if err != nil {
534+ return nil , errors .Wrap (err , "failed to create client" )
535+ }
460536
461- // Log credential information using structured logging (without sensitive data)
462537 log .Debug ("Selected service principal" , "clientId" , clientID )
463538
539+ return client , nil
540+ }
541+
542+ func (a * AzureQuery ) initializeClientSecretProvider (azureCreds map [string ]string , log logging.Logger ) (* armresourcegraph.Client , error ) {
543+ tenantID := azureCreds [TenantID ]
544+ clientID := azureCreds [ClientID ]
545+ clientSecret := azureCreds [ClientSecret ]
546+
464547 // To configure DefaultAzureCredential to authenticate a user-assigned managed identity,
465548 // set the environment variable AZURE_CLIENT_ID to the identity's client ID.
466549 cred , err := azidentity .NewClientSecretCredential (tenantID , clientID , clientSecret , nil )
467550 if err != nil {
468- return armresourcegraph. ClientResourcesResponse {} , errors .Wrap (err , "failed to obtain credentials" )
551+ return nil , errors .Wrap (err , "failed to obtain clientsecret credentials" )
469552 }
470553
471554 // Create and authorize a ResourceGraph client
472555 client , err := armresourcegraph .NewClient (cred , nil )
473556 if err != nil {
474- return armresourcegraph. ClientResourcesResponse {} , errors .Wrap (err , "failed to create client" )
557+ return nil , errors .Wrap (err , "failed to create client" )
475558 }
476559
477- // Setup the query request
478- queryRequest := a .setupQueryRequest (in , allSubscriptionIDs , log )
560+ log .Debug ("Selected service principal" , "clientId" , clientID )
479561
480- // Create the query request, Run the query and get the results.
481- results , err := client .Resources (ctx , queryRequest , nil )
482- if err != nil {
483- return armresourcegraph.ClientResourcesResponse {}, errors .Wrap (err , "failed to finish the request" )
484- }
485- return results , nil
562+ return client , nil
486563}
487564
488565// ParseNestedKey enables the bracket and dot notation to key reference
0 commit comments