Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestAccServiceEndpointGenericV2_Basic(t *testing.T) {
ImportState: true,
ImportStateIdFunc: testutils.ComputeProjectQualifiedResourceImportID(tfSvcEpNode),
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"authorization_parameters"},
ImportStateVerifyIgnore: []string{"authorization_parameters", "validate_input"},
},
},
})
Expand Down Expand Up @@ -114,7 +114,7 @@ func TestAccServiceEndpointGenericV2_UsernamePassword(t *testing.T) {
ImportState: true,
ImportStateIdFunc: testutils.ComputeProjectQualifiedResourceImportID(tfSvcEpNode),
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"authorization_parameters"},
ImportStateVerifyIgnore: []string{"authorization_parameters", "validate_input"},
},
},
})
Expand Down Expand Up @@ -149,7 +149,7 @@ func TestAccServiceEndpointGenericV2_SharedProjects(t *testing.T) {
ImportState: true,
ImportStateIdFunc: testutils.ComputeProjectQualifiedResourceImportID(tfSvcEpNode),
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"authorization_parameters"},
ImportStateVerifyIgnore: []string{"authorization_parameters", "validate_input"},
},
},
})
Expand Down Expand Up @@ -274,6 +274,8 @@ resource "azuredevops_serviceendpoint_generic_v2" "test" {
type = "%s"
server_url = "https://github.com"

validate_input = true

authorization_scheme = "Token"
authorization_parameters = {
AccessToken = "test-token"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ func ResourceServiceEndpointGenericV2() *schema.Resource {
Type: schema.TypeString,
},
},
"validate_input": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Comment thread
magodo marked this conversation as resolved.
Description: "Whether to validate the provided service endpoint configuration against the service endpoint type definition. Enabling this will cause the provider to return an error if the configuration is invalid.",
},
},
}
}
Expand Down Expand Up @@ -301,9 +307,12 @@ func resourceServiceEndpointGenericV2Create(ctx context.Context, d *schema.Resou
return diag.FromErr(err)
}

// Validate the service endpoint configuration
if err := validateServiceEndpointSchema(clients, *config, false); err != nil {
return diag.FromErr(fmt.Errorf("service endpoint validation failed: %w", err))
// Validate the service endpoint configuration if validate_input is enabled
validateInput := d.Get("validate_input").(bool)
if validateInput {
if err := validateServiceEndpointSchema(clients, *config, false); err != nil {
return diag.FromErr(fmt.Errorf("service endpoint validation failed: %w", err))
}
}

// Create the service endpoint
Expand Down Expand Up @@ -533,6 +542,11 @@ func resourceServiceEndpointGenericV2Read(ctx context.Context, d *schema.Resourc
return diag.FromErr(fmt.Errorf("error setting shared_project_ids: %w", err))
}

// Set validate_input to default value if not explicitly set in state
if err := d.Set("validate_input", false); err != nil {
return diag.FromErr(fmt.Errorf("error setting validate_input: %w", err))
}

return nil
}

Expand Down Expand Up @@ -627,22 +641,30 @@ func resourceServiceEndpointGenericV2Update(ctx context.Context, d *schema.Resou
return resourceServiceEndpointGenericV2Read(ctx, d, m)
}

// updateServiceEndpointFromResourceData updates a service endpoint with values from resource data
// updateServiceEndpointFromResourceData updates a service endpoint with values from resource data.
//
// Each field is only overwritten on the endpoint returned by the API when the corresponding
// attribute has actually changed in the Terraform plan. This way, attributes excluded via the
// `ignore_changes` lifecycle meta-argument are preserved as-is (Terraform reports `HasChange`
// as false for ignored attributes, so we leave the API value untouched).
//
// Authorization is a special case: the API returns masked/empty values for sensitive
// parameters, so we must always reconstruct the authorization block from the Terraform state
// (which still holds the real credentials) whenever any auth-related field has changed.
func updateServiceEndpointFromResourceData(d *schema.ResourceData, endpoint *serviceendpoint.ServiceEndpoint) (*serviceendpoint.ServiceEndpoint, error) {
// Update fields that have changed
if d.HasChange("name") {
endpoint.Name = converter.String(d.Get("name").(string))
}

if d.HasChange("description") {
endpoint.Description = converter.String(d.Get("description").(string))
}

if d.HasChange("server_url") {
endpoint.Url = converter.String(d.Get("server_url").(string))
}

// Handle authorization updates if any auth fields changed
// Rebuild authorization from Terraform state when any auth field changed. The API
// returns masked values for sensitive parameters, so we never rely on the API response
// for these – the state always holds the real credentials.
if d.HasChange("authorization_scheme") || d.HasChange("authorization_parameters") {
authScheme, authParams, err := getAuthorizationDetails(d)
if err != nil {
Expand All @@ -654,13 +676,14 @@ func updateServiceEndpointFromResourceData(d *schema.ResourceData, endpoint *ser
}
}

// Handle data updates if changed
if d.HasChange("parameters") {
data, err := toStringMap(d.Get("parameters"), "parameters")
if err != nil {
return nil, err
}
endpoint.Data = &data
if len(data) > 0 {
endpoint.Data = &data
}
}

return endpoint, nil
Expand Down Expand Up @@ -793,6 +816,12 @@ func customizeServiceEndpointGenericV2Diff(ctx context.Context, d *schema.Resour
return nil
}

// Only validate if validate_input is enabled
validateInput := d.Get("validate_input").(bool)
if !validateInput {
return nil
}

serviceEndpointType := d.Get("type").(string)
authScheme, authParams, err := getAuthorizationDetailsFromDiff(d)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions website/docs/r/serviceendpoint_generic_v2.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ resource "azuredevops_serviceendpoint_generic_v2" "example" {
project_id = azuredevops_project.example.id
name = "Example Generic Service Endpoint"
description = "Managed by Terraform"
service_endpoint_type = "generic"
type = "generic"
server_url = "https://example.com"
authorization_scheme = "UsernamePassword"
authorization_parameters = {
Expand All @@ -33,17 +33,18 @@ resource "azuredevops_serviceendpoint_generic_v2" "example" {
}
}

# Token-based authentication
# Token-based authentication with validation enabled
resource "azuredevops_serviceendpoint_generic_v2" "token_example" {
project_id = azuredevops_project.example.id
name = "Token-based Service Endpoint"
description = "Managed by Terraform"
service_endpoint_type = "generic"
type = "generic"
server_url = "https://api.example.com"
authorization_scheme = "Token"
authorization_parameters = {
apitoken = "your-api-token"
}
validate_input = true

parameters = {
releaseUrl = "https://releases.example.com"
Expand All @@ -64,6 +65,7 @@ The following arguments are supported:
* `authorization_scheme` - (Required) The authorization scheme to use. Common values include "UsernamePassword", "Token", "OAuth", etc.
* `authorization_parameters` - (Optional) Map of key/value pairs for the specific authorization scheme. These often include sensitive data like tokens, usernames, and passwords.
* `parameters` - (Optional) Additional data associated with the service endpoint. This is a map of key/value pairs.
* `validate_input` - (Optional) Whether to validate the provided service endpoint configuration against the service endpoint type definition retrieved from Azure DevOps. When enabled, Terraform will verify that the `type`, `authorization_scheme`, `authorization_parameters`, and `parameters` match the expected schema for the service endpoint type. This helps catch configuration errors during plan/apply. Defaults to `false`.

## Attributes Reference

Expand Down
Loading