859988e173
* adding vnets, snets and snet delegation for mysql flex * adding filestorage, keyvault, secrets and acls * adding private dns zones, vnetlinks and private endpoints * updating app service, adding AVD host pool * fixing OS disk name * adding vm extensions * removing registratingTokenOperation * adding ps1 for bicep deploy * moving to separate azuredeploysecure, adding Microsoft.Web SE to integrationSubnet and computeSubnet * fixing bicepdeploysecure to point to correct bicep file --------- Co-authored-by: Matheus Carboni <mcarboni@microsoft.com> |
||
---|---|---|
.. | ||
ansible | ||
media | ||
workspaces | ||
.gitignore | ||
.terraform.lock.hcl | ||
README.md | ||
autoshutdown.json | ||
azure-pipelines.yml | ||
backend.tf | ||
locals.tf | ||
main.tf | ||
outputs.tf | ||
variables.tf |
README.md
Secure REDCap on Azure
This repo will deploy REDCap on Azure using Terraform. The Terraform configuration will provision all the infrastructure with necessary security controls in place. This repo assumes you have a hub/spoke network topology in place in Azure and this REDCap deployment will be a spoke within your overall Azure architecture. Once the infrastructure has been provisioned, you will need to establish a virtual network peering from the hub virtual network back to the REDCap virtual network and deploy source code to get the application up and running. From there, you can run the Ansible playbook (inventory file gets generated as part of this deployment) to configure Azure Virtual Desktop (AVD) session hosts which is going to be used as our secure entry points to the REDCap backend system. The front end of REDCap (i.e., survey URLs) will be accessible through the Azure Front Door service.
This repo does not include any REDCap shared services such as Azure FrontDoor or SendGrid (the box in the upper right portion of the diagram above). That needs to be managed from a separate repository. If you do not have Azure Front Door in place, you can access the app service front end and kudu console while logged into the AVD session host.
Prerequisites
Before you begin, make sure you have the following:
-
Understanding of and/or experience with Terraform on Azure
-
Implementation of Azure Enterprise Scale Landing Zones
- To keep consistent with Terrafrom tooling, you may want to implement this Azure/caf-enterprise-scale module for ESLZ
-
As part of Azure Landing Zone architecture, you should have Hub/Spoke network topology.
- The hub virtual network (VNET) should have to have an Azure Firewall or 3rd party Network Virtual Applicance (NVA) in place.
- If your Active Directory Domain Controller or Azure AD Domain Services is in another spoke network, you'll need to have proper User Defined Routes (UDR) in place to ensure transitive networking is enabled from the REDCap spoke networks and the AD servers.
-
Azure Storage Account or Terraform Cloud to store your remote state files.
Once you have these in place, update the
backend.tf
file to include your backend implementation -
REDCap Community site credentials which the deployment automation will use to pull your copy of the REDCap source directly from the community site.
NOTE: These values will be stored within the Azure Key Vault and the values will not be displayed within the Azure App Service configuration settings.
-
Virtual Network address allocation for the REDCap resources and divided into Subnets. Here are the minimum CIDR ranges you'll need:
The deployment relies on the subnet names listed below. If you decide to change these, make sure you replace all instances in
main.tf
./25
for the virtual network/27
forPrivateLinkSubnet
/27
forComputeSubnet
/26
forIntegrationSubnet
-
DNS IP address(es) for domain joining virtual machines (for AVD).
-
Firewall IP address for configuring UDRs.
Make sure your firewall is configured to allow traffic to pass from and to the REDCap virtual networks. See this link if you are using Azure Firewall.
-
VNET peering information. More on vnet peering here.
The Terraform configuration in this repo will perform the one-way peer from REDCap to your hub virtual network.
-
Route table (UDR) routes.
Hub/Spoke topology means you may be relying on resources that are deployed in another spoke vnet within your overall network topology. If resources are in a spoke vnet, you'll need to send the traffic to the firewall in the hub for spoke-to-spoke transit networking. More on vnet traffic routing here.
Naming conventions
The resources provisioned using this repo will be named using the naming conventions as outlined in the Cloud Adoption Framework. See this link for more info
Workspaces
In order to maintain multiple REDCap deployments with this repo, a decision was made to manage each deployment config using .tfvars
files and terraform workspaces. Terraform workspaces will allow you to keep all deployment state information in a single storage account but logically separated using workspaces. With each deployment, you'll need to ensure you are selecting the right workspace and using the right .tfvars file. This can get difficult to manage and there's a high possibility of human error.
The alternative would be to create branches for each deployment but managing code changes between branches can become cumbersome over time as well.
So, what get's deployed?
- Azure Virtual Network with service endpoints enabled for Key Vault, Storage, Sql, and Web and a subnet delegation for App Service Vnet integration.
Virtual network peering will also be made to hub (one way) but peer from hub to REDCap is not in scope here. Also, route table routes will be added to send traffic for internet and AD to the firewall but routes coming back to REDCap is not in scope here either. You will need to manage these in another repo or via Azure Portal.
- Azure Private DNS zones for blob, mysql, and keyvault.
The decision was made to deploy private DNS zones and linked to the REDCap virtual network as opposed to the hub virtual network which is more common. The reason for this was to reduce the network dependency (other than the hub peering) and not allow the REDCap resources to be resolvable within the rest of the network topology.
- Azure Storage Account with private endpoint and service endpoints enabled (general purpose) to store survey data.
- Azure Storage Account with private endpoint and service endpoints enabled (premium files) to mount as a shared drive in the secure workstation.
- Azure Key Vault with private endpoint and service endpoints enabled to store application secrets. Access policies will be configured for AppService to be able to read secrets.
- Azure Database for MySQL with private endpoint enabled and service endpoints.
- Azure App Service to host REDCap application. This service will be vnet integrated and have network access restrictions in place to NOT allow any incoming traffic from any source except the ComputeSubnet (from secure WVD workstations), IntegrationSubnet, or Azure FrontDoor.
The client IP of whereever you are running the terraform from is included only for testing purposes. Be sure to remove the configuration in a production deployment.
- Azure Application Insights for app monitoring
- Windows Virtual Desktop to provide secure computing environment to pull survey data and perform data analysis.
- Windows Virtual Machines with Windows Virtual Desktop Agents installed to register as WVD Session Hosts
The virtual machines will have the following extensions installed: DependencyAgent, IaaSAntimalware, and WinRM (for Ansible) installed via Custom Script Extension.
- Azure Recovery Services Vault with VM Backup Policy and Azure Files backup policy.
- Ansible inventory file will be created during the Terraform provisioning process which you can use to run the
site.yml
playbook againstAnsible playbook will perform the actions of downloading and installing the WVD agents and joining the VM to your domain.
Provisioning REDCap Infrastructure
-
Create a new
.tfvars
file and drop into theworkspaces
directory.- The name of your
.tfvars
file and theterraform workspace
should be the same.
- The name of your
-
Execute the
terraform plan
andterraform apply
commands and pass in your.tfvars
file in the-var-file
parameter.-
You will be required to enter the local VM username and password and the REDCap zip file URL.
-
Here is a sample
terraform plan
command:workspace=sample1 terraform workspace select $(workspace) || terraform workspace new $(workspace) terraform plan -var-file="workspaces/$(workspace).tfvars" -var="vm_username=$(local-vm-username)" -var="vm_password=$(local-vm-password)" -var="redcapAppZip=$(redcapzip)" -out=$(System.DefaultWorkingDirectory)/$(workspace).tfplan
-
Here is a sample
terraform apply
command:terraform apply --auto-approve $(workspace).tfplan
-
-
After the resources have been provisioned, you'll need to create a vnet peering back from the hub vnet to the REDCap's vnet.
- This codebase will only apply one half of the peering (from REDCap vnet to the Hub).
-
Next, deploy the source code from the github repo
-
The command to deploy the source is in the
terraform output
asdeploy_source
-
To get the value of the
deploy_source
output variable use this command:terraform output -raw deploy_source
-
Configure REDCap WVD Workstations
Ansible can automate configuration on Windows machines; however, the Ansible playbooks must be run from a Linux OS (Ubuntu, REDHat, CentOS, etc.).
Configuration of secure workstations will be automated using Ansible. The site.yml
Ansible Playbook found in this repo relies on a few variables needed to domain join your virtual machines. Rather then saving credentials to the repo (never a good thing) we'll use ansible-vault
to encrypt contents leveraging Ansible Vault and pass in a secrets.yml
file on the ansible-playbook run.
Let's start by creating a vault file:
ansible-vault create secrets.yml
Type in a new vault password and enter the following contents:
You're using
vi
here so make sure you hit thei
key be ininsert
mode
dns_domain_name: <YOUR_DOMAIN_NAME>
domain_admin_user: <YOUR_DOMAIN_JOIN_USER>
domain_admin_password: <YOUR_DOMAIN_JOIN_PASSWORD>
domain_ou_path: <YOUR_DOMAIN_OU_PATH>
Save the file using the following command
:wq
The file secrets.yml
needs to be saved to your repository or downloaded as a secure file within your pipeline.
To use view the ansible vault file you'll need to enter the vault password to decrypt the contents. However, in a pipeline scenario, you will not have the opportunity to enter the pipeline at runtime, but you can use a file and point the ansible-vault to that. This is the approach we'll use for the pipeline.
Create a vaultpass
file.
echo '<YOUR_ANSIBLE_VAULT_PASSWORD>' > vaultpass
To ensure you did all this properly, you can use the view
subcommand of ansible-vault
. This will decrypt the vault and display the contents you entered.
ansible-vault view secrets.yml --vault-password-file vaultpass
If all looks good, be sure the variable names aligns with the variables we'll use in our playbook. When you are ready to run the playbook, you will run it by passing in additional variables from your secrets.yml
file. This will be denoted using the -e
flag and since we are referencing a file, you'll need to add the @
symbol in front of the file name
ansible-playbook -i inventory-sample2 -e @secrets.yml --vault-password-file vaultpass site.yml
Resources:
Deleting REDCap
- If you have deployed a Recovery Services Vault, you'll need to make sure to stop and delete your VM and file share backups, unregister your storage account from Backup Infrastructure, and remove the management lock on the resource group (if necessary) before running the
terraform destroy
command. - Be sure to delete the vnet peering from the hub to the REDCap instance
- Be sure to delete the
terraform workspace
Azure DevOps Pipeline
This repo comes with an azure-pipelines.yml
file. To use it, you'll need to setup a Variable Group and add the following secrets. Ideally you will be storing these values in Azure Key Vault and using that to link secrets:
client-id
- used by terraformclient-secret
- used by terraformtenant-id
- used by terraformmain-subscription-id
- this is the id of the subscription where your storage account where remote state file liveslocal-vm-username
- this will passed dynamically to the terraform apply commandlocal-vm-password
- this will passed dynamically to the terraform apply commandredcapzip
- this is the publically accessible (yet secure) URL to your REDCap zip fileansible-vault-password
- this is used to decrypt your ansible-vault without being prompted for a password
You should also provision a small Linux VM in your REDCap shared services subscription and install the self-hosted Azure DevOps Build Agent software on it. This way, you will be able to use your build machine to invoke the Ansible playbook against the new session host VMs using private IP addresses within your Azure virtual network.
Alternatively, if you do not want to manage another VM, you can follow this guide to run an Azure DevOps Build Agent in a container using Azure Container Instances within your virtual network.
Lastly, add pipeline variables called notifyUsers
and workspace
that can be set at queue time.