Skip to content

Commit

Permalink
Merge pull request #1307 from sweanan/add-sqlmi-module
Browse files Browse the repository at this point in the history
Add Azure SQL Managed Instance Module
  • Loading branch information
denis256 authored Jul 7, 2023
2 parents c130714 + 0fd2dfd commit d58da78
Show file tree
Hide file tree
Showing 9 changed files with 521 additions and 2 deletions.
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

0 comments on commit d58da78

Please sign in to comment.