Skip to content

Commit

Permalink
fhir - a state migration to work around the previously incorrect id c…
Browse files Browse the repository at this point in the history
…asing
  • Loading branch information
mbfrahry committed Dec 1, 2022
1 parent 566c357 commit e20b9ea
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 11 deletions.
6 changes: 6 additions & 0 deletions internal/services/healthcare/healthcare_fhir_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/hashicorp/terraform-provider-azurerm/helpers/azure"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tags"
Expand All @@ -37,6 +38,11 @@ func resourceHealthcareApisFhirService() *pluginsdk.Resource {
Delete: pluginsdk.DefaultTimeout(30 * time.Minute),
},

SchemaVersion: 1,
StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{
0: migration.HealthCareFhirV0ToV1{},
}),

Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
_, err := parse.FhirServiceID(id)
return err
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package migration

import (
"context"
"log"

"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

type HealthCareFhirV0ToV1 struct{}

func (s HealthCareFhirV0ToV1) Schema() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"resource_group_name": commonschema.ResourceGroupName(),

"workspace_id": {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
},

"location": commonschema.Location(),

"kind": {
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
},

"access_policy_object_ids": {
Type: pluginsdk.TypeSet,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"authentication": {
Type: pluginsdk.TypeList,
Required: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"authority": {
Type: pluginsdk.TypeString,
Required: true,
},

"audience": {
Type: pluginsdk.TypeString,
Required: true,
},

"smart_proxy_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
},
},
},
},

"identity": commonschema.SystemAssignedIdentityOptional(),

// can't use the registry ID due to the ID cannot be obtained when setting the property in state file
"container_registry_login_server_url": {
Type: pluginsdk.TypeSet,
Optional: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"oci_artifact": {
Type: pluginsdk.TypeList,
Optional: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"login_server": {
Type: pluginsdk.TypeString,
Required: true,
},

"image_name": {
Type: pluginsdk.TypeString,
Optional: true,
},

"digest": {
Type: pluginsdk.TypeString,
Optional: true,
},
},
},
},

"cors": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"allowed_origins": {
Type: pluginsdk.TypeSet,
Required: true,
MaxItems: 64,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"allowed_headers": {
Type: pluginsdk.TypeSet,
Required: true,
MaxItems: 64,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"allowed_methods": {
Type: pluginsdk.TypeSet,
Required: true,
MaxItems: 64,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
},

"max_age_in_seconds": {
Type: pluginsdk.TypeInt,
Optional: true,
},

"credentials_allowed": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
},
},
},
},

"configuration_export_storage_account_name": {
Type: pluginsdk.TypeString,
Optional: true,
},

"public_network_access_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"tags": commonschema.Tags(),
}
}

func (s HealthCareFhirV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc {
return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
oldId := rawState["id"].(string)
newId, err := parse.FhirServiceIDInsensitively(oldId)
if err != nil {
return nil, err
}

log.Printf("[DEBUG] Updating ID from %q to %q", oldId, newId)

rawState["id"] = newId.ID()
return rawState, nil
}
}
60 changes: 58 additions & 2 deletions internal/services/healthcare/parse/fhir_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (id FhirServiceId) String() string {
}

func (id FhirServiceId) ID() string {
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.HealthcareApis/workspaces/%s/fhirservices/%s"
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.HealthcareApis/workspaces/%s/fhirServices/%s"
return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.Name)
}

Expand All @@ -63,7 +63,63 @@ func FhirServiceID(input string) (*FhirServiceId, error) {
if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil {
return nil, err
}
if resourceId.Name, err = id.PopSegment("fhirservices"); err != nil {
if resourceId.Name, err = id.PopSegment("fhirServices"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &resourceId, nil
}

// FhirServiceIDInsensitively parses an FhirService ID into an FhirServiceId struct, insensitively
// This should only be used to parse an ID for rewriting, the FhirServiceID
// method should be used instead for validation etc.
//
// Whilst this may seem strange, this enables Terraform have consistent casing
// which works around issues in Core, whilst handling broken API responses.
func FhirServiceIDInsensitively(input string) (*FhirServiceId, error) {
id, err := resourceids.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

resourceId := FhirServiceId{
SubscriptionId: id.SubscriptionID,
ResourceGroup: id.ResourceGroup,
}

if resourceId.SubscriptionId == "" {
return nil, fmt.Errorf("ID was missing the 'subscriptions' element")
}

if resourceId.ResourceGroup == "" {
return nil, fmt.Errorf("ID was missing the 'resourceGroups' element")
}

// find the correct casing for the 'workspaces' segment
workspacesKey := "workspaces"
for key := range id.Path {
if strings.EqualFold(key, workspacesKey) {
workspacesKey = key
break
}
}
if resourceId.WorkspaceName, err = id.PopSegment(workspacesKey); err != nil {
return nil, err
}

// find the correct casing for the 'fhirServices' segment
fhirServicesKey := "fhirServices"
for key := range id.Path {
if strings.EqualFold(key, fhirServicesKey) {
fhirServicesKey = key
break
}
}
if resourceId.Name, err = id.PopSegment(fhirServicesKey); err != nil {
return nil, err
}

Expand Down
Loading

0 comments on commit e20b9ea

Please sign in to comment.