diff --git a/changelog/14214.txt b/changelog/14214.txt new file mode 100644 index 000000000..773b894b4 --- /dev/null +++ b/changelog/14214.txt @@ -0,0 +1,3 @@ +```release-note:improvement +agent: Adds ability to configure specific user-assigned managed identities for Azure auto-auth. +``` diff --git a/command/agent/auth/azure/azure.go b/command/agent/auth/azure/azure.go index bc01e561f..528e82ffe 100644 --- a/command/agent/auth/azure/azure.go +++ b/command/agent/auth/azure/azure.go @@ -30,6 +30,8 @@ type azureMethod struct { role string resource string + objectID string + clientID string } func NewAzureAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { @@ -63,11 +65,29 @@ func NewAzureAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) { return nil, errors.New("could not convert 'resource' config value to string") } + objectIDRaw, ok := conf.Config["object_id"] + if ok { + a.objectID, ok = objectIDRaw.(string) + if !ok { + return nil, errors.New("could not convert 'object_id' config value to string") + } + } + + clientIDRaw, ok := conf.Config["client_id"] + if ok { + a.clientID, ok = clientIDRaw.(string) + if !ok { + return nil, errors.New("could not convert 'client_id' config value to string") + } + } + switch { case a.role == "": return nil, errors.New("'role' value is empty") case a.resource == "": return nil, errors.New("'resource' value is empty") + case a.objectID != "" && a.clientID != "": + return nil, errors.New("only one of 'object_id' or 'client_id' may be provided") } return a, nil @@ -86,7 +106,7 @@ func (a *azureMethod) Authenticate(ctx context.Context, client *api.Client) (ret } } - body, err := getMetadataInfo(ctx, instanceEndpoint, "") + body, err := getMetadataInfo(ctx, instanceEndpoint, "", "", "") if err != nil { retErr = err return @@ -103,7 +123,7 @@ func (a *azureMethod) Authenticate(ctx context.Context, client *api.Client) (ret AccessToken string `json:"access_token"` } - body, err = getMetadataInfo(ctx, identityEndpoint, a.resource) + body, err = getMetadataInfo(ctx, identityEndpoint, a.resource, a.objectID, a.clientID) if err != nil { retErr = err return @@ -138,7 +158,7 @@ func (a *azureMethod) CredSuccess() { func (a *azureMethod) Shutdown() { } -func getMetadataInfo(ctx context.Context, endpoint, resource string) ([]byte, error) { +func getMetadataInfo(ctx context.Context, endpoint, resource, objectID, clientID string) ([]byte, error) { req, err := http.NewRequest("GET", endpoint, nil) if err != nil { return nil, err @@ -149,6 +169,12 @@ func getMetadataInfo(ctx context.Context, endpoint, resource string) ([]byte, er if resource != "" { q.Add("resource", resource) } + if objectID != "" { + q.Add("object_id", objectID) + } + if clientID != "" { + q.Add("client_id", clientID) + } req.URL.RawQuery = q.Encode() req.Header.Set("Metadata", "true") req.Header.Set("User-Agent", useragent.String()) diff --git a/website/content/docs/agent/autoauth/methods/azure.mdx b/website/content/docs/agent/autoauth/methods/azure.mdx index b2a0abb58..d02d0e61e 100644 --- a/website/content/docs/agent/autoauth/methods/azure.mdx +++ b/website/content/docs/agent/autoauth/methods/azure.mdx @@ -17,3 +17,13 @@ on the value of the `resource` parameter. - `role` `(string: required)` - The role to authenticate against on Vault - `resource` `(string: required)` - The resource name to use when getting instance information + +- `object_id` `(string: optional)` - The object ID of the user-assigned managed identity to use + when acquiring an [access token][azure-access-token]. Only one of `object_id` or `client_id` + may be provided. + +- `client_id` `(string: optional)` - The client ID of the user-assigned managed identity to use + when acquiring an [access token][azure-access-token]. Only one of `object_id` or `client_id` + may be provided. + +[azure-access-token]: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http