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