Skip to content

Commit

Permalink
[NWSDE #67 #108] NWSDE Azure SQL (Entra + Auditing)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonnyry committed Jan 28, 2025
1 parent 11af477 commit 3c2f878
Show file tree
Hide file tree
Showing 19 changed files with 1,115 additions and 0 deletions.
7 changes: 7 additions & 0 deletions templates/workspace_services/azuresql-nwsde/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# See https://docs.docker.com/engine/reference/builder/#dockerignore-file
# Put files here that you don't want copied into your bundle's invocation image
.gitignore
Dockerfile.tmpl

# Local .terraform directories
**/.terraform/*
1 change: 1 addition & 0 deletions templates/workspace_services/azuresql-nwsde/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.cnab/
36 changes: 36 additions & 0 deletions templates/workspace_services/azuresql-nwsde/Dockerfile.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# syntax=docker/dockerfile-upstream:1.4.0
FROM --platform=linux/amd64 debian:bookworm-slim

# PORTER_INIT

RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache

# git
#
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt apt-get update \
&& apt-get install -y git --no-install-recommends

# sqlcmd
#
RUN apt-get update \
&& apt-get install -y curl gnupg \
&& curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
&& echo 'deb https://packages.microsoft.com/debian/11/prod bullseye main'> /etc/apt/sources.list.d/prod.list \
&& apt-get update \
&& apt-get -y install sqlcmd --no-install-recommends \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# nslookup
#
RUN apt-get update && \
apt-get install -y dnsutils && \
rm -rf /var/lib/apt/lists/*


WORKDIR ${BUNDLE_DIR}

# PORTER_MIXINS

# Use the BUNDLE_DIR build argument to copy files into the bundle
COPY --link . ${BUNDLE_DIR}/
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Entra manual configuration for `azuresql-nwsde`

## Background

In order for the Azure SQL instance to communicate with Entra
and validate Entra users/groups being added, it requires
a managed identity with MS Graph permissions of
`Directory.Read.All`.

When the resource processor deploys `azuresql-nwsde`
component it can create the identity, however it does does not
have the permissions to grant another identity MS Graph
admin permissions.

Therefore a managed identity for the Azure SQL instance
must be created manually in advance of using this template and
passed as an `RP_BUNDLE_VALUES` element. Only one identity
is required per TRE - the identity is re-used across Azure SQL
instances.

## Create an identity for NWSDE Azure SQL

1. Ensure your TRE's config.yaml file is created and populated.

2. Run the `create-azuresql-identity.sh` script, with a user that has Directory granting permissions such as Global Administrator.

3. Add the resulting identity resource ID to a `azuresql_identity` attribute of the `RP_BUNDLE_VALUES` variable in `config.yaml` or
GitHub secrets, depending on your deployment method.

The `RP_BUNDLE_VALUES` variable is a JSON object, and the `azuresql_identity` property within it identifies the image gallery that contains the images specified by `source_image_name`:

```bash
RP_BUNDLE_VALUES='{"azuresql_identity":"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/<mgmt-rg>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id-azuresql-<tre_id>"}'
```

4. Once added you will need either re-run a full deployment, or re-run `make deploy-core`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/bin/bash

# requires a privileged entra role to run, e.g. Global Administrator

BOLD="\e[1m"
NORMAL="\e[0m"

echo -e "${BOLD}Creating Azure SQL identity for use in NWSDE-Common-Services deployment${NORMAL}"
echo -e "${BOLD}-----------------------------------------------------------------------${NORMAL}\n"

CONFIG_YAML=../../../config.yaml

echo -e "${BOLD}Parsing values from ${CONFIG_YAML}...${NORMAL}\n"

if [[ ! -f "$CONFIG_YAML" ]]; then
echo -e "config.yaml file not found"
exit 1
fi

LOCATION=$(yq '.location' "$CONFIG_YAML")
TRE_ID=$(yq '.tre_id' "$CONFIG_YAML")
MGMT_RESOURCE_GROUP=$(yq '.management.mgmt_resource_group_name' "$CONFIG_YAML")

if [[ -z "$LOCATION" ]]; then
echo "Value not found for LOCATION in config.yaml"
exit 1
fi

if [[ -z "$TRE_ID" ]]; then
echo "Value not found for TRE_ID in config.yaml"
exit 1
fi

if [[ -z "$MGMT_RESOURCE_GROUP" ]]; then
echo "Value not found for MGMT_RESOURCE_GROUP in config.yaml"
exit 1
fi

IDENTITY_NAME="id-azuresql-$TRE_ID"

echo -e "Using values:\n"
echo -e " > TRE_ID = ${TRE_ID}"
echo -e " > LOCATION = ${LOCATION}"
echo -e " > MGMT_RESOURCE_GROUP = ${MGMT_RESOURCE_GROUP}"
echo -e "\nCreating identity:\n"
echo -e " > AZURESQL_IDENTITY = ${IDENTITY_NAME}\n"

echo -e "${BOLD}Checking for resource group $MGMT_RESOURCE_GROUP (and creating if doesn't exist)...${NORMAL}\n"

az group create --name "$MGMT_RESOURCE_GROUP" \
--location "$LOCATION" \
--output table

echo -e "\n${BOLD}Creating identity $IDENTITY_NAME...${NORMAL}\n"

az identity create --name "$IDENTITY_NAME" \
--resource-group "$MGMT_RESOURCE_GROUP" \
--location "$LOCATION" \
--tags "manually-created=true" \
--output table

echo -e "\n${BOLD}Waiting 30s for Entra service principal to be created...${NORMAL}\n"

sleep 30

echo -e "\n${BOLD}Granting directory read permissions to identity $IDENTITY_NAME...${NORMAL}\n"

MSGRAPH_APP_ID="00000003-0000-0000-c000-000000000000"
MSGRAPH_APP_PERMISSION="Directory.Read.All"
MSGRAPH_SP_ID=$(az ad sp show --id "$MSGRAPH_APP_ID" --query id --output tsv)
MSGRAPH_APP_PERMISSION_ID=$(az ad sp show --id "$MSGRAPH_APP_ID" --query "appRoles[?value=='$MSGRAPH_APP_PERMISSION'].id" --output tsv)
IDENTITY_SP_ID=$(az identity show --name "$IDENTITY_NAME" --resource-group "$MGMT_RESOURCE_GROUP" --query principalId --output tsv)

az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$IDENTITY_SP_ID/appRoleAssignments" \
--body @- << EOF
{
"principalId": "$IDENTITY_SP_ID",
"resourceId": "$MSGRAPH_SP_ID",
"appRoleId": "$MSGRAPH_APP_PERMISSION_ID"
}
EOF

echo -e "\n${BOLD}Now set the azuresql_identity attribute in RP_BUNDLE_VALUES (in deploy.env or GitHub Secrets) to the resource ID below${NORMAL}"
echo -e "${BOLD}----------------------------------------------------------------------------------------------------------------------${NORMAL}\n"

RESOURCE_ID=$(az identity show --name "$IDENTITY_NAME" \
--resource-group "$MGMT_RESOURCE_GROUP" \
--query id \
--output tsv)

echo -e "{\"azuresql_identity\":\"${RESOURCE_ID}\"}\n"
68 changes: 68 additions & 0 deletions templates/workspace_services/azuresql-nwsde/parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"schemaType": "ParameterSet",
"schemaVersion": "1.0.1",
"namespace": "",
"name": "tre-workspace-service-azuresql-nwsde",
"parameters": [
{
"name": "tre_id",
"source": {
"env": "TRE_ID"
}
},
{
"name": "id",
"source": {
"env": "ID"
}
},
{
"name": "tfstate_container_name",
"source": {
"env": "TERRAFORM_STATE_CONTAINER_NAME"
}
},
{
"name": "tfstate_resource_group_name",
"source": {
"env": "MGMT_RESOURCE_GROUP_NAME"
}
},
{
"name": "tfstate_storage_account_name",
"source": {
"env": "MGMT_STORAGE_ACCOUNT_NAME"
}
},
{
"name": "sql_sku",
"source": {
"env": "SQL_SKU"
}
},
{
"name": "storage_gb",
"source": {
"env": "STORAGE_GB"
}
},
{
"name": "workspace_id",
"source": {
"env": "WORKSPACE_ID"
}
},
{
"name": "arm_environment",
"source": {
"env": "ARM_ENVIRONMENT"
}
},
{
"name": "azuresql_identity",
"source": {
"env": "AZURESQL_IDENTITY"
}
}
]
}
153 changes: 153 additions & 0 deletions templates/workspace_services/azuresql-nwsde/porter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
schemaVersion: 1.0.0
name: tre-workspace-service-azuresql-nwsde
version: 1.0.0
description: "An Azure SQL workspace service [nw]"
registry: azuretre
dockerfile: Dockerfile.tmpl

credentials:
# Credentials for interacting with the AAD Auth tenant
- name: auth_client_id
env: AUTH_CLIENT_ID
- name: auth_client_secret
env: AUTH_CLIENT_SECRET
- name: auth_tenant_id
env: AUTH_TENANT_ID
# Credentials for interacting with Azure
- name: azure_tenant_id
env: ARM_TENANT_ID
- name: azure_subscription_id
env: ARM_SUBSCRIPTION_ID
- name: azure_client_id
env: ARM_CLIENT_ID
- name: azure_client_secret
env: ARM_CLIENT_SECRET
parameters:
- name: workspace_id
type: string
- name: tre_id
type: string

# the following are added automatically by the resource processor
- name: id
type: string
description: "Resource ID"
env: id
- name: tfstate_resource_group_name
type: string
description: "Resource group containing the Terraform state storage account"
- name: tfstate_storage_account_name
type: string
description: "The name of the Terraform state storage account"
- name: tfstate_container_name
env: tfstate_container_name
type: string
default: "tfstate"
description: "The name of the Terraform state storage container"
- name: arm_use_msi
env: ARM_USE_MSI
type: boolean
default: false
- name: arm_environment
env: ARM_ENVIRONMENT
type: string
default: "public"
- name: sql_sku
type: string
default: "S2 | 50 DTUs"
- name: storage_gb
type: integer
default: 5
- name: azuresql_identity
type: string
default: ""

mixins:
- exec
- terraform:
clientVersion: 1.9.8

outputs:
- name: azuresql_fqdn
type: string
applyTo:
- install
- upgrade
- name: workspace_address_spaces
type: string
applyTo:
- install
- upgrade

install:
- terraform:
description: "Deploy Azure SQL workspace service"
vars:
auth_client_id: ${ bundle.credentials.auth_client_id }
auth_client_secret: ${ bundle.credentials.auth_client_secret }
auth_tenant_id: ${ bundle.credentials.auth_tenant_id }
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
sql_sku: ${ bundle.parameters.sql_sku }
storage_gb: ${ bundle.parameters.storage_gb }
arm_environment: ${ bundle.parameters.arm_environment }
azuresql_identity: ${ bundle.parameters.azuresql_identity }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"
resource_group_name: ${ bundle.parameters.tfstate_resource_group_name }
storage_account_name: ${ bundle.parameters.tfstate_storage_account_name }
container_name: ${ bundle.parameters.tfstate_container_name }
key: tre-workspace-service-azuresql-${ bundle.parameters.id }
outputs:
- name: azuresql_fqdn
- name: workspace_address_spaces

upgrade:
- terraform:
description: "Upgrade Azure SQL workspace service"
vars:
auth_client_id: ${ bundle.credentials.auth_client_id }
auth_client_secret: ${ bundle.credentials.auth_client_secret }
auth_tenant_id: ${ bundle.credentials.auth_tenant_id }
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
sql_sku: ${ bundle.parameters.sql_sku }
storage_gb: ${ bundle.parameters.storage_gb }
arm_environment: ${ bundle.parameters.arm_environment }
azuresql_identity: ${ bundle.parameters.azuresql_identity }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"
resource_group_name: ${ bundle.parameters.tfstate_resource_group_name }
storage_account_name: ${ bundle.parameters.tfstate_storage_account_name }
container_name: ${ bundle.parameters.tfstate_container_name }
key: tre-workspace-service-azuresql-${ bundle.parameters.id }
outputs:
- name: azuresql_fqdn
- name: workspace_address_spaces

uninstall:
- terraform:
description: "Tear down Azure SQL workspace service"
vars:
auth_client_id: ${ bundle.credentials.auth_client_id }
auth_client_secret: ${ bundle.credentials.auth_client_secret }
auth_tenant_id: ${ bundle.credentials.auth_tenant_id }
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
sql_sku: ${ bundle.parameters.sql_sku }
storage_gb: ${ bundle.parameters.storage_gb }
arm_environment: ${ bundle.parameters.arm_environment }
azuresql_identity: ${ bundle.parameters.azuresql_identity }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"
resource_group_name: ${ bundle.parameters.tfstate_resource_group_name }
storage_account_name: ${ bundle.parameters.tfstate_storage_account_name }
container_name: ${ bundle.parameters.tfstate_container_name }
key: tre-workspace-service-azuresql-${ bundle.parameters.id }
Loading

0 comments on commit 3c2f878

Please sign in to comment.