Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Azure SQL Managed Instance Module #1307

Merged
merged 17 commits into from
Jul 7, 2023
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ jobs:
env:
AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }}
run: |
cd test
cd test/azure

APP_ID=`echo $AZURE_CREDENTIALS | jq -r -c ".clientId"`
APP_PASSWORD=`echo $AZURE_CREDENTIALS | jq -r -c ".clientSecret"`
Expand All @@ -131,7 +131,7 @@ jobs:
export TF_VAR_client_secret="$APP_PASSWORD"

# run the actual tests under the `azure` subfolder
go test ./azure/* -v -timeout 90m
go test --tags=azure -v -timeout 90m
- name: report back the result
if: always()
env:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Terraform Azure SQL DB 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 SQL Managed Instance, and a SQL Managed Instance database.

- A [SQL Managed Instance](https://azure.microsoft.com/en-us/products/azure-sql/managed-instance/).
- A SQL Managed Database.

Check out [test/azure/terraform_azure_sqlmanagedinstance_example_test.go](./../../../test/azure/terraform_azure_sqlmanagedinstance_example_test.go) to see how you can write automated tests for this module and validate the configuration of the parameters and options.

**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/en-us/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/en-us/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

**WARNING**: The deploymnet for this module usually takes more than 4-6 hours as stated in the [microsoft docs](https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/management-operations-overview?view=azuresql#duration), so please make sure to set the timeout accordingly in the below go test command.

1. Sign up for [Azure](https://azure.microsoft.com/)
2. Configure your Azure credentials using one of the [supported methods for Azure CLI
tools](https://docs.microsoft.com/en-us/cli/azure/azure-cli-configuration?view=azure-cli-latest)
3. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`
4. Configure your Terratest [Go test environment](../README.md)
5. `cd test/azure`
6. `go build terraform_azure_sqlmanagedinstance_example_test.go`
7. `go test -v -run TestTerraformAzureSQLManagedInstanceExample -timeout <in hours>`
128 changes: 128 additions & 0 deletions examples/azure/terraform-azure-sqlmanagedinstance-example/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY AN AZURE SQL Managed Instance
# This is an example of how to deploy an AZURE SQL Managed Instance
# See test/terraform_azure_example_test.go for how to write automated tests for this code.
# ---------------------------------------------------------------------------------------------------------------------


# ---------------------------------------------------------------------------------------------------------------------
# CONFIGURE OUR AZURE CONNECTION
# ---------------------------------------------------------------------------------------------------------------------

provider "azurerm" {
version = "~>3.13.0"
features {}
}

# ---------------------------------------------------------------------------------------------------------------------
# CREATE RANDOM PASSWORD
# ---------------------------------------------------------------------------------------------------------------------

# Random password is used as an example to simplify the deployment and improve the security of the database.
# This is not as a production recommendation as the password is stored in the Terraform state file.
resource "random_password" "password" {
length = 16
override_special = "-_%@"
min_upper = "1"
min_lower = "1"
min_numeric = "1"
min_special = "1"
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY A RESOURCE GROUP
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_resource_group" "sqlmi_rg" {
name = "terratest-sqlmi-${var.postfix}"
location = var.location
}

# ---------------------------------------------------------------------------------------------------------------------
# DEPLOY NETWORK RESOURCES
# This network includes a public address for integration test demonstration purposes
# ---------------------------------------------------------------------------------------------------------------------

resource "azurerm_network_security_group" "sqlmi_nt_sec_grp" {
name = "securitygroup-${var.postfix}"
location = azurerm_resource_group.sqlmi_rg.location
resource_group_name = azurerm_resource_group.sqlmi_rg.name
}

resource "azurerm_network_security_rule" "allow_misubnet_inbound" {
name = "allow_subnet_${var.postfix}"
priority = 200
direction = "Inbound"
access = "Allow"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "10.0.0.0/24"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.sqlmi_rg.name
network_security_group_name = azurerm_network_security_group.sqlmi_nt_sec_grp.name
}

resource "azurerm_virtual_network" "sqlmi_vm" {
name = "vnet-${var.postfix}"
resource_group_name = azurerm_resource_group.sqlmi_rg.name
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.sqlmi_rg.location
}

resource "azurerm_subnet" "sqlmi_sub" {
name = "subnet-${var.postfix}"
resource_group_name = azurerm_resource_group.sqlmi_rg.name
virtual_network_name = azurerm_virtual_network.sqlmi_vm.name
address_prefixes = ["10.0.0.0/24"]

delegation {
name = "managedinstancedelegation"

service_delegation {
name = "Microsoft.Sql/managedInstances"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action", "Microsoft.Network/virtualNetworks/subnets/prepareNetworkPolicies/action", "Microsoft.Network/virtualNetworks/subnets/unprepareNetworkPolicies/action"]
}
}
}

resource "azurerm_subnet_network_security_group_association" "sqlmi_sb_assoc" {
subnet_id = azurerm_subnet.sqlmi_sub.id
network_security_group_id = azurerm_network_security_group.sqlmi_nt_sec_grp.id
}

resource "azurerm_route_table" "sqlmi_rt" {
name = "routetable-${var.postfix}"
location = azurerm_resource_group.sqlmi_rg.location
resource_group_name = azurerm_resource_group.sqlmi_rg.name
disable_bgp_route_propagation = false
depends_on = [
azurerm_subnet.sqlmi_sub,
]
}

resource "azurerm_subnet_route_table_association" "sqlmi_sb_rt_assoc" {
subnet_id = azurerm_subnet.sqlmi_sub.id
route_table_id = azurerm_route_table.sqlmi_rt.id
}

# DEPLOY managed sql instance ## This depends on vnet ##
resource "azurerm_mssql_managed_instance" "sqlmi_mi" {
name = "sqlmi${var.postfix}"
resource_group_name = azurerm_resource_group.sqlmi_rg.name
location = azurerm_resource_group.sqlmi_rg.location

license_type = var.sqlmi_license_type
sku_name = var.sku_name
storage_size_in_gb = var.storage_size
subnet_id = azurerm_subnet.sqlmi_sub.id
vcores = var.cores

administrator_login = var.admin_login
administrator_login_password = "thisIsDog11"
}

resource "azurerm_mssql_managed_database" "sqlmi_db" {
name = var.sqlmi_db_name
managed_instance_id = azurerm_mssql_managed_instance.sqlmi_mi.id
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
output "resource_group_name" {
value = azurerm_resource_group.sqlmi_rg.name
}

output "network_security_group_name" {
value = azurerm_network_security_group.sqlmi_nt_sec_grp.name
}

output "virtual_network_name" {
value = azurerm_virtual_network.sqlmi_vm.name
}

output "subnet_name" {
value = azurerm_subnet.sqlmi_sub.name
}

output "managed_instance_name" {
value = azurerm_mssql_managed_instance.sqlmi_mi.name
}

output "managed_instance_db_name" {
value = azurerm_mssql_managed_database.sqlmi_db.name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# ---------------------------------------------------------------------------------------------------------------------
# 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 supported azure location where the resource exists"
type = string
default = "West US2"
}

variable "sqlmi_license_type" {
description = "The license type for the sql managed instance"
type = string
default = "BasePrice"
}

variable "sku_name" {
description = "The sku name for the sql managed instance"
type = string
default = "GP_Gen5"
}

variable "storage_size" {
description = "The storage for the sql managed instance"
type = string
default = 32
}

variable "cores" {
description = "The vcores for the sql managed instance"
type = string
default = 4
}

variable "admin_login" {
description = "The login for the sql managed instance"
type = string
default = "sqlmiadmin"
}


variable "sqlmi_db_name" {
description = "The Database for the sql managed instance"
type = string
default = "testdb"
}

variable "postfix" {
description = "A postfix string to centrally mitigate resource name collisions."
type = string
default = "resource"
}
59 changes: 59 additions & 0 deletions modules/azure/client_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/datafactory/mgmt/2018-06-01/datafactory"
kvmng "github.com/Azure/azure-sdk-for-go/services/keyvault/mgmt/2016-10-01/keyvault"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2019-09-01/network"
sqlmi "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v3.0/sql"
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2019-06-01/subscriptions"
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
"github.com/Azure/azure-sdk-for-go/services/synapse/mgmt/2020-12-01/synapse"
Expand Down Expand Up @@ -321,6 +322,64 @@ func CreateSQLServerClient(subscriptionID string) (*sql.ServersClient, error) {
return &sqlClient, nil
}

// CreateSQLMangedInstanceClient is a helper function that will create and setup a sql server client
func CreateSQLMangedInstanceClient(subscriptionID string) (*sqlmi.ManagedInstancesClient, error) {
// Validate Azure subscription ID
subscriptionID, err := getTargetAzureSubscription(subscriptionID)
if err != nil {
return nil, err
}

// Lookup environment URI
baseURI, err := getBaseURI()
if err != nil {
return nil, err
}

// Create a sql server client
sqlmiClient := sqlmi.NewManagedInstancesClientWithBaseURI(baseURI, subscriptionID)

// Create an authorizer
authorizer, err := NewAuthorizer()
if err != nil {
return nil, err
}

// Attach authorizer to the client
sqlmiClient.Authorizer = *authorizer

return &sqlmiClient, nil
}

// CreateSQLMangedDatabasesClient is a helper function that will create and setup a sql server client
func CreateSQLMangedDatabasesClient(subscriptionID string) (*sqlmi.ManagedDatabasesClient, error) {
// Validate Azure subscription ID
subscriptionID, err := getTargetAzureSubscription(subscriptionID)
if err != nil {
return nil, err
}

// Lookup environment URI
baseURI, err := getBaseURI()
if err != nil {
return nil, err
}

// Create a sql server client
sqlmidbClient := sqlmi.NewManagedDatabasesClientWithBaseURI(baseURI, subscriptionID)

// Create an authorizer
authorizer, err := NewAuthorizer()
if err != nil {
return nil, err
}

// Attach authorizer to the client
sqlmidbClient.Authorizer = *authorizer

return &sqlmidbClient, nil
}

// CreateDatabaseClient is a helper function that will create and setup a SQL DB client
func CreateDatabaseClient(subscriptionID string) (*sql.DatabasesClient, error) {
// Validate Azure subscription ID
Expand Down
Loading