diff --git a/examples/azure/terraform-azure-loganalytics-example/README.md b/examples/azure/terraform-azure-loganalytics-example/README.md new file mode 100644 index 000000000..436a11018 --- /dev/null +++ b/examples/azure/terraform-azure-loganalytics-example/README.md @@ -0,0 +1,41 @@ +# Terraform Azure Log Analytics Example + +This folder contains a Terraform module that deploys resources in [Azure](https://azure.microsoft.com/) to demonstrate +how you can use TerraTest to write automated tests for your Azure Terraform code. This module deploys a Log Analytics Workspace. + +- A [Log Analytics Workspace](https://docs.microsoft.com/azure/azure-monitor/platform/log-analytics-agent) that gives the module the following: + - [Name](https://docs.microsoft.com/azure/azure-monitor/learn/quick-create-workspace#:~:text=%20Create%20a%20Log%20Analytics%20workspace%20in%20the,and%20region%20as%20in%20the%20deleted...%20More%20) with the value specified in the `loganalytics_workspace_name` output variable. + - [Sku](https://docs.microsoft.com/azure/azure-monitor/learn/quick-create-workspace#:~:text=%20Create%20a%20Log%20Analytics%20workspace%20in%20the,and%20region%20as%20in%20the%20deleted...%20More%20) with the value specified in the `loganalytics_workspace_sku` output variable. + - [RetentionPeriodInDays](https://docs.microsoft.com/azure/azure-monitor/learn/quick-create-workspace#:~:text=%20Create%20a%20Log%20Analytics%20workspace%20in%20the,and%20region%20as%20in%20the%20deleted...%20More%20) with the value specified in the `loganalytics_workspace_retention` output variable. + +Check out [test/azure/terraform_azure_loganalytics_example_test.go](/test/azure/terraform_azure_loganalytics_example_test.go) to see how you can write +automated tests for this module. + +Note that the Log Analytics Workspace in this module don't actually do anything; it just runs the resources for +demonstration purposes. + +**WARNING**: This module and the automated tests for it deploy real resources into your Azure account which can cost you +money. The resources are all part of the [Azure Free Account](https://azure.microsoft.com/free/), so if you haven't used that up, +it should be free, but you are completely responsible for all Azure charges. + +## Running this module manually + +1. Sign up for [Azure](https://azure.microsoft.com/) +1. Configure your Azure credentials using one of the [supported methods for Azure CLI + tools](https://docs.microsoft.com/cli/azure/azure-cli-configuration?view=azure-cli-latest) +1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH` +1. Ensure [environment variables](../README.md#review-environment-variables) are available +1. Run `terraform init` +1. Run `terraform apply` +1. When you're done, run `terraform destroy` + +## Running automated tests against this module + +1. Sign up for [Azure](https://azure.microsoft.com/) +1. Configure your Azure credentials using one of the [supported methods for Azure CLI + tools](https://docs.microsoft.com/cli/azure/azure-cli-configuration?view=azure-cli-latest) +1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH` +1. Configure your TerraTest [Go test environment](../README.md) +1. `cd test/azure` +1. `go build terraform_azure_loganalytics_example_test.go` +1. `go test -v -run TestTerraformAzureLogAnalyticsExample` diff --git a/examples/azure/terraform-azure-loganalytics-example/main.tf b/examples/azure/terraform-azure-loganalytics-example/main.tf new file mode 100644 index 000000000..90b115599 --- /dev/null +++ b/examples/azure/terraform-azure-loganalytics-example/main.tf @@ -0,0 +1,37 @@ +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY LOG ANALYTICS +# This is an example of how to deploy a Log Analytics workspace resource. +# --------------------------------------------------------------------------------------------------------------------- +# See test/azure/terraform_azure_loganalytics_example_test.go for how to write automated tests for this code. +# --------------------------------------------------------------------------------------------------------------------- + +provider "azurerm" { + version = "~> 2.20" + features {} +} + +# PIN TERRAFORM VERSION + +terraform { + # This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting + # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it + # forwards compatible with 0.13.x code. + required_version = ">= 0.12.26" +} + +# --------------------------------------------------------------------------------------------------------------------- +# DEPLOY A RESOURCE GROUP +# --------------------------------------------------------------------------------------------------------------------- + +resource "azurerm_resource_group" "resource_group" { + name = "terratest-log-rg-${var.postfix}" + location = var.location +} + +resource "azurerm_log_analytics_workspace" "log_analytics_workspace" { + name = "log-ws-${var.postfix}" + location = azurerm_resource_group.resource_group.location + resource_group_name = azurerm_resource_group.resource_group.name + sku = "PerGB2018" + retention_in_days = 30 +} \ No newline at end of file diff --git a/examples/azure/terraform-azure-loganalytics-example/outputs.tf b/examples/azure/terraform-azure-loganalytics-example/outputs.tf new file mode 100644 index 000000000..4b9598122 --- /dev/null +++ b/examples/azure/terraform-azure-loganalytics-example/outputs.tf @@ -0,0 +1,16 @@ +output "resource_group_name" { + value = azurerm_resource_group.resource_group.name +} + +output "loganalytics_workspace_name" { + value = azurerm_log_analytics_workspace.log_analytics_workspace.name +} + +output "loganalytics_workspace_sku" { + value = azurerm_log_analytics_workspace.log_analytics_workspace.sku +} + +output "loganalytics_workspace_retention" { + value = azurerm_log_analytics_workspace.log_analytics_workspace.retention_in_days +} + diff --git a/examples/azure/terraform-azure-loganalytics-example/variables.tf b/examples/azure/terraform-azure-loganalytics-example/variables.tf new file mode 100644 index 000000000..1b845bd6d --- /dev/null +++ b/examples/azure/terraform-azure-loganalytics-example/variables.tf @@ -0,0 +1,32 @@ +# --------------------------------------------------------------------------------------------------------------------- +# ENVIRONMENT VARIABLES +# Define these secrets as environment variables +# --------------------------------------------------------------------------------------------------------------------- + +# ARM_CLIENT_ID +# ARM_CLIENT_SECRET +# ARM_SUBSCRIPTION_ID +# ARM_TENANT_ID + +# --------------------------------------------------------------------------------------------------------------------- +# REQUIRED PARAMETERS +# You must provide a value for each of these parameters. +# --------------------------------------------------------------------------------------------------------------------- + +# --------------------------------------------------------------------------------------------------------------------- +# OPTIONAL PARAMETERS +# These parameters have reasonable defaults. +# --------------------------------------------------------------------------------------------------------------------- + + +variable "location" { + description = "The location to set for the storage account." + type = string + default = "East US" +} + +variable "postfix" { + description = "A postfix string to centrally mitigate resource name collisions" + type = string + default = "resource" +} diff --git a/modules/azure/loganalytics.go b/modules/azure/loganalytics.go new file mode 100644 index 000000000..7ff380804 --- /dev/null +++ b/modules/azure/loganalytics.go @@ -0,0 +1,72 @@ +package azure + +import ( + "context" + "fmt" + + "github.com/Azure/azure-sdk-for-go/services/preview/operationalinsights/mgmt/2020-03-01-preview/operationalinsights" + "github.com/gruntwork-io/terratest/modules/testing" + "github.com/stretchr/testify/require" +) + +// LogAnalyticsWorkspaceExists indicates whether the operatonal insights workspaces exists. +// This function would fail the test if there is an error. +func LogAnalyticsWorkspaceExists(t testing.TestingT, workspaceName string, resourceGroupName string, subscriptionID string) bool { + exists, err := LogAnalyticsWorkspaceExistsE(workspaceName, resourceGroupName, subscriptionID) + require.NoError(t, err) + return exists +} + +// GetLogAnalyticsWorkspace gets an operational insights workspace if it exists in a subscription. +// This function would fail the test if there is an error. +func GetLogAnalyticsWorkspace(t testing.TestingT, workspaceName string, resourceGroupName string, subscriptionID string) *operationalinsights.Workspace { + ws, err := GetLogAnalyticsWorkspaceE(workspaceName, resourceGroupName, subscriptionID) + require.NoError(t, err) + + return ws +} + +// GetLogAnalyticsWorkspaceE gets an operational insights workspace if it exists in a subscription. +func GetLogAnalyticsWorkspaceE(workspaceName, resoureGroupName, subscriptionID string) (*operationalinsights.Workspace, error) { + client, err := GetLogAnalyticsWorkspacesClientE(subscriptionID) + if err != nil { + return nil, err + } + + ws, err := client.Get(context.Background(), resoureGroupName, workspaceName) + if err != nil { + return nil, err + } + return &ws, nil +} + +// LogAnalyticsWorkspaceExistsE indicates whether the operatonal insights workspaces exists and may return an error. +func LogAnalyticsWorkspaceExistsE(workspaceName string, resourceGroupName string, subscriptionID string) (bool, error) { + _, err := GetLogAnalyticsWorkspaceE(workspaceName, resourceGroupName, subscriptionID) + if err != nil { + if ResourceNotFoundErrorExists(err) { + return false, nil + } + return false, err + } + return true, nil +} + +// GetLogAnalyticsWorkspacesClientE return workspaces client; otherwise error. +func GetLogAnalyticsWorkspacesClientE(subscriptionID string) (*operationalinsights.WorkspacesClient, error) { + subscriptionID, err := getTargetAzureSubscription(subscriptionID) + if err != nil { + fmt.Println("Workspace client error getting subscription") + return nil, err + } + + client := operationalinsights.NewWorkspacesClient(subscriptionID) + authorizer, err := NewAuthorizer() + if err != nil { + fmt.Println("authorizer error") + return nil, err + } + + client.Authorizer = *authorizer + return &client, nil +} diff --git a/modules/azure/loganalytics_test.go b/modules/azure/loganalytics_test.go new file mode 100644 index 000000000..99bd753e1 --- /dev/null +++ b/modules/azure/loganalytics_test.go @@ -0,0 +1,30 @@ +package azure + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +/* +The below tests are currently stubbed out, with the expectation that they will throw errors. +If/when methods to create and delete log analytics resources are added, these tests can be extended. +*/ + +func TestLogAnalyticsWorkspace(t *testing.T) { + t.Parallel() + + _, err := LogAnalyticsWorkspaceExistsE("fake", "", "") + assert.Error(t, err, "Workspace") +} + +func TestGetLogAnalyticsWorkspaceE(t *testing.T) { + t.Parallel() + workspaceName := "" + resourceGroupName := "" + subscriptionID := "" + + _, err := GetLogAnalyticsWorkspaceE(workspaceName, resourceGroupName, subscriptionID) + require.Error(t, err) +} diff --git a/test/azure/terraform_azure_loganalytics_example_test.go b/test/azure/terraform_azure_loganalytics_example_test.go new file mode 100644 index 000000000..934dbf7f5 --- /dev/null +++ b/test/azure/terraform_azure_loganalytics_example_test.go @@ -0,0 +1,60 @@ +// +build azure + +// NOTE: We use build tags to differentiate azure testing because we currently do not have azure access setup for +// CircleCI. + +package test + +import ( + "strconv" + "strings" + "testing" + + "github.com/gruntwork-io/terratest/modules/azure" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +func TestTerraformAzureLogAnalyticsExample(t *testing.T) { + t.Parallel() + + // subscriptionID is overridden by the environment variable "ARM_SUBSCRIPTION_ID" + subscriptionID := "" + uniquePostfix := random.UniqueId() + + // website::tag::1:: Configure Terraform setting up a path to Terraform code. + terraformOptions := &terraform.Options{ + // The path to where our Terraform code is located + TerraformDir: "../../examples/azure/terraform-azure-loganalytics-example", + // Variables to pass to our Terraform code using -var options + Vars: map[string]interface{}{ + "postfix": uniquePostfix, + }, + } + + // website::tag::4:: At the end of the test, run `terraform destroy` to clean up any resources that were created + defer terraform.Destroy(t, terraformOptions) + + // website::tag::2:: Run `terraform init` and `terraform apply`. Fail the test if there are any errors. + terraform.InitAndApply(t, terraformOptions) + + // website::tag::3:: Run `terraform output` to get the values of output variables + resourceGroupName := terraform.Output(t, terraformOptions, "resource_group_name") + workspaceName := terraform.Output(t, terraformOptions, "loganalytics_workspace_name") + sku := terraform.Output(t, terraformOptions, "loganalytics_workspace_sku") + retentionPeriodString := terraform.Output(t, terraformOptions, "loganalytics_workspace_retention") + + // website::tag::4:: Verify the Log Analytics properties and ensure it matches the output. + workspaceExists := azure.LogAnalyticsWorkspaceExists(t, workspaceName, resourceGroupName, subscriptionID) + assert.True(t, workspaceExists, "log analytics workspace not found.") + + actualWorkspace := azure.GetLogAnalyticsWorkspace(t, workspaceName, resourceGroupName, subscriptionID) + + actualSku := string(actualWorkspace.Sku.Name) + assert.Equal(t, strings.ToLower(sku), strings.ToLower(actualSku), "log analytics sku mismatch") + + actualRetentionPeriod := *actualWorkspace.RetentionInDays + expectedPeriod, _ := strconv.ParseInt(retentionPeriodString, 10, 32) + assert.Equal(t, int32(expectedPeriod), actualRetentionPeriod, "log analytics retention period mismatch") +}