func schemaValidate()

in internal/services/resource.go [22:128]


func schemaValidate(config *AzapiResourceModel) error {
	if config == nil {
		return nil
	}

	azureResourceType, apiVersion, err := utils.GetAzureResourceTypeApiVersion(config.Type.ValueString())
	if err != nil {
		return fmt.Errorf(`the argument "type" is invalid: %s`, err.Error())
	}
	resourceDef, _ := azure.GetResourceDefinition(azureResourceType, apiVersion)

	log.Printf("[INFO] prepare validation for resource type: %s, api-version: %s", azureResourceType, apiVersion)
	versions := azure.GetApiVersions(azureResourceType)
	if len(versions) == 0 {
		return schemaValidationError(fmt.Sprintf("the argument \"type\" is invalid.\n resource type %s can't be found.\n", azureResourceType))
	}
	isVersionValid := false
	for _, version := range versions {
		if version == apiVersion {
			isVersionValid = true
			break
		}
	}
	if !isVersionValid {
		return schemaValidationError(fmt.Sprintf("the argument \"type\"'s api-version is invalid.\n The supported versions are [%s].\n", strings.Join(versions, ", ")))
	}

	if resourceDef == nil {
		return nil
	}

	var bodyToValidate attr.Value
	if !config.Body.IsNull() && !config.Body.IsUnknown() && !config.Body.IsNull() && !config.Body.IsUnderlyingValueUnknown() {
		if v, ok := config.Body.UnderlyingValue().(types.Object); ok {
			attributes := v.Attributes()
			attributeTypes := v.AttributeTypes(context.Background())

			attributes["name"] = config.Name
			attributeTypes["name"] = types.StringType

			if !config.Location.IsNull() {
				attributes["location"] = config.Location
				attributeTypes["location"] = types.StringType
			}

			if !config.Tags.IsNull() {
				attributes["tags"] = config.Tags
				attributeTypes["tags"] = types.MapType{ElemType: types.StringType}
			}

			if !config.Identity.IsNull() {
				identityAttributeTypes := map[string]attr.Type{
					"type": types.StringType,
				}
				identityModel := identity.FromList(config.Identity)
				if len(identityModel.IdentityIDs.Elements()) != 0 {
					identityAttributeTypes["userAssignedIdentities"] = types.MapType{ElemType: types.DynamicType}
					elements := make(map[string]attr.Value)
					identityIds := identityModel.IdentityIDs.Elements()
					for _, identityId := range identityIds {
						elements[identityId.(types.String).ValueString()] = types.DynamicNull()
					}
					attributes["identity"] = types.ObjectValueMust(identityAttributeTypes, map[string]attr.Value{
						"type":                   identityModel.Type,
						"userAssignedIdentities": types.MapValueMust(types.DynamicType, elements),
					})
				} else {
					attributes["identity"] = types.ObjectValueMust(identityAttributeTypes, map[string]attr.Value{
						"type": identityModel.Type,
					})
				}
				attributeTypes["identity"] = types.ObjectType{AttrTypes: identityAttributeTypes}
			}

			bodyToValidate = types.ObjectValueMust(attributeTypes, attributes)
		}
	} else {
		bodyToValidate = config.Body
	}

	bodyToValidate, err = dynamic.MergeDynamic(types.DynamicValue(bodyToValidate), config.SensitiveBody)
	if err != nil {
		return fmt.Errorf("failed to merge write-only body: %s", err)
	}

	validateErrors := (*resourceDef).Validate(bodyToValidate, "")

	errors := make([]error, 0)
	// skip error that location is not in the body, because user might use the default location feature
	// same for tags
	for _, err := range validateErrors {
		if strings.Contains(err.Error(), "`location` is required") || strings.Contains(err.Error(), "`tags` is required") {
			continue
		}
		errors = append(errors, err)
	}

	if len(errors) != 0 {
		errorMsg := "the argument \"body\" is invalid:\n"
		for _, err := range errors {
			errorMsg += fmt.Sprintf("%s\n", err.Error())
		}
		return schemaValidationError(errorMsg)
	}

	return nil
}