diff --git a/CHANGELOG.md b/CHANGELOG.md index 6443c63..35972b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.10.0 (July 9, 2024) + +* Added support for tagging by resource type, refer to the [documentation](docs/resources/array_azure.md#nested-schema-for-resource_tags) +* Added support for specifying version plan tag when deploying array (e.g. `6.6.4` or `6.6.x`) + ## 0.9.0 (July 3, 2023) * Added support for PremiumV2 SSD with V20MP2R2 SKU, refer to the [documentation](docs/resources/array_azure.md) @@ -46,4 +51,4 @@ ## 0.1.0 (February 9, 2021) * Initial Release. Support for AWS CBS instances only. -* Create/Destroy, no updates yet. \ No newline at end of file +* Create/Destroy, no updates yet. diff --git a/Makefile b/Makefile index 50c4d24..5bb0b82 100644 --- a/Makefile +++ b/Makefile @@ -91,3 +91,4 @@ tidy: @go fix ./cbs @go clean ./cbs @go clean --tags mock ./cbs + diff --git a/cbs/acceptance/environment.go b/cbs/acceptance/environment.go index 44395c0..d1690a9 100644 --- a/cbs/acceptance/environment.go +++ b/cbs/acceptance/environment.go @@ -40,10 +40,6 @@ const ( // parameters file in json format EnvTfAccAzureParamsPath = "TEST_ACC_AZURE_PARAMS_PATH" - // Enviromment variable with path to the Fusion Storage Endpoint - // Collection Azure acceptance tests parameters file in json format - EnvTfAccFusionSECAzureParamsPath = "TEST_ACC_FUSION_SEC_AZURE_PARAMS_PATH" - // Environment variable with path to the AWS acceptance tests // parameters file in json format EnvTfAccAwsParamsPath = "TEST_ACC_AWS_PARAMS_PATH" diff --git a/cbs/acceptance/test_params.go b/cbs/acceptance/test_params.go index 0002e19..1c9a309 100644 --- a/cbs/acceptance/test_params.go +++ b/cbs/acceptance/test_params.go @@ -34,8 +34,8 @@ type AccTestCbsAzureParams struct { VirtualNetworkId string `json:"virtual_network_id"` JitGroup string `json:"jit_group"` JitGroupID string `json:"jit_group_id"` - FusionSECIdentity string `json:"fusion_sec_identity"` UserAssignedIdentity string `json:"user_assigned_identity"` + ResourceTags string `json:"resource_tags"` } type AccTestCbsFusionSECAzureParams struct { diff --git a/cbs/common.go b/cbs/common.go index 22a468c..49e68ae 100644 --- a/cbs/common.go +++ b/cbs/common.go @@ -139,6 +139,19 @@ func validateAzureManagedApplicationName(v interface{}, k string) (warnings []st return warnings, errors } +func validateVersionPrefixTag(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + pattern := `^\d+\.\d+(?:\.\d+|\.x)?$` + regexpPattern := regexp.MustCompile(pattern) + + if !regexpPattern.MatchString(value) { + errors = append(errors, fmt.Errorf("version prefix tag format not correct")) + } + + return warnings, errors +} + func validateAzureResourceGroupName(v interface{}, k string) (warnings []string, errors []error) { value := v.(string) diff --git a/cbs/data_azure_plans.go b/cbs/data_azure_plans.go index 536e19b..aa35c2f 100644 --- a/cbs/data_azure_plans.go +++ b/cbs/data_azure_plans.go @@ -29,7 +29,6 @@ import ( "strings" "time" - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/appcatalog" "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -67,6 +66,39 @@ func dataSourceAzurePlans() *schema.Resource { } } +func dataSourceCbsPlanAzure() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCbsPlanAzureRead, + Schema: map[string]*schema.Schema{ + "plan_version": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateVersionPrefixTag, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Elem: schema.TypeString, + }, + "version": { + Type: schema.TypeString, + Computed: true, + Elem: schema.TypeString, + }, + "publisher": { + Type: schema.TypeString, + Computed: true, + Elem: schema.TypeString, + }, + "product": { + Type: schema.TypeString, + Computed: true, + Elem: schema.TypeString, + }, + }, + } +} + type Plan struct { Name string Product string @@ -96,7 +128,7 @@ func (a PlanByVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] } var plan_name_regexp = regexp.MustCompile(`^[\w]+_([\d]+)_([\d]+)_([\d]+)$`) // Retrieve a plan information from Azure DefaultTemplate artifact -func getPlanFromTemplateJson(data []byte) (*Plan, error) { +func GetPlanFromTemplateJson(data []byte) (*Plan, error) { // Parse the default template var unmarshalled_data JSONDefaultTemplate err := json.Unmarshal(data, &unmarshalled_data) @@ -131,16 +163,14 @@ func versionPlans(plans []Plan) ([]VersionedPlan, error) { return versioned_plans, nil } -func queryMarketplaceForPlans(ctx context.Context) ([]VersionedPlan, error) { - - search_client := appcatalog.NewSearchClient() - response, err := search_client.Get(ctx, "en", "US", "terraform-cbs-provider", []appcatalog.SearchV2FieldName{"All"}, []string{"purestoragemarketplaceadmin"}) +func QueryMarketplaceForPlans(ctx context.Context) ([]VersionedPlan, error) { + productSummary, err := GetProductSummary(ctx) if err != nil { return nil, err } var template_plans []Plan - for _, response_result := range response.Results { + for _, response_result := range productSummary.Results { for _, response_plan := range response_result.Plans { if !strings.HasPrefix(*response_plan.PlanID, "cbs_azure") { // Exclude any plans which aren't the Pure Cloud Block Store on Azure offering. @@ -158,7 +188,7 @@ func queryMarketplaceForPlans(ctx context.Context) ([]VersionedPlan, error) { continue } - template_plan, err := getPlanFromTemplateJson(template_data) + template_plan, err := GetPlanFromTemplateJson(template_data) if err != nil { continue } @@ -174,7 +204,7 @@ func queryMarketplaceForPlans(ctx context.Context) ([]VersionedPlan, error) { func dataSourceAzurePlansRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { var diags diag.Diagnostics - versioned_plans, err := queryMarketplaceForPlans(ctx) + versioned_plans, err := QueryMarketplaceForPlans(ctx) if err != nil { return diag.FromErr(err) } @@ -201,3 +231,50 @@ func dataSourceAzurePlansRead(ctx context.Context, d *schema.ResourceData, m int return diags } + +func dataSourceCbsPlanAzureRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + + versioned_plans, err := QueryMarketplaceForPlans(ctx) + if err != nil { + return diag.FromErr(err) + } + + version_prefix_tag := d.Get("plan_version").(string) + if version_prefix_tag[len(version_prefix_tag)-1] == 'x' { + version_prefix_tag = version_prefix_tag[0 : len(version_prefix_tag)-1] + } + + set := false + for _, versioned_plan := range versioned_plans { + match := plan_name_regexp.FindStringSubmatch(versioned_plan.Plan.Name) + version_tag := fmt.Sprintf("%s.%s.%s", match[1], match[2], match[3]) + if strings.HasPrefix(version_tag, version_prefix_tag) { + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + err = d.Set("name", versioned_plan.Plan.Name) + if err != nil { + return diag.FromErr(err) + } + err = d.Set("product", versioned_plan.Plan.Product) + if err != nil { + return diag.FromErr(err) + } + err = d.Set("publisher", versioned_plan.Plan.Publisher) + if err != nil { + return diag.FromErr(err) + } + err = d.Set("version", versioned_plan.Plan.Version) + if err != nil { + return diag.FromErr(err) + } + set = true + break + } + } + + if !set { + return diag.FromErr(errors.New("Specific plan for provided version tag not found")) + } + + return diags +} diff --git a/cbs/data_azure_plans_test.go b/cbs/data_azure_plans_test.go index dc267f2..c146d9f 100644 --- a/cbs/data_azure_plans_test.go +++ b/cbs/data_azure_plans_test.go @@ -21,6 +21,7 @@ package cbs import ( "fmt" "os" + "regexp" "strconv" "testing" @@ -49,10 +50,35 @@ func TestAccDataAzurePlans(t *testing.T) { }) } +func TestAccDataCbsPlanAzure(t *testing.T) { + + if os.Getenv(acceptance.EnvTfAccAzureSkipMarketplace) != "" { + t.Skipf("Skipping acc test due to env variable '%s'", acceptance.EnvTfAccAzureSkipMarketplace) + } + + resource.ParallelTest(t, resource.TestCase{ + ProviderFactories: testAccProvidersFactory, + Steps: []resource.TestStep{ + { + Config: testAccAzureDataCbsPlanAzure(), + Check: resource.ComposeTestCheckFunc( + testAccDataCbsPlanAzure(), + ), + }, + }, + }) +} + func testAccAzureDataPlansConfig() string { return `data "cbs_azure_plans" "azure_plans" {}` } +func testAccAzureDataCbsPlanAzure() string { + return `data "cbs_plan_azure" "version_plan" { + plan_version = "6.6.x" + }` +} + func testAccDataAzurePlans() resource.TestCheckFunc { return func(s *terraform.State) error { data_resource := s.RootModule().Resources["data.cbs_azure_plans.azure_plans"] @@ -62,9 +88,11 @@ func testAccDataAzurePlans() resource.TestCheckFunc { if err != nil { return err } + if plans_size < 2 { return fmt.Errorf("Unexpected plans size: %d", plans_size) } + for i := 0; i < plans_size; i++ { // Check product version derivation from plan name makes sense. plan_name := data_resource.Primary.Attributes["plans."+strconv.Itoa(i)+".name"] @@ -101,3 +129,34 @@ func testAccDataAzurePlans() resource.TestCheckFunc { return nil } } + +func testAccDataCbsPlanAzure() resource.TestCheckFunc { + return func(s *terraform.State) error { + pattern := `^\d+\.\d+\.\d+$` + re := regexp.MustCompile(pattern) + data_resource := s.RootModule().Resources["data.cbs_plan_azure.version_plan"] + + plan_name := data_resource.Primary.Attributes["name"] + match := plan_name_regexp.FindStringSubmatch(plan_name) + version_tag := fmt.Sprintf("%s.%s.%s", match[1], match[2], match[3]) + if len(match) != 4 || !re.MatchString(version_tag) { + return fmt.Errorf("Incorrect plan name: %s", plan_name) + } + + plan_product := data_resource.Primary.Attributes["product"] + if plan_product != "pure_storage_cloud_block_store_deployment" { + return fmt.Errorf("Incorrect plan product : %s", plan_product) + } + + plan_publisher := data_resource.Primary.Attributes["publisher"] + if plan_publisher != "purestoragemarketplaceadmin" { + return fmt.Errorf("Incorrect plan publisher : %s", plan_publisher) + } + + plan_version := data_resource.Primary.Attributes["version"] + if !re.MatchString(plan_version) { + return fmt.Errorf("Incorrect plan version : %s", plan_version) + } + return nil + } +} diff --git a/cbs/internal/array/faclient/faclient_test.go b/cbs/internal/array/faclient/faclient_test.go deleted file mode 100644 index 7eacdd1..0000000 --- a/cbs/internal/array/faclient/faclient_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package faclient_test - -import ( - "testing" - - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/array/faclient" - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/array/faclient/auth" -) - -const testPrivateKey = ` ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAumcxoptOzGLwQuTAWZXovlc0KO79Zp/MYFlsF5XMWS8q2/ff -dpnknyfFNUIy6uEYf3kKHl1D15KuyGZJ2Opiv2nDc1XJAl4LWQU2YlliKrJ2yURq -oxRxQfoW0T/Zi1+v7wqyfxq6uYqUMxitBRRgstIGihTBGgjc7obP9mUp+3MH3NPx -qXT4CVsQDKlDWJC7pR0sZmeGc/YeDanD4iXl9ldk4j45ow8eaO6yeJgRx39hW7kT -NYYrDaWrlwVAt96OQHGmFmNyVIaikvm5tSid4nr3GKioyvU3a4aFIRK9s0iJ4Pa1 -Xkae0/M+LVnwqUzSPtOKB4ZOsxOkcG22QV5aQQIDAQABAoIBABSG4qdmdPH6/zxO -loQHBx9W2Q6c6tjPRiFiF44tV9iGYjDhBgG4hr2kEop/5L2K1jjLanrXiG4H4Cl/ -Yih5/y/XBMgBrWYOVy+RbGC+ORo8luopF5kn2iUK1lJqtpkri2NGiUuH9QITqahK -0lrZ2KA4krHIAU/NpA68V9BpaYsf+oUek43iZrZyjABp9sv4DOm1JeA61vUFTBPQ -euZCcxbdFo/5+9k2AAEr29o79fnLw8yeAckczke9Xpp++XAC8HQ9/ZjSeCo9NEEE -iAx7T1ahaT2LzRf5K/Uj3n4Fuo11XnMjqDyet46/yk821QB2H0epm+Z3iaMjSCYW -0mnCKbECgYEA6nnkIx5keh9BT5SdHHAKePCJBo/v7Ag6E2Q9yMN/QQu0t+wQZGne -+4P8Xts1arigWg85LOdrAdFq6DYuT3ja1JKwahQ/XlWYqpbLwuHnq3LvQm2pLxYA -gqnb7GPrfKm3bj10Cezy3Zv4EwpDRiXrVSEqlXNsVQV+Pnnusx/n7XUCgYEAy4Oa -VNK4Ey9nPeYL95de0fL4sI35EZNY9yX6n4pv+EOgvrsQSoWQb0JQLyV/Tmw064vb -70Z1bEHz8wWM2BjedqFuKkdzUpQTTZIXS5ozyxmAAS6h4MvBPSuFOO7yqhkkSKJo -McO61FteoR26LvRapt+x1r55iFtvCwkVm5FkJB0CgYA6w8vKhW53MOgkcsGhg+8L -+nTNITvnMvSjMYdOjriQ68ciJVbCY8pPzPduKpBLq/P8Pj59I46tCPg7NIEMx+RI -TG9MVsC++sLlVh/BOu7eCFMwmd1CAMil9r44k55MQxjG1z4C0tDXe6SD2RmdNhmx -3zsV87Sd5l+KdvK9D+0HlQKBgDjiKer3kvfZ0hOdD08/AgPQ0+4VYL6m3sEF3o1l -VnKgBHgLNTx/JKXUdTEYXAMBf7EuwGSa3wtJS/RrYrisCtJBwNcUbYlxVgvif5xk -F4H3OK4b6Kc6jGKanXwSXcVpjZi3vEPcn4XnnAWQl4+0QPpPoBeT2chhNiJxgZag -BsuJAoGBAIt/VO7U1Dea29weR3nk5nNblhKUAG5kcZ82iCUaOg5tPMy+E09ClaxF -6b85KZRT66rSlVFcPfNETD9ZEdwrtM/YTNmhsUf3KdW+p1IbHF3VwhHWRwrrSkyz -HYz83jsJVBLVMwmUltHmsGLKSGwpaAJHqHh7OOgka9bmiuGnSDD/ ------END RSA PRIVATE KEY-----` - -const testPublicKey = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAumcxoptOzGLwQuTAWZXo -vlc0KO79Zp/MYFlsF5XMWS8q2/ffdpnknyfFNUIy6uEYf3kKHl1D15KuyGZJ2Opi -v2nDc1XJAl4LWQU2YlliKrJ2yURqoxRxQfoW0T/Zi1+v7wqyfxq6uYqUMxitBRRg -stIGihTBGgjc7obP9mUp+3MH3NPxqXT4CVsQDKlDWJC7pR0sZmeGc/YeDanD4iXl -9ldk4j45ow8eaO6yeJgRx39hW7kTNYYrDaWrlwVAt96OQHGmFmNyVIaikvm5tSid -4nr3GKioyvU3a4aFIRK9s0iJ4Pa1Xkae0/M+LVnwqUzSPtOKB4ZOsxOkcG22QV5a -QQIDAQAB ------END PUBLIC KEY-----` - -type testParams struct { - host string - issuer string - clientID string - keyID string - privateKey string -} - -var params testParams - -// TODO use env vars to specify test params -func setup() { - params.host = "vm-ejacobs2" - params.issuer = "test-client" - params.clientID = "90b1fb8a-f15e-4a59-98ca-b220b53e7e10" - params.keyID = "aa837c07-d1c8-4c11-9ab6-c307aa913aca" - params.privateKey = testPrivateKey -} - -func TestAuth(t *testing.T) { - setup() - - tokenConfig := auth.TokenConfig{ - Issuer: params.issuer, - ClientID: params.clientID, - KeyID: params.keyID, - User: "pureuser", - PrivateKey: params.privateKey, - } - - _, err := faclient.New(params.host, tokenConfig) - if err != nil { - t.Error(err) - } -} diff --git a/cbs/internal/cloud/api.go b/cbs/internal/cloud/api.go index 9aeae9e..a66a065 100644 --- a/cbs/internal/cloud/api.go +++ b/cbs/internal/cloud/api.go @@ -22,6 +22,7 @@ import ( "context" "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/managedapplications" + "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/resources" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" vaultSecret "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/array" @@ -61,6 +62,8 @@ type AzureClientAPI interface { GroupsListComplete(ctx context.Context, filter string) (*[]graphrbac.ADGroup, error) AppsCreateOrUpdate(ctx context.Context, resourceGroupName string, applicationName string, parameters managedapplications.Application) error AppsGet(ctx context.Context, resourceGroupName string, applicationName string) (managedapplications.Application, error) + ResourcesGetByType(ctx context.Context, resourceType string, managedResourceGroup string) ([]resources.GenericResourceExpanded, error) + ResourceGet(ctx context.Context, resourceID string) (resources.GenericResource, error) AppsDelete(ctx context.Context, resourceGroupName string, applicationName string) error SecretSet(ctx context.Context, vaultId string, secretName string, parameters vaultSecret.SecretSetParameters) (vaultSecret.SecretBundle, error) SecretGet(ctx context.Context, vaultId string, secretName string, version string) (vaultSecret.SecretBundle, error) diff --git a/cbs/internal/cloud/azure.go b/cbs/internal/cloud/azure.go index 699ca98..1c1ff3a 100644 --- a/cbs/internal/cloud/azure.go +++ b/cbs/internal/cloud/azure.go @@ -43,6 +43,7 @@ import ( vaultManagement "github.com/Azure/azure-sdk-for-go/services/preview/keyvault/mgmt/2020-04-01-preview/keyvault" "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/managedapplications" + "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/resources" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/tracing" @@ -55,6 +56,7 @@ import ( ) const azureEnvironment = "public" +const resourceApiVersion = "2021-07-01" // Used to trace Azure API requests type tracerTransport struct { @@ -104,6 +106,7 @@ type azureClient struct { groupsClient graphrbac.GroupsClient vaultManagementClient vaultManagement.VaultsClient vaultSecretClient vaultSecret.BaseClient + resourceClient resources.Client } func buildAzureClient(ctx context.Context, userConfig AzureConfig) (AzureClientAPI, error) { @@ -171,11 +174,15 @@ func buildAzureClient(ctx context.Context, userConfig AzureConfig) (AzureClientA groupClient := graphrbac.NewGroupsClient(config.TenantID) groupClient.Authorizer = graphAuth + resourceClient := resources.NewClient(config.SubscriptionID) + resourceClient.Authorizer = auth + return &azureClient{ applicationsClient: applicationsClient, groupsClient: groupClient, vaultManagementClient: vaultManagementClient, vaultSecretClient: vaultSecretClient, + resourceClient: resourceClient, }, nil } @@ -208,6 +215,30 @@ func (azureClient *azureClient) AppsGet(ctx context.Context, resourceGroupName s return azureClient.applicationsClient.Get(ctx, resourceGroupName, applicationName) } +func (azureClient *azureClient) ResourceGet(ctx context.Context, resourceID string) (resources.GenericResource, error) { + return azureClient.resourceClient.GetByID(ctx, resourceID, resourceApiVersion) +} + +func (azureClient *azureClient) ResourcesGetByType(ctx context.Context, resourceType string, resourceGroupName string) ([]resources.GenericResourceExpanded, error) { + resList, err := azureClient.resourceClient.ListByResourceGroupComplete(context.Background(), resourceGroupName, "", "", nil) + if err != nil { + return nil, err + } + + resourceList := make([]resources.GenericResourceExpanded, 0) + for resList.NotDone() { + resource := resList.Value() + if *resource.Type == resourceType { + resourceList = append(resourceList, resource) + } + if resList.NextWithContext(context.TODO()) != nil { + break + } + } + + return resourceList, nil +} + func (azureClient *azureClient) AppsDelete(ctx context.Context, resourceGroupName string, applicationName string) error { future, err := azureClient.applicationsClient.Delete(ctx, resourceGroupName, applicationName) if err != nil { diff --git a/cbs/internal/cloud/mock_azure.go b/cbs/internal/cloud/mock_azure.go index 5dd5273..370b331 100644 --- a/cbs/internal/cloud/mock_azure.go +++ b/cbs/internal/cloud/mock_azure.go @@ -32,6 +32,7 @@ import ( "regexp" "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/managedapplications" + "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/resources" "github.com/Azure/azure-sdk-for-go/services/graphrbac/1.6/graphrbac" vaultSecret "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" "github.com/Azure/go-autorest/autorest" @@ -138,6 +139,14 @@ func (m *mockAzureClient) AppsGet(ctx context.Context, resourceGroupName string, return *app, nil } +func (m *mockAzureClient) ResourceGet(ctx context.Context, resourceID string) (resources.GenericResource, error) { + return resources.GenericResource{}, nil +} + +func (m *mockAzureClient) ResourcesGetByType(ctx context.Context, resourceType string, managedResourceGroup string) ([]resources.GenericResourceExpanded, error) { + return nil, nil +} + func (m *mockAzureClient) AppsDelete(ctx context.Context, resourceGroupName string, applicationName string) error { mockdb.AzureAppsDel(resourceGroupName, applicationName) return nil diff --git a/cbs/internal/service/version_mapper.go b/cbs/internal/service/version_mapper.go deleted file mode 100644 index 088d8d4..0000000 --- a/cbs/internal/service/version_mapper.go +++ /dev/null @@ -1,175 +0,0 @@ -/* - - Copyright 2021, Pure Storage Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -package service - -import ( - "encoding/json" - "fmt" - "net/http" - - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/version" - - "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/managedapplications" - "github.com/Azure/go-autorest/autorest/to" - goversion "github.com/hashicorp/go-version" -) - -const ( - // The version mapper only supports purity release versions starting at 6.1.6 - minPurityVersionString = "6.1.6" - - // TODO finalize public location and URL - versionConfigURL = "https://github.dev.purestorage.com/raw/FlashArray/terraform-provider-cbs-utils/master/version_map.json" -) - -var minPurityVersion *goversion.Version - -type VersionMapper interface { - GetCFTForVersion(purityVersionString string) (string, error) - GetManagedAppPlanForVersion(purityVersionString string) (*managedapplications.Plan, error) -} - -type awsDetails struct { - TemplateURL string `json:"template_url"` -} - -type azureDetails struct { - PlanName string `json:"plan_name"` - PlanProduct string `json:"plan_product"` - PlanPublisher string `json:"plan_publisher"` - PlanVersion string `json:"plan_version"` -} - -type versionDetails struct { - AWS awsDetails `json:"aws"` - Azure azureDetails `json:"azure"` - MinProviderVersion string `json:"min_provider_version"` - MaxProviderVersion string `json:"max_provider_version"` -} - -type versionDetailsMap map[string]versionDetails - -type versionConfig struct { - Versions versionDetailsMap `json:"purity_versions"` -} - -type versionMapperImpl struct { - providerVersion string - configURL string -} - -func init() { - var err error - if minPurityVersion, err = goversion.NewVersion(minPurityVersionString); err != nil { - panic(err) - } -} - -func NewVersionMapper() VersionMapper { - return &versionMapperImpl{ - providerVersion: version.ProviderVersion, - configURL: versionConfigURL, - } -} - -func (vm *versionMapperImpl) GetCFTForVersion(purityVersionString string) (string, error) { - details, err := vm.getAndValidateVersionDetails(purityVersionString) - if err != nil { - return "", err - } - return details.AWS.TemplateURL, nil -} - -func (vm *versionMapperImpl) GetManagedAppPlanForVersion(purityVersionString string) (*managedapplications.Plan, error) { - details, err := vm.getAndValidateVersionDetails(purityVersionString) - if err != nil { - return nil, err - } - plan := &managedapplications.Plan{ - Name: to.StringPtr(details.Azure.PlanName), - Product: to.StringPtr(details.Azure.PlanProduct), - Publisher: to.StringPtr(details.Azure.PlanPublisher), - Version: to.StringPtr(details.Azure.PlanVersion), - } - return plan, nil -} - -func (vm *versionMapperImpl) getAndValidateVersionDetails(purityVersionString string) (*versionDetails, error) { - - config, err := vm.retrieveVersionDetailsFile() - if err != nil { - return nil, err - } - - versionDetails, ok := config.Versions[purityVersionString] - if !ok { - purityVersion, err := goversion.NewVersion(purityVersionString) - - if err == nil && purityVersion.LessThan(minPurityVersion) { - return nil, fmt.Errorf("invalid version %s. The \"version\" parameter only supports purity versions %s and later", - purityVersion, minPurityVersion) - } else { - return nil, fmt.Errorf("invalid version %s", purityVersionString) - } - - } else if versionDetails.MaxProviderVersion != "" || versionDetails.MinProviderVersion != "" { - maxProviderVersionString := versionDetails.MaxProviderVersion - minProviderVersionString := versionDetails.MinProviderVersion - - providerVersion := goversion.Must(goversion.NewVersion(vm.providerVersion)) - - if maxProviderVersionString != "" { - maxProviderVersion := goversion.Must(goversion.NewVersion(maxProviderVersionString)) - - if providerVersion.GreaterThan(maxProviderVersion) { - return nil, fmt.Errorf("purity version %s is only supported by provider versions %s and earlier. Current provider version: %s", - purityVersionString, maxProviderVersionString, vm.providerVersion) - } - } - if minProviderVersionString != "" { - minProviderVersion := goversion.Must(goversion.NewVersion(minProviderVersionString)) - - if providerVersion.LessThan(minProviderVersion) { - return nil, fmt.Errorf("purity version %s is only supported by provider versions %s and later. Current provider version: %s", - purityVersionString, minProviderVersionString, vm.providerVersion) - } - } - } - - return &versionDetails, nil -} - -func (vm *versionMapperImpl) retrieveVersionDetailsFile() (*versionConfig, error) { - - res, err := http.Get(vm.configURL) - if err != nil { - return nil, fmt.Errorf("could not retrieve purity version configuration: %s", err) - } - defer res.Body.Close() - - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("received invalid status code %d when attempting to retrieve purity version configuration", res.StatusCode) - } - - var config versionConfig - if err := json.NewDecoder(res.Body).Decode(&config); err != nil { - panic(err) - } - return &config, nil -} diff --git a/cbs/internal/service/version_mapper_test.go b/cbs/internal/service/version_mapper_test.go deleted file mode 100644 index 291a38b..0000000 --- a/cbs/internal/service/version_mapper_test.go +++ /dev/null @@ -1,186 +0,0 @@ -/* - - Copyright 2021, Pure Storage Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -package service - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/version" - "github.com/stretchr/testify/assert" -) - -const testConfigDir = "../../../testing" -const testConfigURL = "http://localhost:%d/test_version_config.json" - -// Make sure that we can retrieve the real config file -func TestVersionMapper_retrieveRealConfig(t *testing.T) { - mapper := versionMapperImpl{ - providerVersion: version.ProviderVersion, - configURL: versionConfigURL, - } - if _, err := mapper.retrieveVersionDetailsFile(); err != nil { - t.Errorf("failed to retrieve config file from %s: %s", versionConfigURL, err) - } -} - -func launchServer(port int) *http.Server { - - server := http.Server{ - Handler: http.FileServer(http.Dir(testConfigDir)), - } - - l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - if err != nil { - panic(err) - } - - go server.Serve(l) - - return &server -} - -func TestVersionMapper_badRequest(t *testing.T) { - port := 8080 - server := launchServer(port) - defer server.Shutdown(context.Background()) - - tests := map[string]struct { - purityVersion string - providerVersion string - expectedError string - }{ - "purityVersionTooEarly": { - purityVersion: "6.1.0", - providerVersion: "0.5.0", - expectedError: "invalid version 6.1.0. The \"version\" parameter only supports purity versions 6.1.6 and later", - }, - "purityVersionNotFound": { - purityVersion: "6.2.0", - providerVersion: "0.5.0", - expectedError: "invalid version 6.2.0", - }, - "purityVersionInvalid": { - purityVersion: "foobar", - providerVersion: "0.5.0", - expectedError: "invalid version foobar", - }, - "providerVersionTooEarly": { - purityVersion: "6.1.7", - providerVersion: "0.5.0", - expectedError: "purity version 6.1.7 is only supported by provider versions 0.6.0 and later. Current provider version: 0.5.0", - }, - "providerVersionTooLate": { - purityVersion: "6.1.6", - providerVersion: "0.7.0", - expectedError: "purity version 6.1.6 is only supported by provider versions 0.6.0 and earlier. Current provider version: 0.7.0", - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - mapper := versionMapperImpl{ - providerVersion: test.providerVersion, - configURL: fmt.Sprintf(testConfigURL, port), - } - _, err := mapper.GetCFTForVersion(test.purityVersion) - assert.NotNil(t, err) - - if err.Error() != test.expectedError { - t.Errorf("expected error: %s, actual error: %s", test.expectedError, err.Error()) - } - }) - } -} - -func TestVersionMapper_badURL(t *testing.T) { - port := 8081 - server := launchServer(port) - defer server.Shutdown(context.Background()) - - invalidConfigURL := fmt.Sprintf("http://localhost:%d/test_version_config_invalid.json", port) - - mapper := versionMapperImpl{ - providerVersion: "0.5.0", - configURL: invalidConfigURL, - } - - _, err := mapper.GetCFTForVersion("6.1.6") - assert.NotNil(t, err) - - expectedError := "received invalid status code 404 when attempting to retrieve purity version configuration" - assert.EqualError(t, err, expectedError) -} - -func TestVersionMapper_success(t *testing.T) { - port := 8082 - server := launchServer(port) - defer server.Shutdown(context.Background()) - - tests := map[string]struct { - purityVersion string - providerVersion string - azurePlanVersion string - }{ - "test_616": { - purityVersion: "6.1.6", - providerVersion: "0.5.0", - azurePlanVersion: "1.0.0", - }, - "test_617": { - purityVersion: "6.1.7", - providerVersion: "0.7.0", - azurePlanVersion: "1.0.9", - }, - "test_618": { - purityVersion: "6.1.8", - providerVersion: "foo", //no min or max provider version for 6.1.8 test config, this should be able to be anything - azurePlanVersion: "1.0.5", - }, - } - - for name, test := range tests { - t.Run(name, func(t *testing.T) { - mapper := versionMapperImpl{ - providerVersion: test.providerVersion, - configURL: fmt.Sprintf(testConfigURL, port), - } - - // Test AWS - awsURL, err := mapper.GetCFTForVersion(test.purityVersion) - assert.Nil(t, err) - - expectedURL := fmt.Sprintf("aws_url_%s", test.purityVersion) - assert.Equal(t, expectedURL, awsURL) - - // Test Azure - plan, err := mapper.GetManagedAppPlanForVersion(test.purityVersion) - assert.Nil(t, err) - - assert.Equal(t, fmt.Sprintf("azure_plan_%s", test.purityVersion), *plan.Name) - assert.Equal(t, fmt.Sprintf("azure_product_%s", test.purityVersion), *plan.Product) - assert.Equal(t, fmt.Sprintf("azure_publisher_%s", test.purityVersion), *plan.Publisher) - assert.Equal(t, test.azurePlanVersion, *plan.Version) - - }) - } -} diff --git a/cbs/provider.go b/cbs/provider.go index 6b7aa4d..e0aeabb 100644 --- a/cbs/provider.go +++ b/cbs/provider.go @@ -96,12 +96,12 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "cbs_array_aws": resourceArrayAWS(), - "cbs_array_azure": resourceArrayAzure(), - "cbs_fusion_sec_azure": resourceFusionSECAzure(), + "cbs_array_aws": resourceArrayAWS(), + "cbs_array_azure": resourceArrayAzure(), }, DataSourcesMap: map[string]*schema.Resource{ "cbs_azure_plans": dataSourceAzurePlans(), + "cbs_plan_azure": dataSourceCbsPlanAzure(), }, ConfigureContextFunc: configureProvider, } diff --git a/cbs/provider_test.go b/cbs/provider_test.go index b99f7d2..4fbd7b8 100644 --- a/cbs/provider_test.go +++ b/cbs/provider_test.go @@ -238,7 +238,6 @@ func TestProvider_HasExpectedResources(t *testing.T) { expectedResources := []string{ "cbs_array_aws", "cbs_array_azure", - "cbs_fusion_sec_azure", } resources := testAccProvider.ResourcesMap diff --git a/cbs/resource_array_azure.go b/cbs/resource_array_azure.go index 5abe01a..ec7cb13 100644 --- a/cbs/resource_array_azure.go +++ b/cbs/resource_array_azure.go @@ -20,6 +20,8 @@ package cbs import ( "context" + "encoding/json" + "errors" "fmt" "log" @@ -30,6 +32,7 @@ import ( vaultSecret "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/auth" + "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/appcatalog" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/cloud" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/internal/tfazurerm" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/version" @@ -54,20 +57,6 @@ const ( defaultPlanVersion = "1.0.0" ) -var templateTags = []string{ - "Microsoft.Network/applicationSecurityGroups", - "Microsoft.DocumentDB/databaseAccounts", - "Microsoft.Compute/disks", - "Microsoft.KeyVault/vaults", - "Microsoft.Network/loadBalancers", - "Microsoft.ManagedIdentity/userAssignedIdentities", - "Microsoft.Compute/virtualMachines/extensions", - "Microsoft.Network/networkInterfaces", - "Microsoft.Network/networkSecurityGroups", - "Microsoft.Network/publicIPAddresses", - "Microsoft.Compute/virtualMachines", -} - var azureParams = []interface{}{ "arrayName", "licenseKey", @@ -219,12 +208,6 @@ func resourceArrayAzure() *schema.Resource { Required: true, }, - "fusion_sec_identity": { - Type: schema.TypeString, - Description: "Optional input that denotes the identity of a Fusion Storage Endpoint Collection, obtained during Azure Portal GUI or CLI deployment", - Optional: true, - }, - "jit_approval_group_object_ids": { Description: "This is a list of Azure group object IDs for people who are allowed to approve JIT requests", Required: true, @@ -274,6 +257,39 @@ func resourceArrayAzure() *schema.Resource { }, }, + "resource_tags": { + Description: "Optional field that defines specific tags for specific resource types", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "tag": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "value": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + }, + // Outputs "application_name": { Type: schema.TypeString, @@ -343,6 +359,198 @@ func resourceArrayAzure() *schema.Resource { return sch } +type JSONCreateUiDefinitionTemplate struct { + Parameters struct { + Steps []struct { + Name string + Elements []struct { + Name string + Resources []string + } + } + } +} + +// Retrieve resource list from Azure createUiDefinition artifact +func GetResourcesFromTemplateJson(data []byte) ([]string, error) { + // Parse the createUiDefinition template + var unmarshalled_data JSONCreateUiDefinitionTemplate + err := json.Unmarshal(data, &unmarshalled_data) + if err != nil { + return nil, err + } + + if len(unmarshalled_data.Parameters.Steps) == 0 { + return nil, fmt.Errorf("createUiDefinition resources is of unexpected size %d, it must have at least one element", len(unmarshalled_data.Parameters.Steps)) + } + + var resources []string + for _, v := range unmarshalled_data.Parameters.Steps { + if v.Name == "tags" { + if len(v.Elements) != 1 { + return nil, fmt.Errorf("createUiDefinition resources array is of unexpected size %d, it must have the size of 1", len(v.Elements)) + } + + if v.Elements[0].Name != "tags" { + return nil, fmt.Errorf("createUiDefinition resources has incorrect format to retrieve resource list") + } + + if len(v.Elements[0].Resources) == 0 { + return nil, fmt.Errorf("createUiDefinition resources must not be empty") + } + + resources = v.Elements[0].Resources + } + } + + return resources, nil +} + +func GetPlanArtifacts(ctx context.Context, planName string) (map[string]*appcatalog.Artifact, error) { + productSummary, err := GetProductSummary(ctx) + if err != nil { + return nil, err + } + + for _, response_result := range productSummary.Results { + for _, response_plan := range response_result.Plans { + if !strings.HasPrefix(*response_plan.PlanID, "cbs_azure") { + continue + } + + artifacts := make(map[string]*appcatalog.Artifact) + var plan *Plan + for _, response_artifact := range response_plan.Artifacts { + artifacts[*response_artifact.Name] = response_artifact + // we get the current plan from Default Template + if *response_artifact.Name == "DefaultTemplate" { + template_data, err := downloadToBuffer(*response_artifact.URI) + if err != nil { + return artifacts, err + } + + plan, err = GetPlanFromTemplateJson(template_data) + if err != nil { + return artifacts, err + } + } + } + + if plan.Name == planName { + return artifacts, nil + } + } + } + + return nil, fmt.Errorf("plan %s not found to get artifacts", planName) +} + +func GetProductSummary(ctx context.Context) (appcatalog.SearchClientGetResponse, error) { + search_client := appcatalog.NewSearchClient() + return search_client.Get(ctx, "en", "US", "terraform-cbs-provider", []appcatalog.SearchV2FieldName{"All"}, []string{"purestoragemarketplaceadmin"}) +} + +func GetResourceListFromUiDefinitionUrl(url string) ([]string, error) { + template_data, err := downloadToBuffer(url) + if err != nil { + return nil, err + } + + return GetResourcesFromTemplateJson(template_data) +} + +func getPlanResources(ctx context.Context, p interface{}) ([]string, error) { + planInterface := p.([]interface{})[0] + plan := planInterface.(map[string]interface{}) + planName := plan["name"].(string) + + artifacts, err := GetPlanArtifacts(ctx, planName) + if err != nil { + return nil, err + } + + return GetResourceListFromUiDefinitionUrl(*artifacts["createuidefinition"].URI) +} + +func getResourcesFromAppDefinitionId(ctx context.Context, azureClient cloud.AzureClientAPI, appDefinitionID string) ([]string, error) { + resource, err := azureClient.ResourceGet(ctx, appDefinitionID) + if err != nil { + return nil, err + } + + properties := resource.Properties.(map[string]interface{}) + artifacts := properties["artifacts"].([]interface{}) + var resources []string + for _, artifact := range artifacts { + details := artifact.(map[string]interface{}) + name := details["name"].(string) + if name == "ApplicationResourceTemplate" { + // to get createUiDefinition.json file we simply construct url by + // removing last resource number (32 bytes long) and replace it with createUiDefinition.json + uri := details["uri"].(string) + uri = uri[:len(uri)-32] + "createUiDefinition.json" + resources, err = GetResourceListFromUiDefinitionUrl(uri) + if err != nil { + return nil, errors.New("cannot get resource list from uidefinition url") + } + break + } + } + + if len(resources) == 0 { + return nil, errors.New("Resource list must not be empty") + } + + return resources, err +} + +func getResourcesForCurrentDeployment(ctx context.Context, azureClient cloud.AzureClientAPI, d *schema.ResourceData) ([]string, error) { + var resources []string + var err error + if appDefinitionId, ok := d.GetOk("app_definition_id"); ok { + resources, err = getResourcesFromAppDefinitionId(ctx, azureClient, appDefinitionId.(string)) + if err != nil { + return nil, fmt.Errorf("cannot get resource list from app_definition_id %s %+v", appDefinitionId.(string), err) + } + } + + if planParam, ok := d.GetOk("plan"); ok { + resources, err = getPlanResources(ctx, planParam) + if err != nil { + return nil, fmt.Errorf("failed to retrieve resource list based on plan %+v", err) + } + } + + if len(resources) == 0 { + return nil, fmt.Errorf("resource list cannot be empty") + } + + return resources, nil +} + +func SetResourceTags(resourceType string, resourceTagList []interface{}, resources []string, tagsMap *map[string]interface{}) error { + resourceExists := false + for _, v := range resources { + if v == resourceType { + resourceExists = true + } + } + if !resourceExists { + return fmt.Errorf("provided resource type %s not found", resourceType) + } + for _, tagPair := range resourceTagList { + tagMap := tagPair.(map[string]interface{}) + key := tagMap["name"].(string) + value := tagMap["value"].(string) + if _, ok := (*tagsMap)[resourceType]; !ok { + (*tagsMap)[resourceType] = make(map[string]interface{}) + } + (*tagsMap)[resourceType].(map[string]interface{})[key] = value + } + + return nil +} + func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m interface{}) (returnedDiags diag.Diagnostics) { tflog.Trace(ctx, "resourceArrayAzureCreate") azureClient, diags := m.(*CbsService).azureClientService(ctx) @@ -428,22 +636,45 @@ func resourceArrayAzureCreate(ctx context.Context, d *schema.ResourceData, m int return diag.Errorf("failed to retrieve user_assigned_identity") } - if v, ok := d.GetOk("fusion_sec_identity"); ok { - identities = append(identities, v.(string)) - setAppParameter("fusionSECIdentity", expandIdentityObject(identities[1:])) - } - parameters.Identity = expandIdentityObject(identities) + tagsMap := make(map[string]interface{}) if v, ok := d.GetOk("tags"); ok { + resources, err := getResourcesForCurrentDeployment(ctx, azureClient, d) + if err != nil { + return diag.Errorf("cannot get resource list %+v", err) + } + tags := v.(map[string]interface{}) - tagsMap := make(map[string]interface{}) - for _, tag := range templateTags { - tagsMap[tag] = tags + for _, tag := range resources { + copyMap := make(map[string]interface{}) + for key, value := range tags { + copyMap[key] = value + } + tagsMap[tag] = copyMap } - setAppParameter("tagsByResource", tagsMap) } + if v, ok := d.GetOk("resource_tags"); ok { + resources, err := getResourcesForCurrentDeployment(ctx, azureClient, d) + if err != nil { + return diag.Errorf("cannot get resource list %+v", err) + } + + resource_tags := v.([]interface{}) + for _, resource_tag := range resource_tags { + resource := resource_tag.(map[string]interface{})["resource"].(string) + resourceTagList := resource_tag.(map[string]interface{})["tag"].([]interface{}) + + err = SetResourceTags(resource, resourceTagList, resources, &tagsMap) + if err != nil { + return diag.Errorf("cannot set resource tags %+v", err) + } + } + } + + setAppParameter("tagsByResource", tagsMap) + err = prevalidateKeyVaultId(ctx, d, azureClient) if err != nil { return diag.FromErr(err) @@ -546,13 +777,6 @@ func resourceArrayAzureRead(ctx context.Context, d *schema.ResourceData, m inter recips := strings.Split(v.value.(string), ",") d.Set("alert_recipients", recips) } - if k == "tagsByResource" { - maps := v.value.(map[string]interface{}) - for _, tagValue := range maps { - d.Set("tags", tagValue) - break - } - } if azureParamSet.Contains(k) { if strings.HasSuffix(k, "Vnet") { vnetName = v.value.(string) @@ -561,7 +785,6 @@ func resourceArrayAzureRead(ctx context.Context, d *schema.ResourceData, m inter } else { d.Set(templateToTFParam(k, renamedAzureParams), v.value) } - } } } diff --git a/cbs/resource_array_azure_test.go b/cbs/resource_array_azure_test.go index 3b94baa..7016364 100644 --- a/cbs/resource_array_azure_test.go +++ b/cbs/resource_array_azure_test.go @@ -21,6 +21,7 @@ package cbs import ( "context" "encoding/json" + "errors" "fmt" "io/ioutil" "os" @@ -29,6 +30,7 @@ import ( "testing" "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/acceptance" + "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/internal/cloud" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -39,49 +41,20 @@ var azureParamsConfigure sync.Once func TestAccArrayAzure_basic(t *testing.T) { loadAccAzureParams(t) - - if os.Getenv(acceptance.EnvTfAccAzureSkipMarketplace) != "" { - t.Skipf("Skipping acc test due to env variable '%s'", acceptance.EnvTfAccAzureSkipMarketplace) + plans, err := QueryMarketplaceForPlans(context.TODO()) + if err != nil { + t.Error(err) } - arrayName := acctest.RandomWithPrefix(cbsAzureParam.ArrayName) - resourceName := "cbs_array_azure.test_array_azure" - orgDomain := "example.com" - orgDomain2 := "example-invalid-update.com" - - resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: testAccProvidersFactory, - CheckDestroy: testAccCheckArrayAzureDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAzureConfig(arrayName, orgDomain, false), - Check: resource.ComposeTestCheckFunc( - testAccArrayAzureExists(resourceName), - testAccCheckAzureBasicCheck(resourceName, arrayName, orgDomain), - testAccArrayAzureOptOutDefaultProtectionPolicy(resourceName), - ), - }, - { - Config: testAccAzureConfig(arrayName, orgDomain2, false), - ExpectError: regexp.MustCompile("Updates are not supported."), - Check: resource.ComposeTestCheckFunc( - testAccArrayAzureExists(resourceName), - testAccCheckAzureBasicCheck(resourceName, arrayName, orgDomain), - ), - }, - }, - }) -} - -func TestAccArrayAzure_basicFusion(t *testing.T) { - loadAccAzureParams(t) + // when tags are provided plan is needed to get plan specific + // list of resources to assign all tags to each resource + cbsAzureParam.PlanName = plans[0].Plan.Name + cbsAzureParam.PlanProduct = "pure_storage_cloud_block_store_deployment" + cbsAzureParam.PlanPublisher = "purestoragemarketplaceadmin" + cbsAzureParam.PlanVersion = plans[0].Plan.Version if os.Getenv(acceptance.EnvTfAccAzureSkipMarketplace) != "" { - t.Skipf("Skipping acc tests due to env variable '%s'", acceptance.EnvTfAccAzureSkipMarketplace) - } - - if cbsAzureParam.FusionSECIdentity == "" { - t.Skip("Skipping acc test as fusion_sec_identity is not set in the param file") + t.Skipf("Skipping acc test due to env variable '%s'", acceptance.EnvTfAccAzureSkipMarketplace) } arrayName := acctest.RandomWithPrefix(cbsAzureParam.ArrayName) @@ -94,15 +67,16 @@ func TestAccArrayAzure_basicFusion(t *testing.T) { CheckDestroy: testAccCheckArrayAzureDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureConfig(arrayName, orgDomain, true), + Config: testAccAzureConfig(arrayName, orgDomain), Check: resource.ComposeTestCheckFunc( testAccArrayAzureExists(resourceName), + testAccArrayTags(resourceName), testAccCheckAzureBasicCheck(resourceName, arrayName, orgDomain), testAccArrayAzureOptOutDefaultProtectionPolicy(resourceName), ), }, { - Config: testAccAzureConfig(arrayName, orgDomain2, true), + Config: testAccAzureConfig(arrayName, orgDomain2), ExpectError: regexp.MustCompile("Updates are not supported."), Check: resource.ComposeTestCheckFunc( testAccArrayAzureExists(resourceName), @@ -130,7 +104,7 @@ func TestAccArrayAzure_basicAppId(t *testing.T) { CheckDestroy: testAccCheckArrayAzureDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureConfigAppId(arrayName, orgDomain, false), + Config: testAccAzureConfigAppId(arrayName, orgDomain), Check: resource.ComposeTestCheckFunc( testAccArrayAzureExists(resourceName), testAccCheckAzureAppIdCheck(resourceName, arrayName, orgDomain), @@ -138,7 +112,7 @@ func TestAccArrayAzure_basicAppId(t *testing.T) { ), }, { - Config: testAccAzureConfigAppId(arrayName, orgDomain2, false), + Config: testAccAzureConfigAppId(arrayName, orgDomain2), ExpectError: regexp.MustCompile("Updates are not supported."), Check: resource.ComposeTestCheckFunc( testAccArrayAzureExists(resourceName), @@ -149,51 +123,157 @@ func TestAccArrayAzure_basicAppId(t *testing.T) { }) } -func TestAccArrayAzure_basicAppIdFusion(t *testing.T) { - loadAccAzureParams(t) +func TestGetResourcesFromTemplateJsonMalformedJson(t *testing.T) { + // case for incorrect json file + _, err := GetResourcesFromTemplateJson([]byte{'x'}) + if err == nil { + t.Errorf("incorrect json parsing must have failed") + } +} - if cbsAzureParam.AppDefinitionId == "" { - t.Skip("Skipping acc test due to app_definition_id is not set in the param file") +func TestGetResourcesFromTemplateJsonEmptyResources(t *testing.T) { + resource_json := []byte(`{ + "parameters": { + "steps": [ + { + "name": "network", + }, + { + "name": "tags_name", + "elements": [ + { + "name": "tags_name", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + ], + } + ] + }, + ], + } + }`) + + // resources not present + _, err := GetResourcesFromTemplateJson(resource_json) + if err == nil { + t.Errorf("Should have failed as resources are not found") } +} + +func TestGetResourcesFromTemplateIncorrectField(t *testing.T) { + var err error + resource_json := []byte(`{ + "parameters": { + "steps": [ + { + "name": "network", + }, + { + "name": "tags_name", + "elements": [ + { + "name": "tags_name", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + "Microsoft.Compute/virtualMachines", + "Microsoft.Network/networkInterfaces", + ], + } + ] + }, + ], + } + }`) - if os.Getenv(acceptance.EnvTfAccAzureSkipFusionAppId) != "" { - t.Skipf("Skipping acc tests due to env variable '%s'", acceptance.EnvTfAccAzureSkipFusionAppId) + //tags parameter not found + _, err = GetResourcesFromTemplateJson(resource_json) + if err == nil { + t.Errorf("should have failed as tags not found %+v", err) + return } - if cbsAzureParam.FusionSECIdentity == "" { - t.Skip("Skipping acc test as fusion_sec_identity is not set in the param file") + if err.Error() != "invalid character '}' looking for beginning of object key string" { + t.Errorf("should have failed for the error - 'invalid character '}' looking for beginning of object key string'") } +} - arrayName := acctest.RandomWithPrefix(cbsAzureParam.ArrayName) - resourceName := "cbs_array_azure.test_array_azure" - orgDomain := "example.com" - orgDomain2 := "example-invalid-update.com" +func TestSetResourceTagsResourceSuccess(t *testing.T) { + tagsMap := make(map[string]interface{}) + resource := "someResourceType" + resources := []string{"someResourceType", "someOtherResource"} + resourceTagList := []interface{}{map[string]interface{}{"name": "someTag", "value": "someTagValue"}} - resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: testAccProvidersFactory, - CheckDestroy: testAccCheckArrayAzureDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAzureConfigAppId(arrayName, orgDomain, true), - Check: resource.ComposeTestCheckFunc( - testAccArrayAzureExists(resourceName), - testAccCheckAzureAppIdCheck(resourceName, arrayName, orgDomain), - testAccArrayAzureOptOutDefaultProtectionPolicy(resourceName), - ), - }, - { - Config: testAccAzureConfigAppId(arrayName, orgDomain2, true), - ExpectError: regexp.MustCompile("Updates are not supported."), - Check: resource.ComposeTestCheckFunc( - testAccArrayAzureExists(resourceName), - testAccCheckAzureAppIdCheck(resourceName, arrayName, orgDomain), - ), - }, - }, - }) + err := SetResourceTags(resource, resourceTagList, resources, &tagsMap) + if err != nil { + t.Errorf("must have succeeded but have error %s", err.Error()) + } + + if _, ok := tagsMap[resource]; !ok { + t.Errorf("resource tag is not set") + } + + if val, ok := tagsMap[resource].(map[string]interface{})["someTag"]; !ok { + t.Errorf("tag not set for the resource") + } else { + if val.(string) != "someTagValue" { + t.Errorf("tag value not correct") + } + } } -func testAccAzureConfig(name string, orgDomain string, fusionArray bool) string { +func TestSetResourceTagsResourceNotFound(t *testing.T) { + tagsMap := make(map[string]interface{}) + resource := "someResourceType1" + resources := []string{"someResourceType", "someOtherResource"} + resourceTagList := []interface{}{map[string]string{"tag_name": "someTagName", "tag_value": "someTagValue"}} + + err := SetResourceTags(resource, resourceTagList, resources, &tagsMap) + if err == nil { + t.Errorf("must have failed with unknown resource type mssage") + } + + if err.Error() != "provided resource type someResourceType1 not found" { + t.Errorf("error must be: %s but was %s", "provided resource type someResourceType1 not found", err.Error()) + } +} + +func TestGetResourcesFromTemplateSuccess(t *testing.T) { + var resources []string + var err error + resource_json := []byte(`{ + "parameters": { + "steps": [ + { + "name": "network" + }, + { + "name": "tags", + "elements": [ + { + "name": "tags", + "type": "Microsoft.Common.TagsByResource", + "resources": [ + "Microsoft.Compute/virtualMachines", + "Microsoft.Network/networkInterfaces" + ] + } + ] + } + ] + } + }`) + + resources, err = GetResourcesFromTemplateJson(resource_json) + if err != nil { + t.Errorf("json parsing must have been successful %s", err.Error()) + } + + if len(resources) != 2 { + t.Errorf("unexpected number of resources %d", len(resources)) + } +} + +func testAccAzureConfig(name string, orgDomain string) string { planHCL := "" if cbsAzureParam.PlanName != "" || cbsAzureParam.PlanProduct != "" || @@ -214,9 +294,9 @@ func testAccAzureConfig(name string, orgDomain string, fusionArray bool) string ) } - fusionHCL := "" - if fusionArray { - fusionHCL = fmt.Sprintf(`fusion_sec_identity = "%s"`, cbsAzureParam.FusionSECIdentity) + resourceTags := "[]" + if cbsAzureParam.ResourceTags != "" { + resourceTags = cbsAzureParam.ResourceTags } return fmt.Sprintf(` @@ -238,31 +318,39 @@ func testAccAzureConfig(name string, orgDomain string, fusionArray bool) string array_model = "%[13]s" zone = 3 - %[14]s - jit_approval_group_object_ids = [ - "%[15]s", + "%[14]s", ] - %[16]s + %[15]s - user_assigned_identity = "%[17]s" + user_assigned_identity = "%[16]s" tags = { foo = "bar" - test = "value" + dada = "dudu" + some_tag_2 = "some_tag_2_value" + } + + dynamic "resource_tags" { + for_each = %[17]s + content { + resource = resource_tags.value.resource + dynamic "tag" { + for_each = resource_tags.value.tags + content { + name = tag.value.tag_name + value = tag.value.tag_value + } + } + } } }`, name, orgDomain, cbsAzureParam.ResourceGroupName, cbsAzureParam.LicenseKey, cbsAzureParam.PureuserPrivateKeyPath, cbsAzureParam.SystemSubnet, cbsAzureParam.ReplicationSubnet, cbsAzureParam.ISCSISubnet, cbsAzureParam.ManagementSubnet, cbsAzureParam.VirtualNetworkId, - cbsAzureParam.Location, cbsAzureParam.KeyvaultId, cbsAzureParam.ArrayModel, fusionHCL, cbsAzureParam.JitGroupID, planHCL, - cbsAzureParam.UserAssignedIdentity) + cbsAzureParam.Location, cbsAzureParam.KeyvaultId, cbsAzureParam.ArrayModel, cbsAzureParam.JitGroupID, planHCL, + cbsAzureParam.UserAssignedIdentity, resourceTags) } -func testAccAzureConfigAppId(name string, orgDomain string, fusionArray bool) string { - fusionHCL := "" - if fusionArray { - fusionHCL = fmt.Sprintf(`fusion_sec_identity = "%s"`, cbsAzureParam.FusionSECIdentity) - } - +func testAccAzureConfigAppId(name string, orgDomain string) string { return fmt.Sprintf(` resource "cbs_array_azure" "test_array_azure" { array_name = "%[1]s" @@ -282,18 +370,11 @@ func testAccAzureConfigAppId(name string, orgDomain string, fusionArray bool) st array_model = "%[13]s" zone = 3 - %[14]s - - app_definition_id = "%[15]s" - user_assigned_identity = "%[16]s" - - tags = { - foo = "bar" - test = "value" - } + app_definition_id = "%[14]s" + user_assigned_identity = "%[15]s" }`, name, orgDomain, cbsAzureParam.ResourceGroupName, cbsAzureParam.LicenseKey, cbsAzureParam.PureuserPrivateKeyPath, cbsAzureParam.SystemSubnet, cbsAzureParam.ReplicationSubnet, cbsAzureParam.ISCSISubnet, cbsAzureParam.ManagementSubnet, cbsAzureParam.VirtualNetworkId, - cbsAzureParam.Location, cbsAzureParam.KeyvaultId, cbsAzureParam.ArrayModel, fusionHCL, cbsAzureParam.AppDefinitionId, + cbsAzureParam.Location, cbsAzureParam.KeyvaultId, cbsAzureParam.ArrayModel, cbsAzureParam.AppDefinitionId, cbsAzureParam.UserAssignedIdentity) } @@ -341,6 +422,93 @@ func testAccCheckArrayAzureDestroy(s *terraform.State) error { return nil } +func checkVirtualMachineTags(ctx context.Context, api cloud.AzureClientAPI, managedResourceGroup string) error { + virtualMachines, err := api.ResourcesGetByType(ctx, "Microsoft.Compute/virtualMachines", managedResourceGroup) + if err != nil { + return err + } + + for _, virtualMachine := range virtualMachines { + if _, ok := virtualMachine.Tags["foo"]; !ok { + return errors.New("foo was not set in tags for Microsoft.Compute/virtualMachines") + } + + if _, ok := virtualMachine.Tags["some_tag_1"]; !ok { + return errors.New("some_tag_1 was not set in tags for Microsoft.Compute/virtualMachines") + } + + if _, ok := virtualMachine.Tags["some_tag_2"]; !ok { + return errors.New("some_tag_2 was not set in tags for Microsoft.Compute/virtualMachines") + } + + if *virtualMachine.Tags["some_tag_1"] != "some_tag_1_value" || + *virtualMachine.Tags["some_tag_2"] != "some_tag_2_value" || + *virtualMachine.Tags["foo"] != "bar" { + return errors.New("tags for Microsoft.Compute/virtualMachines not correct") + } + } + return nil +} + +func checkNetworkInterfacesTags(ctx context.Context, api cloud.AzureClientAPI, managedResourceGroup string) error { + disks, err := api.ResourcesGetByType(ctx, "Microsoft.Network/networkInterfaces", managedResourceGroup) + if err != nil { + return err + } + + for _, disk := range disks { + if _, ok := disk.Tags["foo"]; !ok { + return errors.New("foo was not set in tags for Microsoft.Network/networkInterfaces") + } + + if _, ok := disk.Tags["some_tag_2"]; !ok { + return errors.New("some_tag_2 was not set in tags for Microsoft.Network/networkInterfaces") + } + + if _, ok := disk.Tags["some_tag_3"]; !ok { + return errors.New("some_tag_3 was not set in tags for Microsoft.Network/networkInterfaces") + } + + if _, ok := disk.Tags["some_tag_4"]; !ok { + return errors.New("some_tag_4 was not set in tags for Microsoft.Network/networkInterfaces") + } + + if *disk.Tags["some_tag_3"] != "some_tag_3_value" || + *disk.Tags["some_tag_4"] != "some_tag_4_value" || + *disk.Tags["some_tag_2"] != "some_tag_2_value" || + *disk.Tags["foo"] != "bar" { + return errors.New("tags for Microsoft.Network/networkInterfaces not correct") + } + } + return nil +} + +// we want to verify that the global tags and the tags for specific resource types and are set +func testAccArrayTags(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Managed Application not found: %s", resourceName) + } + + ctx := context.Background() + azureClient, diags := testAccProvider.Meta().(*CbsService).azureClientService(ctx) + if diags.HasError() { + return fmt.Errorf("err: %+v", diags) + } + + if err := checkVirtualMachineTags(ctx, azureClient, rs.Primary.Attributes["managed_resource_group_name"]); err != nil { + return err + } + + if err := checkNetworkInterfacesTags(ctx, azureClient, rs.Primary.Attributes["managed_resource_group_name"]); err != nil { + return err + } + + return nil + } +} + func testAccArrayAzureExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -397,9 +565,6 @@ func testAccCheckAzureCommonAttrsCheck(resourceName string, arrayName string, or resource.TestCheckResourceAttr(resourceName, "log_sender_domain", orgDomain), resource.TestCheckResourceAttr(resourceName, "pureuser_private_key_path", cbsAzureParam.PureuserPrivateKeyPath), resource.TestCheckResourceAttr(resourceName, "resource_group_name", cbsAzureParam.ResourceGroupName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"), - resource.TestCheckResourceAttr(resourceName, "tags.test", "value"), resource.TestCheckResourceAttr(resourceName, "virtual_network_id", cbsAzureParam.VirtualNetworkId), resource.TestCheckResourceAttr(resourceName, "zone", "3"), resource.TestCheckResourceAttr(resourceName, "system_subnet", cbsAzureParam.SystemSubnet), diff --git a/cbs/resource_fusion_sec_azure.go b/cbs/resource_fusion_sec_azure.go deleted file mode 100644 index 63307ec..0000000 --- a/cbs/resource_fusion_sec_azure.go +++ /dev/null @@ -1,416 +0,0 @@ -/* - - Copyright 2021, Pure Storage Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -package cbs - -import ( - "context" - "fmt" - "log" - "strings" - "time" - - "github.com/Azure/azure-sdk-for-go/profiles/latest/resources/mgmt/managedapplications" - "github.com/Azure/go-autorest/autorest/to" - mapset "github.com/deckarep/golang-set" - - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -// Default managed application plan -const ( - defaultFusionSECPlanName = "pure_sec_1_0_0" - defaultFusionSECPlanProduct = "pure_fusion_storage_endpoint_collection" - defaultFusionSECPlanPublisher = "purestoragemarketplaceadmin" - defaultFusionSECPlanVersion = "1.0.3" -) - -var fusionSECAzureTemplateTags = []string{ - "Microsoft.Network/loadBalancers", - "Microsoft.ManagedIdentity/userAssignedIdentities", -} - -var fusionSECAzureParams = []interface{}{ - "fusionSECName", - "location", - "loadBalancerNetworkRg", - "loadBalancerNetworkName", - "loadBalancerSubnet", -} - -var renamedFusionSECAzureParams = map[string]string{} - -var fusionSECAzureTFOutputs = []string{ - "applicationName", - "managedResourceGroupName", - "hmvip0", - "hmvip1", - "loadBalancerFullIdentityId", -} - -func resourceFusionSECAzure() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceFusionSECAzureCreate, - ReadContext: resourceFusionSECAzureRead, - UpdateContext: resourceFusionSECAzureUpdate, - DeleteContext: resourceFusionSECAzureDelete, - Schema: map[string]*schema.Schema{ - "resource_group_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validateAzureResourceGroupName, - }, - - "location": { - Type: schema.TypeString, - Required: true, - }, - - // parameters - "fusion_sec_name": { - Description: "The name of the Fusion Storage Endpoint Collection (SEC). 0-59 alphanumeric characters only.", - Type: schema.TypeString, - Required: true, - ValidateFunc: validateAzureManagedApplicationName, - }, - - "load_balancer_network_rg": { - Type: schema.TypeString, - Required: true, - }, - - "load_balancer_network_name": { - Type: schema.TypeString, - Required: true, - }, - - "load_balancer_subnet": { - Type: schema.TypeString, - Required: true, - }, - - "jit_approval_group_object_ids": { - Description: "This is a list of Azure group object IDs for people who are allowed to approve JIT requests", - Required: true, - Type: schema.TypeList, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.IsUUID, - }, - }, - - "plan": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "product": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "publisher": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "version": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - }, - }, - - "tags": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - - // Outputs - "application_name": { - Type: schema.TypeString, - Computed: true, - }, - "managed_resource_group_name": { - Type: schema.TypeString, - Computed: true, - }, - "hmvip0": { - Type: schema.TypeString, - Computed: true, - }, - "hmvip1": { - Type: schema.TypeString, - Computed: true, - }, - "load_balancer_full_identity_id": { - Type: schema.TypeString, - Computed: true, - }, - }, - Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(30 * time.Minute), - Read: schema.DefaultTimeout(5 * time.Minute), - Delete: schema.DefaultTimeout(30 * time.Minute), - }, - } -} - -func resourceFusionSECAzureCreate(ctx context.Context, d *schema.ResourceData, m interface{}) (returnedDiags diag.Diagnostics) { - tflog.Trace(ctx, "resourceFusionSECAzurereate") - azureClient, diags := m.(*CbsService).azureClientService(ctx) - if diags.HasError() { - return diags - } - - name := d.Get("fusion_sec_name").(string) - managedResourceGroup := toAzureManagedResourceGroup(name) - resourceGroupName := d.Get("resource_group_name").(string) - - if d.IsNewResource() { - existing, err := azureClient.AppsGet(ctx, resourceGroupName, name) - if err != nil { - if !responseWasNotFound(existing.Response) { - return diag.Errorf("failed to check for presence of existing Managed Application Name %q (Resource Group %q): %+v", name, resourceGroupName, err) - } - } - if existing.ID != nil && *existing.ID != "" { - return diag.Errorf( - "A resource with the name %q, Resource Group %q and ID %q already exists - to be managed via Terraform this resource needs to be imported into the State.", - name, - resourceGroupName, - *existing.ID, - ) - } - } - - parameters := managedapplications.Application{ - Location: to.StringPtr(d.Get("location").(string)), - } - - targetResourceGroupId := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", azureClient.SubscriptionID(), managedResourceGroup) - parameters.ApplicationProperties = &managedapplications.ApplicationProperties{ - ManagedResourceGroupID: to.StringPtr(targetResourceGroupId), - } - - parameters.Kind = to.StringPtr("MarketPlace") - if v, ok1 := d.GetOk("plan"); ok1 && len(v.([]interface{})) > 0 { - parameters.Plan = expandPlan(v.([]interface{})) - } else { - parameters.Plan = &managedapplications.Plan{ - Name: to.StringPtr(defaultFusionSECPlanName), - Product: to.StringPtr(defaultFusionSECPlanPublisher), - Publisher: to.StringPtr(defaultFusionSECPlanPublisher), - Version: to.StringPtr(defaultFusionSECPlanVersion), - } - } - - parameters.Parameters = make(map[string]interface{}) - setAppParameter := func(key string, value interface{}) { - (parameters.Parameters.(map[string]interface{}))[key] = map[string]interface{}{"value": value} - } - for _, value := range fusionSECAzureParams { - valueStr := value.(string) - setAppParameter(valueStr, d.Get(templateToTFParam(valueStr, renamedFusionSECAzureParams))) - } - - returnedDiags = setAzureJitAccessPolicy(¶meters, d) - - if v, ok := d.GetOk("tags"); ok { - tags := v.(map[string]interface{}) - tagsMap := make(map[string]interface{}) - for _, tag := range fusionSECAzureTemplateTags { - tagsMap[tag] = tags - } - setAppParameter("tagsByResource", tagsMap) - } - - // Error out now, before we create resources - if returnedDiags.HasError() { - return returnedDiags - } - - tflog.Trace(ctx, "resourceFusionSECAzureCreate AppsCreateOrUpdate") - err := azureClient.AppsCreateOrUpdate(ctx, resourceGroupName, name, parameters) - defer func() { - if returnedDiags.HasError() { - if err = azureClient.AppsDelete(ctx, resourceGroupName, name); err != nil { - tflog.Error( - ctx, - fmt.Sprintf( - "failed to delete Managed Application %q (Resource Group %q) after failed CreateOrUpdate operation: %+v", - name, - resourceGroupName, - err, - ), - ) - } - } - }() - if err != nil { - return diag.FromErr(err) - } - - resp, err := azureClient.AppsGet(ctx, resourceGroupName, name) - if err != nil { - return diag.Errorf("failed to retrieve Managed Application %q (Resource Group %q): %+v", name, resourceGroupName, err) - } - if resp.ID == nil || *resp.ID == "" { - return diag.Errorf("cannot read Managed Application %q (Resource Group %q) ID", name, resourceGroupName) - } - d.SetId(*resp.ID) - - diags = resourceFusionSECAzureRead(ctx, d, m) - if diags.HasError() { - returnedDiags = append(returnedDiags, diags...) - } - - return returnedDiags -} - -func resourceFusionSECAzureRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Trace(ctx, "resourceFusionSECAzureRead") - azureClient, diags := m.(*CbsService).azureClientService(ctx) - if diags.HasError() { - return diags - } - - v, ok := d.GetOk("fusion_sec_name") - if !ok { - log.Printf("[WARN] No Managed Application found with Id %q, removing from state", d.Id()) - d.SetId("") - return nil - } - appName := v.(string) - managedResourceGroup := toAzureManagedResourceGroup(appName) - - resourceGroup := d.Get("resource_group_name").(string) - resp, err := azureClient.AppsGet(ctx, resourceGroup, appName) - if err != nil { - if responseWasNotFound(resp.Response) { - log.Printf("[WARN] Managed Application %q does not exist - removing from state", d.Id()) - d.SetId("") - return nil - } - return diag.Errorf("failed to read Managed Application %q (Resource Group %q): %+v", appName, resourceGroup, err) - } - - if err := d.Set("application_name", appName); err != nil { - return diag.FromErr(err) - } - if err := d.Set("managed_resource_group_name", managedResourceGroup); err != nil { - return diag.FromErr(err) - } - if err := d.Set("resource_group_name", resourceGroup); err != nil { - return diag.FromErr(err) - } - if err := d.Set("location", resp.Location); err != nil { - return diag.FromErr(err) - } - - if props := resp.ApplicationProperties; props != nil { - params := formatAzureParameters(props.Parameters) - fusionSECParamSet := mapset.NewSetFromSlice(fusionSECAzureParams) - for k, v := range params { - // SecureString parameters will always have a null value, so ignore them - if v.valType != "SecureString" { - if k == "tagsByResource" { - maps := v.value.(map[string]interface{}) - for _, tagValue := range maps { - if err := d.Set("tags", tagValue); err != nil { - return diag.FromErr(err) - } - break - } - } - if fusionSECParamSet.Contains(k) { - if err := d.Set(templateToTFParam(k, renamedFusionSECAzureParams), v.value); err != nil { - return diag.FromErr(err) - } - } - } - } - - if err := d.Set("jit_approval_group_object_ids", flattenAzureJitApprovalGroupIds(props.JitAccessPolicy)); err != nil { - return diag.FromErr(err) - } - - outputs := props.Outputs.(map[string]interface{}) - - fusionSECAzureTFOutputSet := mapset.NewSet() - for _, s := range fusionSECAzureTFOutputs { - fusionSECAzureTFOutputSet.Add(s) - } - - for k, v := range outputs { - if v != nil { - v := v.(map[string]interface{}) - if fusionSECAzureTFOutputSet.Contains(k) { - if !strings.HasPrefix(k, "hmvip") { - k = toSnake(k) - } - if err := d.Set(k, v["value"]); err != nil { - return diag.FromErr(err) - } - } - } - } - - } - - return nil -} - -func resourceFusionSECAzureUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Trace(ctx, "resourceFusionSECAzureUpdate") - diags := resourceArrayAzureRead(ctx, d, m) - if diags.HasError() { - return diags - } - return diag.Errorf("Updates are not supported.") -} - -func resourceFusionSECAzureDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Trace(ctx, "resourceFusionSECAzureDelete") - azureClient, diags := m.(*CbsService).azureClientService(ctx) - if diags.HasError() { - return diags - } - - resourceGroup := d.Get("resource_group_name").(string) - appName := d.Get("fusion_sec_name").(string) - - err := azureClient.AppsDelete(ctx, resourceGroup, appName) - return diag.FromErr(err) -} diff --git a/cbs/resource_fusion_sec_azure_test.go b/cbs/resource_fusion_sec_azure_test.go deleted file mode 100644 index cfdd4a2..0000000 --- a/cbs/resource_fusion_sec_azure_test.go +++ /dev/null @@ -1,225 +0,0 @@ -/* - - Copyright 2021, Pure Storage Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -package cbs - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "regexp" - "sync" - "testing" - - "github.com/PureStorage-OpenConnect/terraform-provider-cbs/cbs/acceptance" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -var cbsFusionSECAzureParam acceptance.AccTestCbsFusionSECAzureParams -var fusionSECAzureParamsConfigure sync.Once - -func TestAccFusionSECAzure_basic(t *testing.T) { - loadAccFusionSECAzureParams(t) - - if os.Getenv(acceptance.EnvTfAccSkipFusionSECAzure) != "" { - t.Skipf("Skipping acc test due to env variable '%s'", acceptance.EnvTfAccSkipFusionSECAzure) - } - - fusionSECName := acctest.RandomWithPrefix(cbsFusionSECAzureParam.FusionSECName) - resourceName := "cbs_fusion_sec_azure.test_fusion_sec_azure" - - resource.ParallelTest(t, resource.TestCase{ - ProviderFactories: testAccProvidersFactory, - CheckDestroy: testAccCheckFusionSECAzureDestroy, - Steps: []resource.TestStep{ - { - Config: testAccFusionSECAzureConfig(fusionSECName, false), - Check: resource.ComposeTestCheckFunc( - testAccFusionSECAzureExists(resourceName), - testAccCheckFusionSECAzureBasicCheck(resourceName, fusionSECName), - ), - }, - { - Config: testAccFusionSECAzureConfig(fusionSECName, true), - ExpectError: regexp.MustCompile("Updates are not supported."), - Check: resource.ComposeTestCheckFunc( - testAccFusionSECAzureExists(resourceName), - testAccCheckFusionSECAzureBasicCheck(resourceName, fusionSECName), - ), - }, - }, - }) -} - -func testAccFusionSECAzureConfig(name string, update bool) string { - planHCL := "" - if cbsFusionSECAzureParam.PlanName != "" || - cbsFusionSECAzureParam.PlanProduct != "" || - cbsFusionSECAzureParam.PlanPublisher != "" || - cbsFusionSECAzureParam.PlanVersion != "" { - planHCL = fmt.Sprintf(` - plan { - name = "%s" - product = "%s" - publisher = "%s" - version = "%s" - } - `, - cbsFusionSECAzureParam.PlanName, - cbsFusionSECAzureParam.PlanProduct, - cbsFusionSECAzureParam.PlanPublisher, - cbsFusionSECAzureParam.PlanVersion, - ) - } - - if update { - name = fmt.Sprintf("%s-update", name) - } - - return fmt.Sprintf(` - resource "cbs_fusion_sec_azure" "test_fusion_sec_azure" { - fusion_sec_name = "%[1]s" - resource_group_name = "%[2]s" - load_balancer_network_rg = "%[3]s" - load_balancer_network_name = "%[4]s" - load_balancer_subnet = "%[5]s" - location = "%[6]s" - - jit_approval_group_object_ids = [ - "%[7]s", - ] - - %[8]s - - tags = { - foo = "bar" - test = "value" - } - }`, name, cbsFusionSECAzureParam.ResourceGroupName, - cbsFusionSECAzureParam.LoadBalancerNetworkRg, cbsFusionSECAzureParam.LoadBalancerNetworkName, cbsFusionSECAzureParam.LoadBalancerSubnet, - cbsFusionSECAzureParam.Location, cbsFusionSECAzureParam.JitGroupID, planHCL) -} - -// Lazy load the Fusion SEC Azure param values from the json file specified at TEST_ACC_FUSION_SEC_AZURE_PARAMS_PATH. -func loadAccFusionSECAzureParams(t *testing.T) { - fusionSECAzureParamsConfigure.Do(func() { - cfgPath := os.Getenv(acceptance.EnvTfAccFusionSECAzureParamsPath) - if cfgPath == "" { - t.Fatalf("%s environment variable must be set for acceptance testing", acceptance.EnvTfAccFusionSECAzureParamsPath) - } - cfgData, err := ioutil.ReadFile(cfgPath) - if err != nil { - t.Fatalf("Error reading azure config JSON file: %s", err) - } - if err := json.Unmarshal(cfgData, &cbsFusionSECAzureParam); err != nil { - t.Fatalf("Error unmarshaling JSON file: %s", err) - } - }) -} - -func testAccCheckFusionSECAzureDestroy(s *terraform.State) error { - azureClient, diags := testAccProvider.Meta().(*CbsService).azureClientService(context.TODO()) - if diags.HasError() { - return fmt.Errorf("err: %+v", diags) - } - ctx := context.Background() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "cbs_fusion_sec_azure" { - continue - } - - appName := rs.Primary.Attributes["fusion_sec_name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] - resp, err := azureClient.AppsGet(ctx, resourceGroup, appName) - if err != nil { - if responseWasNotFound(resp.Response) { - return nil - } - return err - } - return fmt.Errorf("Managed Application still exists:\n%#v", resp) - } - - return nil -} - -func testAccFusionSECAzureExists(resourceName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Managed Application not found: %s", resourceName) - } - - ctx := context.Background() - azureClient, diags := testAccProvider.Meta().(*CbsService).azureClientService(context.TODO()) - if diags.HasError() { - return fmt.Errorf("err: %+v", diags) - } - - appName := rs.Primary.Attributes["fusion_sec_name"] - resourceGroup := rs.Primary.Attributes["resource_group_name"] - - if resp, err := azureClient.AppsGet(ctx, resourceGroup, appName); err != nil { - if responseWasNotFound(resp.Response) { - return fmt.Errorf("Managed Application %q (Resource Group %q) does not exist", appName, resourceGroup) - } - return err - } - - return nil - } -} - -func testAccCheckFusionSECAzureBasicCheck(resourceName string, fusionSECName string) resource.TestCheckFunc { - return resource.ComposeAggregateTestCheckFunc( - testAccCheckFusionSECAzureCommonAttrsCheck(resourceName, fusionSECName), - resource.TestCheckResourceAttr(resourceName, "jit_approval_group_object_ids.#", "1"), - resource.TestCheckResourceAttr(resourceName, "jit_approval_group_object_ids.0", cbsFusionSECAzureParam.JitGroupID), - testAccCheckFusionSECAzureOutputSetCheck(resourceName), - ) -} - -func testAccCheckFusionSECAzureCommonAttrsCheck(resourceName string, fusionSECName string) resource.TestCheckFunc { - return resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "fusion_sec_name", fusionSECName), - resource.TestCheckResourceAttr(resourceName, "location", cbsFusionSECAzureParam.Location), - resource.TestCheckResourceAttr(resourceName, "resource_group_name", cbsFusionSECAzureParam.ResourceGroupName), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.foo", "bar"), - resource.TestCheckResourceAttr(resourceName, "tags.test", "value"), - resource.TestCheckResourceAttr(resourceName, "load_balancer_network_rg", cbsFusionSECAzureParam.LoadBalancerNetworkRg), - resource.TestCheckResourceAttr(resourceName, "load_balancer_network_name", cbsFusionSECAzureParam.LoadBalancerNetworkName), - resource.TestCheckResourceAttr(resourceName, "load_balancer_subnet", cbsFusionSECAzureParam.LoadBalancerSubnet), - ) -} - -// Check that all the output parameters were set -func testAccCheckFusionSECAzureOutputSetCheck(resourceName string) resource.TestCheckFunc { - return resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "application_name"), - resource.TestCheckResourceAttrSet(resourceName, "managed_resource_group_name"), - resource.TestCheckResourceAttrSet(resourceName, "hmvip0"), - resource.TestCheckResourceAttrSet(resourceName, "hmvip1"), - resource.TestCheckResourceAttrSet(resourceName, "load_balancer_full_identity_id"), - ) -} diff --git a/docs/data-sources/azure_plans.md b/docs/data-sources/azure_plans.md index 0bf9042..859fce5 100644 --- a/docs/data-sources/azure_plans.md +++ b/docs/data-sources/azure_plans.md @@ -1,27 +1,29 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "cbs_azure_plans Data Source - terraform-provider-cbs" +page_title: "cbs_plan_azure Data Source - terraform-provider-cbs" subcategory: "" description: |- --- -# cbs_azure_plans (Data Source) +# cbs_plan_azure (Data Source) -Provides a list of all Azure plans available in the Marketplace. It is sorted by the version, the latest being on top. +Provides plan specific details specified by plan version parameter (or version prefix e.g. `6.6.4` or `6.6.x`) ## Example Usage ```hcl -data "cbs_azure_plans" "azure_plans" {} +data "cbs_plan_azure" "66x_latest_plan" { + plan_version = "6.6.x" +} resource "cbs_array_azure" "azure_instance" { (...) plan { - name = data.cbs_azure_plans.azure_plans.plans[0].name - product = data.cbs_azure_plans.azure_plans.plans[0].product - publisher = data.cbs_azure_plans.azure_plans.plans[0].publisher - version = data.cbs_azure_plans.azure_plans.plans[0].version + name = data.cbs_plan_azure.66x_latest_plan.name + product = data.cbs_plan_azure.66x_latest_plan.product + publisher = data.cbs_plan_azure.66x_latest_plan.publisher + version = data.cbs_plan_azure.66x_latest_plan.version } lifecycle { @@ -32,7 +34,7 @@ resource "cbs_array_azure" "azure_instance" { } output "cbs_azure_available_plans" { - value = data.cbs_azure_plans.azure_plans.plans + value = data.cbs_plan_azure.66x_latest_plan } ``` @@ -42,14 +44,7 @@ output "cbs_azure_available_plans" { ### Read-Only - `id` (String) The ID of this resource. -- `plans` (List of Object) (see [below for nested schema](#nestedatt--plans)) - - -### Nested Schema for `plans` - -Read-Only: - - `name` (String) - `product` (String) - `publisher` (String) -- `version` (String) +- `version` (String) \ No newline at end of file diff --git a/docs/resources/array_aws.md b/docs/resources/array_aws.md index c438d4e..bf72ad5 100644 --- a/docs/resources/array_aws.md +++ b/docs/resources/array_aws.md @@ -27,7 +27,7 @@ resource "cbs_array_aws" "cbs_example" { array_name = "terraform-example-instance" - deployment_template_url = "https://s3.amazonaws.com/awsmp-fulfillment-cf-templates-prod/4ea2905b-7939-4ee0-a521-d5c2fcb41214.e1e81a59-5e4c-4400-9675-85361e830022.template" + deployment_template_url = "https://s3.amazonaws.com/awsmp-fulfillment-cf-templates-prod/4ea2905b-7939-4ee0-a521-d5c2fcb41214/e52106cb-f0af-4409-88af-34a93018f603.template" deployment_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/example_role" log_sender_domain = "example-company.org" diff --git a/docs/resources/array_azure.md b/docs/resources/array_azure.md index ebfac19..0a076c3 100644 --- a/docs/resources/array_azure.md +++ b/docs/resources/array_azure.md @@ -60,6 +60,10 @@ resource "azurerm_key_vault" "cbs_key_vault" { } } +data "cbs_plan_azure" "version_plan" { + plan_version = "6.6.x" +} + resource "cbs_array_azure" "azure_instance" { array_name = "terraform-example-instance" @@ -85,6 +89,13 @@ resource "cbs_array_azure" "azure_instance" { user_assigned_identity = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourcegroups/mock_resource_group_name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxxxxxx", jit_approval_group_object_ids = ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"] + + plan { + name = data.cbs_plan_azure.version_plan.name + product = data.cbs_plan_azure.version_plan.product + publisher = data.cbs_plan_azure.version_plan.publisher + version = data.cbs_plan_azure.version_plan.version + } } ``` @@ -94,7 +105,6 @@ resource "cbs_array_azure" "azure_instance" { - `alert_recipients` (Optional) - List of email addresses to receive alerts. - `array_model` (Required) - CBS array size to launch. The possible values are `V10MUR1`, `V20MUR1` or `V20MP2R2`. - `array_name` (Required) - Name of the array, and the name of the managed application. -- `fusion_sec_identity` (Optional) - Input that denotes the identity of a Fusion Storage Endpoint Collection, obtained during Azure Portal GUI or CLI deployment. Required when the array is deployed for use in a Fusion cluster. - `iscsi_subnet` (Required) - Subnet containing the iSCSI interfaces on the array. - `jit_approval_group_object_ids` (Required) - A list of Azure group object IDs for people who are allowed to approve JIT requests. When used the maximum possible duration of a JIT access request will be set to `PT8H`. @@ -111,6 +121,7 @@ The [azuread_group](https://registry.terraform.io/providers/hashicorp/azuread/la - `resource_group_name` (Required) - Name of the resource group in which to deploy the managed application. - `system_subnet` (Required) - Subnet for the system interface of the Array. - `tags` (Optional) - A list of tags to apply to all resources in the managed application. +- `resource_tags` (Optional) - A list of objects defining specific tags for specific resource types, overriding global tags if conflicting. See [below for nested schema](#nested-schema-for-resource_tags) - `user_assigned_identity` (Required) - A required input that denotes the identity of the customer User Assigned identity. - `virtual_network_id` (Required) - The ID of the virtual network that contains the network interfaces of the array. - `zone` (Required) - The Availability Zone within the deployment location. @@ -124,6 +135,19 @@ The [azuread_group](https://registry.terraform.io/providers/hashicorp/azuread/la - `publisher` (Required) - Specifies the publisher of the plan. - `version` (Required) - Specifies the version of the plan from the marketplace. + +### Nested Schema for `resource_tags` + +- `resource` (Required) - Specifies the Azure resource type to tag with custom tags defined inside of this block. +Example `Microsoft.Compute/virtualMachines`, `Microsoft.Network/networkInterfaces` or `Microsoft.Compute/disks` +- `tag` (Required) - Configuration block defining a custom tag for given resource type. See [below for nested schema](#nested-schema-for-tag) + + +#### Nested Schema for `tag` + +- `name` (Required) - Name of the custom tag +- `value` (Required) - Value for the custom tag + ## Attribute Reference - `application_name` - The name of the managed application. diff --git a/docs/resources/fusion_sec_azure.md b/docs/resources/fusion_sec_azure.md deleted file mode 100644 index 3a762df..0000000 --- a/docs/resources/fusion_sec_azure.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "cbs_fusion_sec_azure Resource - terraform-provider-cbs" -subcategory: "" -description: |- - ---- - -# cbs_fusion_sec_azure Resource - -Allows the deployment and management of a Fusion Storage Endpoint Collection instance on Azure. The instance is deployed as an Azure Managed Application. - -To be used with your Cloud Block Store arrays and Pure Storage Fusion. - -Refer to the [deployment guide](https://support.purestorage.com/FlashArray/PurityFA/Cloud_Block_Store/Cloud_Block_Store_Deployment_and_Configuration_Guide_for_Azure) for information on how to configure the Azure environment for the CBS instance. - -~>Updates are currently not supported for this resource. - -## Example Usage - -```hcl -resource "cbs_fusion_sec_azure" "azure_instance" { - - fusion_sec_name = "terraform-fusion-sec-instance" - - location = "location_xxxx" - resource_group_name = "resource_xxxx" - - load_balancer_network_rg = "xxxx" - load_balancer_network_name = "xxxx" - load_balancer_subnet = "xxxx" - - jit_approval_group_object_ids = ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"] -} -``` - - -## Argument Reference - -- `fusion_sec_name` (Required) - Name of the Storage Endpoint Collection, and the name of the managed application. -- `location` (Required) - Azure location in which to deploy the array. -- `resource_group_name` (Required) - Name of the resource group in which to deploy the managed application. -- `load_balancer_network_rg` (Required) - The Resource Group containing the Virtual Network. -- `load_balancer_network_name` (Required) - "Name of the Virtual Network containing the SEC Load Balancer." -- `load_balancer_subnet` (Required) - Name of the Subnet containing the SEC Load Balancer. -- `jit_approval_group_object_ids` (Required) - A list of Azure group object IDs for people who are allowed to approve JIT requests. When used the maximum possible duration of a JIT access request will be set to `PT8H`. -The [azuread_group](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/group) datasource can be used to look up the group_id from the name of the group. -- `plan` (Optional) - A managed application plan configuration block. See [below for nested schema](#nestedblock--plan). -- `tags` (Optional) - A list of tags to apply to all resources in the managed application. - - - -### Nested Schema for `plan` - -- `name` (Required) - Specifies the name of the plan from the marketplace. -- `product` (Required) - Specifies the product of the plan from the marketplace. -- `publisher` (Required) - Specifies the publisher of the plan. -- `version` (Required) - Specifies the version of the plan from the marketplace. - -## Attribute Reference - -- `application_name` - The name of the managed application. -- `managed_resource_group_name` - The name of the managed resource group of the managed application. -- `hmvip0` - Harbormaster IP address 0. -- `hmvip1` - Harbormaster IP address 1. -- `load_balancer_full_identity_id` - Full identify of the load balancer. diff --git a/examples/aws_array/main.tf b/examples/aws_array/main.tf index 6432b6b..e12ff50 100644 --- a/examples/aws_array/main.tf +++ b/examples/aws_array/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { cbs = { source = "PureStorage-OpenConnect/cbs" - version = "~> 0.9.0" + version = "~> 0.10.0" } } } diff --git a/examples/aws_array/terraform.tfvars b/examples/aws_array/terraform.tfvars index 697f329..12a13e0 100644 --- a/examples/aws_array/terraform.tfvars +++ b/examples/aws_array/terraform.tfvars @@ -1,7 +1,7 @@ #Variables region = "us-west-2" array_name = "array-name" -template_url = "https://s3.amazonaws.com/awsmp-fulfillment-cf-templates-prod/4ea2905b-7939-4ee0-a521-d5c2fcb41214.e1e81a59-5e4c-4400-9675-85361e830022.template" +template_url = "https://s3.amazonaws.com/awsmp-fulfillment-cf-templates-prod/4ea2905b-7939-4ee0-a521-d5c2fcb41214/e52106cb-f0af-4409-88af-34a93018f603.template" deployment_role_arn = "arn:aws:iam::xxxxxxxxxxxx:role/example_role" log_sender_domain = "example-company.org" alert_recipients = ["admin1@example-company.org", "admin2@example-company.org"] diff --git a/examples/azure_array/main.tf b/examples/azure_array/main.tf index cca94d7..76ed0a5 100644 --- a/examples/azure_array/main.tf +++ b/examples/azure_array/main.tf @@ -2,14 +2,16 @@ terraform { required_providers { cbs = { source = "PureStorage-OpenConnect/cbs" - version = "~> 0.9.0" + version = "~> 0.10.0" } } } provider "cbs" {} -data "cbs_azure_plans" "azure_plans" {} +data "cbs_plan_azure" "version_plan" { + plan_version = var.plan_version +} resource "cbs_array_azure" "azure_instance" { @@ -35,11 +37,31 @@ resource "cbs_array_azure" "azure_instance" { jit_approval_group_object_ids = var.jit_group_ids user_assigned_identity = var.user_assigned_identity + resource_tags { + resource = "Microsoft.Compute/virtualMachines" + tag { + name = "foo" + value = "bar" + } + tag { + name = "baz" + value = "qux" + } + } + + resource_tags { + resource = "Microsoft.Network/networkInterfaces" + tag { + name = "foo" + value = "bar" + } + } + plan { - name = data.cbs_azure_plans.azure_plans.plans[0].name - product = data.cbs_azure_plans.azure_plans.plans[0].product - publisher = data.cbs_azure_plans.azure_plans.plans[0].publisher - version = data.cbs_azure_plans.azure_plans.plans[0].version + name = data.cbs_plan_azure.version_plan.name + product = data.cbs_plan_azure.version_plan.product + publisher = data.cbs_plan_azure.version_plan.publisher + version = data.cbs_plan_azure.version_plan.version } lifecycle { @@ -50,7 +72,7 @@ resource "cbs_array_azure" "azure_instance" { } output "cbs_azure_available_plans" { - value = data.cbs_azure_plans.azure_plans.plans + value = data.cbs_plan_azure.version_plan } output "cbs_mgmt_endpoint" { diff --git a/examples/azure_array/terraform.tfvars b/examples/azure_array/terraform.tfvars index ce0e69d..c5c5969 100644 --- a/examples/azure_array/terraform.tfvars +++ b/examples/azure_array/terraform.tfvars @@ -16,3 +16,4 @@ replication_subnet = "SN-xxxxxxxxxxxxxx" jit_group_ids = ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"] key_file_path = "example.pem" user_assigned_identity = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourcegroups/mock_resource_group_name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxxxxxx" +plan_version = "6.6.x" diff --git a/examples/azure_array/variables.tf b/examples/azure_array/variables.tf index 60ab289..575c8fa 100644 --- a/examples/azure_array/variables.tf +++ b/examples/azure_array/variables.tf @@ -64,4 +64,8 @@ variable "key_file_path" { variable "user_assigned_identity" { type = string -} \ No newline at end of file +} + +variable "plan_version" { + type = string +} diff --git a/examples/azure_fusion_sec/main.tf b/examples/azure_fusion_sec/main.tf deleted file mode 100644 index 05e8197..0000000 --- a/examples/azure_fusion_sec/main.tf +++ /dev/null @@ -1,43 +0,0 @@ -terraform { - required_providers { - cbs = { - source = "PureStorage-OpenConnect/cbs" - version = "~> 0.9.0" - } - } -} - -provider "cbs" {} -resource "cbs_fusion_sec_azure" "fusion_sec_azure_instance" { - - fusion_sec_name = var.fusion_sec_name - - location = var.location - resource_group_name = var.resource_group_name - load_balancer_network_rg = var.load_balancer_network_rg - load_balancer_network_name = var.load_balancer_network_name - load_balancer_subnet = var.load_balancer_subnet - - - jit_approval_group_object_ids = var.jit_group_ids - - plan { - name = "xxxx" - product = "xxxx" - publisher = "xxxx" - version = "xxxx" - } -} - - -output "fusion_sec_hmvip0" { - value = fusion_sec_azure_instance.hmvip0 -} - -output "fusion_sec_hmvip0" { - value = fusion_sec_azure_instance.hmvip1 -} - -output "fusion_sec_load_balancer_full_identity_id" { - value = fusion_sec_azure_instance.load_balancer_full_identity_id -} \ No newline at end of file diff --git a/examples/azure_fusion_sec/terraform.tfvars b/examples/azure_fusion_sec/terraform.tfvars deleted file mode 100644 index 06a0819..0000000 --- a/examples/azure_fusion_sec/terraform.tfvars +++ /dev/null @@ -1,9 +0,0 @@ -#Variables -fusion_sec_name = "terraform-example-instance" -location = "location_xxxx" -resource_group_name = "resource_xxxx" -zone = 1 -load_balancer_network_rg = "xxxx" -load_balancer_network_name = "xxxx" -load_balancer_subnet = "xxxx" -jit_group_ids = ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"] diff --git a/examples/azure_fusion_sec/variables.tf b/examples/azure_fusion_sec/variables.tf deleted file mode 100644 index 912fef3..0000000 --- a/examples/azure_fusion_sec/variables.tf +++ /dev/null @@ -1,32 +0,0 @@ -variable "location" { - type = string -} - -variable "resource_group_name" { - type = string -} - -variable "jit_group_ids" { - type = list(string) -} - -variable "zone" { - type = number -} - -variable "fusion_sec_name" { - type = string -} - - -variable "load_balancer_network_rg" { - type = string -} - -variable "load_balancer_network_name" { - type = string -} - -variable "load_balancer_subnet" { - type = string -} diff --git a/go.mod b/go.mod index 646487f..b1c6fc0 100644 --- a/go.mod +++ b/go.mod @@ -22,15 +22,17 @@ require ( github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 github.com/iancoleman/strcase v0.2.0 github.com/mattn/go-sqlite3 v1.14.16 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.4 github.com/terraform-providers/terraform-provider-azurerm v1.44.0 - golang.org/x/crypto v0.1.0 - golang.org/x/term v0.1.0 + golang.org/x/crypto v0.14.0 + golang.org/x/term v0.13.0 ) +require github.com/google/uuid v1.3.1 // indirect + require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 - github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect @@ -92,10 +94,10 @@ require ( github.com/vmihailenco/tagparser v0.1.2 // indirect github.com/zclconf/go-cty v1.11.1 // indirect go.mongodb.org/mongo-driver v1.10.3 // indirect - golang.org/x/net v0.1.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71 // indirect google.golang.org/grpc v1.50.1 // indirect diff --git a/go.sum b/go.sum index 3cdd3e9..528f01c 100644 --- a/go.sum +++ b/go.sum @@ -38,10 +38,10 @@ github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-sdk-for-go v38.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v67.0.0+incompatible h1:SVBwznSETB0Sipd0uyGJr7khLhJOFRUEUb+0JgkCvDo= github.com/Azure/azure-sdk-for-go v67.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8Jgil9UUZtMvxhEFqWo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= github.com/Azure/go-autorest v10.15.4+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v13.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -305,7 +305,8 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -545,8 +546,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/terraform-providers/terraform-provider-azuread v0.6.1-0.20191007035844-361c0a206ad4/go.mod h1:nIkEKBYPFXH/yU7mY7lK5Om6Krga/aMdD/B/OEJpWzc= github.com/terraform-providers/terraform-provider-azurerm v1.44.0 h1:9zehlvIyibEx0dtsZJdaeOZr4MkBhp0Ibhd4JssK3I4= github.com/terraform-providers/terraform-provider-azurerm v1.44.0/go.mod h1:GncKl1cgz6jPcb9gvVP8agaE5YI1IGd04bwmKW1+13Q= @@ -619,8 +621,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -689,8 +691,8 @@ golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5o golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -762,13 +764,13 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -777,8 +779,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/testing/mock-params-azure.json b/testing/mock-params-azure.json index 09e6ac0..6819fd2 100644 --- a/testing/mock-params-azure.json +++ b/testing/mock-params-azure.json @@ -13,5 +13,6 @@ "keyvault_id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mock_resource_group_name/providers/Microsoft.KeyVault/vaults/A00000000000000000000000", "jit_group":"jit_group", "jit_group_id":"00000000-0000-0000-0001-000000000000", - "user_assigned_identity": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourcegroups/mock_resource_group_name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxxxxxx" -} + "user_assigned_identity": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/resourcegroups/mock_resource_group_name/providers/Microsoft.ManagedIdentity/userAssignedIdentities/xxxxxxx", + "resource_tags": "[{\"resource\":\"Microsoft.Compute/virtualMachines\",\"tags\":[{\"tag_name\":\"some_tag_1\",\"tag_value\":\"some_tag_1_value\"},{\"tag_name\":\"some_tag_2\",\"tag_value\":\"some_tag_2_value\"}]},{\"resource\":\"Microsoft.Network/networkInterfaces\",\"tags\":[{\"tag_name\":\"some_tag_3\",\"tag_value\":\"some_tag_3_value\"},{\"tag_name\":\"some_tag_4\",\"tag_value\":\"some_tag_4_value\"}]}]\n" +} \ No newline at end of file diff --git a/testing/mock.env b/testing/mock.env index 1159ca4..f78861e 100644 --- a/testing/mock.env +++ b/testing/mock.env @@ -2,7 +2,6 @@ MOCK_OUT_DIR="../.build/" MOCK_DB_PATH="../.build/test.db" TEST_ACC_AWS_PARAMS_PATH="../testing/mock-params-aws.json" TEST_ACC_AZURE_PARAMS_PATH="../testing/mock-params-azure.json" -TEST_ACC_FUSION_SEC_AZURE_PARAMS_PATH="../testing/mock-params-fusion-sec-azure.json" ARM_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000" ARM_CLIENT_ID=mock_arm_client_id ARM_TENANT_ID=mock_arm_tenant_id