@@ -57,6 +57,28 @@ const (
5757 defaultPlanVersion = "1.0.0"
5858)
5959
60+ const marketplacePlanIdPrefix = "purestoragemarketplaceadmin.pure_cloud_block_store_product_deployment"
61+
62+ var staticResourceList = []string {
63+ "Microsoft.Solutions/applications" ,
64+ "Microsoft.Resources/tags" ,
65+ "Microsoft.Compute/virtualMachines" ,
66+ "Microsoft.Network/networkInterfaces" ,
67+ "Microsoft.DocumentDB/databaseAccounts" ,
68+ "Microsoft.Storage/storageAccounts" ,
69+ "Microsoft.ManagedIdentity/userAssignedIdentities" ,
70+ "Microsoft.KeyVault/vaults" ,
71+ "Microsoft.Network/loadBalancers" ,
72+ "Microsoft.Network/publicIPAddresses" ,
73+ "Microsoft.Compute/disks" ,
74+ "Microsoft.Compute/virtualMachines/extensions" ,
75+ "Microsoft.Network/networkSecurityGroups" ,
76+ "Microsoft.Network/applicationSecurityGroups" ,
77+ "Microsoft.Compute/capacityReservationGroups" ,
78+ "Microsoft.Compute/capacityReservationGroups/capacityReservations" ,
79+ "Microsoft.Resources/deploymentScripts" ,
80+ }
81+
6082var azureParams = []interface {}{
6183 "arrayName" ,
6284 "licenseKey" ,
@@ -407,43 +429,61 @@ func GetResourcesFromTemplateJson(data []byte) ([]string, error) {
407429 return resources , nil
408430}
409431
410- func GetPlanArtifacts (ctx context.Context , planName string ) (map [string ]* appcatalog.Artifact , error ) {
432+ func GetPlanArtifacts (ctx context.Context , plan Plan ) (map [string ]* appcatalog.Artifact , error ) {
411433 productSummary , err := GetProductSummary (ctx )
412434 if err != nil {
413435 return nil , err
414436 }
415437
416438 for _ , response_result := range productSummary .Results {
439+ // Filter out non Cloud Block Store offers, unfortunatelly the API does not have offer/product ID available so we need
440+ // to take a look at the inner plans to filter out offers we do not want
441+ if len (response_result .Plans ) == 0 || ! strings .HasPrefix (* response_result .Plans [0 ].UniquePlanID , marketplacePlanIdPrefix ) {
442+ tflog .Debug (ctx , fmt .Sprintf ("Skipping non Cloud Block Store offer %s" , * response_result .DisplayName ))
443+ continue
444+ }
445+ tflog .Debug (ctx , fmt .Sprintf ("Processing plans under offer %s" , * response_result .DisplayName ))
417446 for _ , response_plan := range response_result .Plans {
418- if ! strings .HasPrefix (* response_plan .PlanID , "cbs_azure" ) {
447+ if plan .Name != * response_plan .PlanID {
448+ tflog .Debug (ctx , fmt .Sprintf ("Skipping non matching plan %s" , * response_plan .PlanID ))
419449 continue
420450 }
451+ tflog .Debug (ctx , fmt .Sprintf ("Found matching plan %s" , * response_plan .PlanID ))
421452
422453 artifacts := make (map [string ]* appcatalog.Artifact )
423- var plan * Plan
454+ var templatePlan * Plan
424455 for _ , response_artifact := range response_plan .Artifacts {
425456 artifacts [* response_artifact .Name ] = response_artifact
426- // we get the current plan from Default Template
457+ // Get the plan properties from the DefaultTemplate artifact to verify integrity
427458 if * response_artifact .Name == "DefaultTemplate" {
428459 template_data , err := downloadToBuffer (* response_artifact .URI )
429460 if err != nil {
430- return artifacts , err
461+ return nil , err
431462 }
432463
433- plan , err = GetPlanFromTemplateJson (template_data )
464+ templatePlan , err = GetPlanFromTemplateJson (template_data )
434465 if err != nil {
435- return artifacts , err
466+ return nil , err
436467 }
437468 }
438469 }
439-
440- if plan .Name == planName {
441- return artifacts , nil
470+ // Verify the integrity of the artifact and that it matches our expectations
471+ if templatePlan .Name != plan .Name {
472+ return nil , fmt .Errorf ("mismatch between planID in marketplace DefaultTemplate" )
473+ }
474+ if templatePlan .Product != plan .Product {
475+ return nil , fmt .Errorf ("mismatch between product in marketplace response and DefaultTemplate" )
442476 }
477+ if templatePlan .Publisher != plan .Publisher {
478+ return nil , fmt .Errorf ("mismatch between publisher in marketplace response and DefaultTemplate" )
479+ }
480+ if templatePlan .Version != plan .Version {
481+ return nil , fmt .Errorf ("mismatch between plan version in marketplace response and DefaultTemplate" )
482+ }
483+ return artifacts , nil
443484 }
444485 }
445-
446- return nil , fmt .Errorf ("plan %s not found to get artifacts" , planName )
486+ return nil , fmt .Errorf ("could not find plan %s in marketplace in order to get Cloud Block Store artifacts" , plan .Name )
447487}
448488
449489func GetProductSummary (ctx context.Context ) (appcatalog.SearchClientGetResponse , error ) {
@@ -460,12 +500,8 @@ func GetResourceListFromUiDefinitionUrl(url string) ([]string, error) {
460500 return GetResourcesFromTemplateJson (template_data )
461501}
462502
463- func getPlanResources (ctx context.Context , p interface {}) ([]string , error ) {
464- planInterface := p .([]interface {})[0 ]
465- plan := planInterface .(map [string ]interface {})
466- planName := plan ["name" ].(string )
467-
468- artifacts , err := GetPlanArtifacts (ctx , planName )
503+ func getPlanResources (ctx context.Context , plan Plan ) ([]string , error ) {
504+ artifacts , err := GetPlanArtifacts (ctx , plan )
469505 if err != nil {
470506 return nil , err
471507 }
@@ -516,9 +552,17 @@ func getResourcesForCurrentDeployment(ctx context.Context, azureClient cloud.Azu
516552 }
517553
518554 if planParam , ok := d .GetOk ("plan" ); ok {
519- resources , err = getPlanResources (ctx , planParam )
555+ // We rely on Terraform framework here that this will always be convertible, otherwise we would panic
556+ planDecoded := planParam .([]interface {})[0 ].(map [string ]interface {})
557+ plan := Plan {
558+ Name : planDecoded ["name" ].(string ),
559+ Product : planDecoded ["product" ].(string ),
560+ Publisher : planDecoded ["publisher" ].(string ),
561+ Version : planDecoded ["version" ].(string ),
562+ }
563+ resources , err = getPlanResources (ctx , plan )
520564 if err != nil {
521- return nil , fmt .Errorf ("failed to retrieve resource list based on plan %+v" , err )
565+ return nil , fmt .Errorf ("could not get resource list from plan %s: % +v" , plan . Name , err )
522566 }
523567 }
524568
@@ -639,13 +683,17 @@ func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m int
639683
640684 parameters .Identity = expandIdentityObject (identities )
641685
686+ // Fetch the list of resources which will be deployed. We rely on createUiDefinition.json artifact which should
687+ // be available through the marketplace api in case of public plans. In case that the plan is not available in
688+ // the marketplace API for any reason we fall back to a statically defined list.
689+ resources , err := getResourcesForCurrentDeployment (ctx , azureClient , d )
690+ if err != nil {
691+ tflog .Warn (ctx , fmt .Sprintf ("Could not get resource list for current deployment, falling back to static resource list: %s" , err ))
692+ resources = staticResourceList
693+ }
694+
642695 tagsMap := make (map [string ]interface {})
643696 if v , ok := d .GetOk ("tags" ); ok {
644- resources , err := getResourcesForCurrentDeployment (ctx , azureClient , d )
645- if err != nil {
646- return diag .Errorf ("cannot get resource list %+v" , err )
647- }
648-
649697 tags := v .(map [string ]interface {})
650698 for _ , tag := range resources {
651699 copyMap := make (map [string ]interface {})
@@ -657,11 +705,6 @@ func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m int
657705 }
658706
659707 if v , ok := d .GetOk ("resource_tags" ); ok {
660- resources , err := getResourcesForCurrentDeployment (ctx , azureClient , d )
661- if err != nil {
662- return diag .Errorf ("cannot get resource list %+v" , err )
663- }
664-
665708 resource_tags := v .([]interface {})
666709 for _ , resource_tag := range resource_tags {
667710 resource := resource_tag .(map [string ]interface {})["resource" ].(string )
0 commit comments