github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/azure/helpers_test.go (about) 1 package azure 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "os" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources" 13 armStorage "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-01-01/storage" 14 "github.com/Azure/go-autorest/autorest" 15 sasStorage "github.com/hashicorp/go-azure-helpers/storage" 16 "github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/containers" 17 ) 18 19 const ( 20 // required for Azure Stack 21 sasSignedVersion = "2015-04-05" 22 ) 23 24 // verify that we are doing ACC tests or the Azure tests specifically 25 func testAccAzureBackend(t *testing.T) { 26 skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_AZURE_TEST") == "" 27 if skip { 28 t.Log("azure backend tests require setting TF_ACC or TF_AZURE_TEST") 29 t.Skip() 30 } 31 } 32 33 // these kind of tests can only run when within Azure (e.g. MSI) 34 func testAccAzureBackendRunningInAzure(t *testing.T) { 35 testAccAzureBackend(t) 36 37 if os.Getenv("TF_RUNNING_IN_AZURE") == "" { 38 t.Skip("Skipping test since not running in Azure") 39 } 40 } 41 42 // these kind of tests can only run when within GitHub Actions (e.g. OIDC) 43 func testAccAzureBackendRunningInGitHubActions(t *testing.T) { 44 testAccAzureBackend(t) 45 46 if os.Getenv("TF_RUNNING_IN_GITHUB_ACTIONS") == "" { 47 t.Skip("Skipping test since not running in GitHub Actions") 48 } 49 } 50 51 func buildTestClient(t *testing.T, res resourceNames) *ArmClient { 52 subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") 53 tenantID := os.Getenv("ARM_TENANT_ID") 54 clientID := os.Getenv("ARM_CLIENT_ID") 55 clientSecret := os.Getenv("ARM_CLIENT_SECRET") 56 msiEnabled := strings.EqualFold(os.Getenv("ARM_USE_MSI"), "true") 57 environment := os.Getenv("ARM_ENVIRONMENT") 58 59 hasCredentials := (clientID != "" && clientSecret != "") || msiEnabled 60 if !hasCredentials { 61 t.Fatal("Azure credentials missing or incomplete") 62 } 63 64 if subscriptionID == "" { 65 t.Fatalf("Missing ARM_SUBSCRIPTION_ID") 66 } 67 68 if tenantID == "" { 69 t.Fatalf("Missing ARM_TENANT_ID") 70 } 71 72 if environment == "" { 73 t.Fatalf("Missing ARM_ENVIRONMENT") 74 } 75 76 // location isn't used in this method, but is in the other test methods 77 location := os.Getenv("ARM_LOCATION") 78 if location == "" { 79 t.Fatalf("Missing ARM_LOCATION") 80 } 81 82 // Endpoint is optional (only for Stack) 83 endpoint := os.Getenv("ARM_ENDPOINT") 84 85 armClient, err := buildArmClient(context.TODO(), BackendConfig{ 86 SubscriptionID: subscriptionID, 87 TenantID: tenantID, 88 ClientID: clientID, 89 ClientSecret: clientSecret, 90 CustomResourceManagerEndpoint: endpoint, 91 Environment: environment, 92 ResourceGroupName: res.resourceGroup, 93 StorageAccountName: res.storageAccountName, 94 UseMsi: msiEnabled, 95 UseAzureADAuthentication: res.useAzureADAuth, 96 }) 97 if err != nil { 98 t.Fatalf("Failed to build ArmClient: %+v", err) 99 } 100 101 return armClient 102 } 103 104 func buildSasToken(accountName, accessKey string) (*string, error) { 105 // grant full access to Objects in the Blob Storage Account 106 permissions := "rwdlacup" // full control 107 resourceTypes := "sco" // service, container, object 108 services := "b" // blob 109 110 // Details on how to do this are here: 111 // https://docs.microsoft.com/en-us/rest/api/storageservices/Constructing-an-Account-SAS 112 signedProtocol := "https,http" 113 signedIp := "" 114 signedVersion := sasSignedVersion 115 116 utcNow := time.Now().UTC() 117 118 // account for servers being up to 5 minutes out 119 startDate := utcNow.Add(time.Minute * -5).Format(time.RFC3339) 120 endDate := utcNow.Add(time.Hour * 24).Format(time.RFC3339) 121 122 sasToken, err := sasStorage.ComputeAccountSASToken(accountName, accessKey, permissions, services, resourceTypes, 123 startDate, endDate, signedProtocol, signedIp, signedVersion) 124 if err != nil { 125 return nil, fmt.Errorf("Error computing SAS Token: %+v", err) 126 } 127 log.Printf("SAS Token should be %q", sasToken) 128 return &sasToken, nil 129 } 130 131 type resourceNames struct { 132 resourceGroup string 133 location string 134 storageAccountName string 135 storageContainerName string 136 storageKeyName string 137 storageAccountAccessKey string 138 useAzureADAuth bool 139 } 140 141 func testResourceNames(rString string, keyName string) resourceNames { 142 return resourceNames{ 143 resourceGroup: fmt.Sprintf("acctestRG-backend-%s-%s", strings.Replace(time.Now().Local().Format("060102150405.00"), ".", "", 1), rString), 144 location: os.Getenv("ARM_LOCATION"), 145 storageAccountName: fmt.Sprintf("acctestsa%s", rString), 146 storageContainerName: "acctestcont", 147 storageKeyName: keyName, 148 useAzureADAuth: false, 149 } 150 } 151 152 func (c *ArmClient) buildTestResources(ctx context.Context, names *resourceNames) error { 153 log.Printf("Creating Resource Group %q", names.resourceGroup) 154 _, err := c.groupsClient.CreateOrUpdate(ctx, names.resourceGroup, resources.Group{Location: &names.location}) 155 if err != nil { 156 return fmt.Errorf("failed to create test resource group: %s", err) 157 } 158 159 log.Printf("Creating Storage Account %q in Resource Group %q", names.storageAccountName, names.resourceGroup) 160 storageProps := armStorage.AccountCreateParameters{ 161 Sku: &armStorage.Sku{ 162 Name: armStorage.StandardLRS, 163 Tier: armStorage.Standard, 164 }, 165 Location: &names.location, 166 } 167 if names.useAzureADAuth { 168 allowSharedKeyAccess := false 169 storageProps.AccountPropertiesCreateParameters = &armStorage.AccountPropertiesCreateParameters{ 170 AllowSharedKeyAccess: &allowSharedKeyAccess, 171 } 172 } 173 future, err := c.storageAccountsClient.Create(ctx, names.resourceGroup, names.storageAccountName, storageProps) 174 if err != nil { 175 return fmt.Errorf("failed to create test storage account: %s", err) 176 } 177 178 err = future.WaitForCompletionRef(ctx, c.storageAccountsClient.Client) 179 if err != nil { 180 return fmt.Errorf("failed waiting for the creation of storage account: %s", err) 181 } 182 183 containersClient := containers.NewWithEnvironment(c.environment) 184 if names.useAzureADAuth { 185 containersClient.Client.Authorizer = *c.azureAdStorageAuth 186 } else { 187 log.Printf("fetching access key for storage account") 188 resp, err := c.storageAccountsClient.ListKeys(ctx, names.resourceGroup, names.storageAccountName, "") 189 if err != nil { 190 return fmt.Errorf("failed to list storage account keys %s:", err) 191 } 192 193 keys := *resp.Keys 194 accessKey := *keys[0].Value 195 names.storageAccountAccessKey = accessKey 196 197 storageAuth, err := autorest.NewSharedKeyAuthorizer(names.storageAccountName, accessKey, autorest.SharedKey) 198 if err != nil { 199 return fmt.Errorf("Error building Authorizer: %+v", err) 200 } 201 202 containersClient.Client.Authorizer = storageAuth 203 } 204 205 log.Printf("Creating Container %q in Storage Account %q (Resource Group %q)", names.storageContainerName, names.storageAccountName, names.resourceGroup) 206 _, err = containersClient.Create(ctx, names.storageAccountName, names.storageContainerName, containers.CreateInput{}) 207 if err != nil { 208 return fmt.Errorf("failed to create storage container: %s", err) 209 } 210 211 return nil 212 } 213 214 func (c ArmClient) destroyTestResources(ctx context.Context, resources resourceNames) error { 215 log.Printf("[DEBUG] Deleting Resource Group %q..", resources.resourceGroup) 216 future, err := c.groupsClient.Delete(ctx, resources.resourceGroup) 217 if err != nil { 218 return fmt.Errorf("Error deleting Resource Group: %+v", err) 219 } 220 221 log.Printf("[DEBUG] Waiting for deletion of Resource Group %q..", resources.resourceGroup) 222 err = future.WaitForCompletionRef(ctx, c.groupsClient.Client) 223 if err != nil { 224 return fmt.Errorf("Error waiting for the deletion of Resource Group: %+v", err) 225 } 226 227 return nil 228 }