@@ -57,6 +57,28 @@ const (
57
57
defaultPlanVersion = "1.0.0"
58
58
)
59
59
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
+
60
82
var azureParams = []interface {}{
61
83
"arrayName" ,
62
84
"licenseKey" ,
@@ -407,43 +429,61 @@ func GetResourcesFromTemplateJson(data []byte) ([]string, error) {
407
429
return resources , nil
408
430
}
409
431
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 ) {
411
433
productSummary , err := GetProductSummary (ctx )
412
434
if err != nil {
413
435
return nil , err
414
436
}
415
437
416
438
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 ))
417
446
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 ))
419
449
continue
420
450
}
451
+ tflog .Debug (ctx , fmt .Sprintf ("Found matching plan %s" , * response_plan .PlanID ))
421
452
422
453
artifacts := make (map [string ]* appcatalog.Artifact )
423
- var plan * Plan
454
+ var templatePlan * Plan
424
455
for _ , response_artifact := range response_plan .Artifacts {
425
456
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
427
458
if * response_artifact .Name == "DefaultTemplate" {
428
459
template_data , err := downloadToBuffer (* response_artifact .URI )
429
460
if err != nil {
430
- return artifacts , err
461
+ return nil , err
431
462
}
432
463
433
- plan , err = GetPlanFromTemplateJson (template_data )
464
+ templatePlan , err = GetPlanFromTemplateJson (template_data )
434
465
if err != nil {
435
- return artifacts , err
466
+ return nil , err
436
467
}
437
468
}
438
469
}
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" )
442
476
}
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
443
484
}
444
485
}
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 )
447
487
}
448
488
449
489
func GetProductSummary (ctx context.Context ) (appcatalog.SearchClientGetResponse , error ) {
@@ -460,12 +500,8 @@ func GetResourceListFromUiDefinitionUrl(url string) ([]string, error) {
460
500
return GetResourcesFromTemplateJson (template_data )
461
501
}
462
502
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 )
469
505
if err != nil {
470
506
return nil , err
471
507
}
@@ -516,9 +552,17 @@ func getResourcesForCurrentDeployment(ctx context.Context, azureClient cloud.Azu
516
552
}
517
553
518
554
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 )
520
564
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 )
522
566
}
523
567
}
524
568
@@ -639,13 +683,17 @@ func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m int
639
683
640
684
parameters .Identity = expandIdentityObject (identities )
641
685
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
+
642
695
tagsMap := make (map [string ]interface {})
643
696
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
-
649
697
tags := v .(map [string ]interface {})
650
698
for _ , tag := range resources {
651
699
copyMap := make (map [string ]interface {})
@@ -657,11 +705,6 @@ func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m int
657
705
}
658
706
659
707
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
-
665
708
resource_tags := v .([]interface {})
666
709
for _ , resource_tag := range resource_tags {
667
710
resource := resource_tag .(map [string ]interface {})["resource" ].(string )
0 commit comments