* add ohdsi workspace service (before adjustments to the OSS)

* add execute permission to scripts

* Fix Postgres timeouts when install OHDSI (#3559)

[ohdsi] fix postgres timeouts

* Add OHDSI workspace service (#3552)

* remove todos

* link core vnet to postgres private dns zone when deploying core

* remove synapse references, and add other data sources to the options

* remove postgres_core_dns_link references

* revert synapse reference deletions

* remove non supported dialects

* add execute permission to scripts

* make some of the daimons required

* add required zone field to postgres:
https://github.com/hashicorp/terraform-provider-azurerm/issues/16888

* add fw rule to allow open id authentication in atlas

* fix firewall step

* add README

* update README

* fix linting errors

* fix linting errors

* update changelog

* Update CHANGELOG.md

Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com>

* add ohdsi ws service to the CI

* clarified  README

* added name, description and overview to the template_schema

* move README content to docs

* change default display name

* add diagram to and instructions about setting up the CDM data source

* add link to ohdsi-on-azure

* move ohdsi-on-azure to the top

* link to OHDSIonAzure for deploying synapse

* Update docs/tre-templates/workspace-services/ohdsi.md

Co-authored-by: Marcus Robinson <marrobi@microsoft.com>

* add Using a sample CDM data source section

---------

Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com>
Co-authored-by: Marcus Robinson <marrobi@microsoft.com>
This commit is contained in:
Yuval Yaron 2023-06-26 20:09:02 +03:00 коммит произвёл GitHub
Родитель 0ed36d0280
Коммит 8c82fd75ee
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
36 изменённых файлов: 2009 добавлений и 0 удалений

4
.github/workflows/deploy_tre_reusable.yml поставляемый
Просмотреть файл

@ -392,6 +392,8 @@ jobs:
BUNDLE_DIR: "./templates/workspace_services/health-services"}
- {BUNDLE_TYPE: "workspace_service",
BUNDLE_DIR: "./templates/workspace_services/databricks"}
- {BUNDLE_TYPE: "workspace_service",
BUNDLE_DIR: "./templates/workspace_services/ohdsi"}
- {BUNDLE_TYPE: "user_resource",
BUNDLE_DIR: "./templates/workspace_services/guacamole/user_resources/guacamole-azure-windowsvm"}
- {BUNDLE_TYPE: "user_resource",
@ -549,6 +551,8 @@ jobs:
BUNDLE_DIR: "./templates/workspace_services/health-services"}
- {BUNDLE_TYPE: "workspace_service",
BUNDLE_DIR: "./templates/workspace_services/databricks"}
- {BUNDLE_TYPE: "workspace_service",
BUNDLE_DIR: "./templates/workspace_services/ohdsi"}
environment: ${{ inputs.environmentName }}
steps:

Просмотреть файл

@ -9,6 +9,7 @@ FEATURES:
ENHANCEMENTS:
* Workspace networking peering sync is handled natively by Terraform ([#3534](https://github.com/microsoft/AzureTRE/issues/3534))
* Use SMTP built in connector vs API connector in Airlock Notifier ([#3572](https://github.com/microsoft/AzureTRE/issues/3572))
* Added OHDSI workspace service ([#3562](https://github.com/microsoft/AzureTRE/issues/3562))
BUG FIXES:
* Nexus might fail to deploy due to wrong identity used in key-vault extension ([#3492](https://github.com/microsoft/AzureTRE/issues/3492))

Двоичные данные
docs/tre-templates/workspace-services/images/ohdsi_service.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 158 KiB

Просмотреть файл

@ -0,0 +1,38 @@
# OHDSI Workspace Service
!!! warning
- This workspace service does not work "out of the box". It requires additional networking configuration to work properly. See the [networking configuration](#networking-configuration) section for more details.
- Currently the only CDM data source supported by the workspace service is Azure Synapse.
See the [official OHDSI website](https://www.ohdsi.org/) and [The Book of OHDSI](https://ohdsi.github.io/TheBookOfOhdsi/).
This service installs the following resources into an existing virtual network within the workspace:
![OHDSI ATLAS Workspace Service](images/ohdsi_service.png)
## Networking configuration
Deploying the OHDSI workspace is not enough for it to function properly, in order for it to work properly, the following networking configuration should be in place:
### 1. The resource processor should be able to access the CDM data source
Multiple OHDSI workspace services cannot share the same RESULTS and TEMP schemas because each OHDSI instance is changing the schemas, which could potentially cause conflicts.
To avoid this, every workspace service must work on its own schemas. To do this, we use golden copying.
This means that the "main" schemas remain untouched, and every workspace service has its own copy of the RESULTS and TEMP schemas, in the CDM data source, which it can modify.
Since the resource processor is in charge of duplicating the schemas, the CDM data source has to be accessible from the resource processor's VNet in order to be able to create them.
### 2. The workspace should be able to access the CDM data source
In order to access the CDM from ATLAS, the CDM data source should be accessible from the workspace's VNet.
Since the CDM data source is outside of TRE, this is not part of the template, however, there are many ways in which this can be done,
one example would be to to deploy a private endpoint for the CDM data source in the workspace's VNet as part of a custom workspace template.
## Setting up a CDM data source
Currently the only CDM data source supported by the workspace service is Azure Synapse.
If you already have an OMOP CDM data source, then all you have to do is to configure the network as described in the [networking configuration](#networking-configuration) section.
If you're data is in a different format, you can read [here](https://ohdsi.github.io/TheBookOfOhdsi/ExtractTransformLoad.html) how to set up the ETL process to convert your medical data to OMOP format.
## Using a sample CDM data source
If you don't have any data yet, or if you just want a quick start, you can deploy an Azure Synapse CDM data source with sample data using the [OHDSI on Azure](https://github.com/microsoft/OHDSIonAzure) repository.
When deploying set `OMOP CDM Database Type` to `Synapse Dedicated Pool` as per the [deployment guide](https://github.com/microsoft/OHDSIonAzure/blob/main/docs/DeploymentGuide.md#:~:text=OMOP%20CDM%20Database%20Type).
Note that you will need to provision a private endpoint into the Azure TRE workspace that connects to the SQL Dedicated Pool as described in the [networking configuration](#networking-configuration) section.

Просмотреть файл

@ -101,6 +101,7 @@ nav:
- MLFlow: tre-templates/workspace-services/mlflow.md
- Health Services: tre-templates/workspace-services/health_services.md
- Azure Databricks: tre-templates/workspace-services/databricks.md
- OHDSI: tre-templates/workspace-services/ohdsi.md
- Shared Services:
- Gitea (Source Mirror): tre-templates/shared-services/gitea.md
- Nexus (Package Mirror): tre-templates/shared-services/nexus.md

Просмотреть файл

@ -0,0 +1,7 @@
# Local .terraform directories
**/.terraform/*
# TF backend files
**/*_backend.tf
Dockerfile.tmpl

Просмотреть файл

@ -0,0 +1,5 @@
ID="__CHANGE_ME__"
WORKSPACE_ID="__CHANGE_ME__"
TRE_ID="__CHANGE_ME__"
MGMT_RESOURCE_GROUP_NAME="__CHANGE_ME__"
MGMT_ACR_NAME="__CHANGE_ME__"

Просмотреть файл

@ -0,0 +1,29 @@
# syntax=docker/dockerfile-upstream:1.4.0
FROM debian:bullseye-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
# sqlcmd is required for schemas initialization in AzureSynapse
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# ignore lint rule that requires `--no-install-recommends` to allow the microsoft packeges to get everything they need and clear it all up in the end
# hadolint ignore=DL3015
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/*
# Git is required for terraform_azurerm_environment_configuration
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
# PostgreSql is required by Atlas
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
apt-get update && apt-get install -y postgresql-client gettext apache2-utils curl jq --no-install-recommends
# PORTER_MIXINS
# Use the BUNDLE_DIR build argument to copy files into the bundle
COPY --link . ${BUNDLE_DIR}/

Просмотреть файл

@ -0,0 +1,7 @@
# OHDSI Workspace Service
## IMPORTANT
- This workspace service does not work "out of the box". It requires additional networking configuration to work properly.
- Currently the only CDM data source supported by the workspace service is Azure Synapse.
Further details are provided in the [documentation](https://microsoft.github.io/AzureTRE/latest/tre-templates/workspace-services/ohdsi/).

Просмотреть файл

@ -0,0 +1,80 @@
{
"schemaType": "ParameterSet",
"schemaVersion": "1.0.1",
"namespace": "",
"name": "tre-workspace-service-ohdsi",
"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": "workspace_id",
"source": {
"env": "WORKSPACE_ID"
}
},
{
"name": "address_space",
"source": {
"env": "ADDRESS_SPACE"
}
},
{
"name": "arm_environment",
"source": {
"env": "ARM_ENVIRONMENT"
}
},
{
"name": "azure_environment",
"source": {
"env": "AZURE_ENVIRONMENT"
}
},
{
"name": "configure_data_source",
"source": {
"env": "CONFIGURE_DATA_SOURCE"
}
},
{
"name": "data_source_config",
"source": {
"env": "DATA_SOURCE_CONFIG"
}
},
{
"name": "data_source_daimons",
"source": {
"env": "DATA_SOURCE_DAIMONS"
}
}
]
}

Просмотреть файл

@ -0,0 +1,185 @@
---
schemaVersion: 1.0.0
name: tre-workspace-service-ohdsi
version: 0.1.94
description: "An OHDSI workspace service"
registry: azuretre
dockerfile: Dockerfile.tmpl
custom:
dialects:
"Azure Synapse": "synapse"
credentials:
- 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
- name: address_space
type: string
description: "Address space for PostgreSQL's subnet"
- name: id
type: string
description: "An Id for this installation"
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
type: string
- name: azure_environment
type: string
description: "Used by Azure CLI to set the Azure environment"
# parameters for configuring the data source
- name: configure_data_source
type: boolean
default: false
- name: data_source_config
type: string
default: ""
- name: data_source_daimons
type: string
default: ""
mixins:
- terraform:
clientVersion: 1.4.6
- az:
clientVersion: 2.37.0
outputs:
- name: connection_uri
type: string
applyTo:
- install
- upgrade
- name: webapi_uri
type: string
applyTo:
- install
- upgrade
- name: authentication_callback_uri
type: string
applyTo:
- install
- upgrade
- name: is_exposed_externally
type: boolean
applyTo:
- install
- upgrade
install:
- az:
description: "Set Azure Cloud Environment"
arguments:
- cloud
- set
flags:
name: ${ bundle.parameters.azure_environment }
- az:
description: "Login to Azure"
arguments:
- login
flags:
identity:
username: ${ bundle.credentials.azure_client_id }
- terraform:
description: "Deploy OHDSI workspace service"
vars:
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
address_space: ${ bundle.parameters.address_space }
arm_environment: ${ bundle.parameters.arm_environment }
configure_data_source: ${ bundle.parameters.configure_data_source }
data_source_config: ${ bundle.parameters.data_source_config }
data_source_daimons: ${ bundle.parameters.data_source_daimons }
backendConfig:
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-ohdsi-${ bundle.parameters.id }
outputs:
- name: connection_uri
- name: webapi_uri
- name: authentication_callback_uri
- name: is_exposed_externally
upgrade:
- az:
description: "Set Azure Cloud Environment"
arguments:
- cloud
- set
flags:
name: ${ bundle.parameters.azure_environment }
- az:
description: "Login to Azure"
arguments:
- login
flags:
identity:
username: ${ bundle.credentials.azure_client_id }
- terraform:
description: "Upgrade shared service"
vars:
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
address_space: ${ bundle.parameters.address_space }
arm_environment: ${ bundle.parameters.arm_environment }
configure_data_source: ${ bundle.parameters.configure_data_source }
data_source_config: ${ bundle.parameters.data_source_config }
data_source_daimons: ${ bundle.parameters.data_source_daimons }
backendConfig:
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-ohdsi-${ bundle.parameters.id }
outputs:
- name: connection_uri
- name: webapi_uri
- name: authentication_callback_uri
- name: is_exposed_externally
uninstall:
- terraform:
description: "Tear down OHDSI workspace service"
vars:
workspace_id: ${ bundle.parameters.workspace_id }
tre_id: ${ bundle.parameters.tre_id }
tre_resource_id: ${ bundle.parameters.id }
address_space: ${ bundle.parameters.address_space }
arm_environment: ${ bundle.parameters.arm_environment }
configure_data_source: ${ bundle.parameters.configure_data_source }
data_source_config: ${ bundle.parameters.data_source_config }
data_source_daimons: ${ bundle.parameters.data_source_daimons }
backendConfig:
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-ohdsi-${ bundle.parameters.id }

Просмотреть файл

@ -0,0 +1,69 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
function build_daimon_object() {
local DAIMON_TYPE=$1
local VALUE=$2
echo '{
"tableQualifier": "'"$VALUE"'",
"priority": 0,
"sourceDaimonId": null,
"daimonType": "'"$DAIMON_TYPE"'"
}'
}
# Login
login_response=$(curl "https://${OHDSI_WEB_API_URL}/WebAPI/user/login/db" \
--data-raw "login=$OHDSI_WEB_API_USER&password=$OHDSI_WEB_API_PASSWORD" \
--compressed -i)
token=$(echo "$login_response" | grep -i bearer: | sed 's/Bearer: //' | tr -d '[:space:]')
# Build the request payload
JSON_PAYLOAD="{}"
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.dialect = $DIALECT' --arg DIALECT "$DIALECT")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.name = $SOURCE_NAME' --arg SOURCE_NAME "$SOURCE_NAME")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.key = $SOURCE_KEY' --arg SOURCE_KEY "$SOURCE_KEY")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.connectionString = $CONNECTION_STRING' --arg CONNECTION_STRING "$CONNECTION_STRING")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.username = $USERNAME' --arg USERNAME "$USERNAME")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.password = $PASSWORD' --arg PASSWORD "$PASSWORD")
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.krbAuthMethod = "password"')
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.krbAdminServer = null')
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons = []')
if [[ -v DAIMON_CDM ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_CDM]' --argjson DAIMON_CDM "$(build_daimon_object "CDM" "${DAIMON_CDM}")")
fi
if [[ -v DAIMON_VOCABULARY ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_VOCABULARY]' --argjson DAIMON_VOCABULARY "$(build_daimon_object "Vocabulary" "${DAIMON_VOCABULARY}")")
fi
if [[ -v DAIMON_RESULTS ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_RESULTS]' --argjson DAIMON_RESULTS "$(build_daimon_object "Results" "${DAIMON_RESULTS}")")
fi
if [[ -v DAIMON_CEM ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_CEM]' --argjson DAIMON_CEM "$(build_daimon_object "CEM" "${DAIMON_CEM}")")
fi
if [[ -v DAIMON_CEM_RESULTS ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_CEM_RESULTS]' --argjson DAIMON_CEM_RESULTS "$(build_daimon_object "CEMResults" "${DAIMON_CEM_RESULTS}")")
fi
if [[ -v DAIMON_TEMP ]]; then
JSON_PAYLOAD=$(echo "$JSON_PAYLOAD" | jq '.daimons += [$DAIMON_TEMP]' --argjson DAIMON_TEMP "$(build_daimon_object "Temp" "${DAIMON_TEMP}")")
fi
# Add the data source
curl -v "https://${OHDSI_WEB_API_URL}/WebAPI/source/" \
-H "Authorization: Bearer ${token}" \
-H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundary2C72lleJPQ9UH4DL' \
--data-raw $'------WebKitFormBoundary2C72lleJPQ9UH4DL\r\nContent-Disposition: form-data; name="keyfile"\r\n\r\nundefined\r\n------WebKitFormBoundary2C72lleJPQ9UH4DL\r\nContent-Disposition: form-data; name="source"; filename="blob"\r\nContent-Type: application/json\r\n\r\n'"${JSON_PAYLOAD}"$'\r\n------WebKitFormBoundary2C72lleJPQ9UH4DL--\r\n' \
--compressed

Просмотреть файл

@ -0,0 +1,21 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
admin_user_password="${OHDSI_ADMIN_PASSWORD}${OHDSI_ADMIN_USERNAME}"
app_user_password="${OHDSI_APP_PASSWORD}${OHDSI_APP_USERNAME}"
admin_md5=("md5$(echo -n "$admin_user_password" | md5sum | awk '{ print $1 }')")
export admin_md5
app_md5=("md5$(echo -n "$app_user_password" | md5sum | awk '{ print $1 }')'")
export app_md5
printf 'Creating roles and users'
envsubst < ../sql/atlas_create_roles_users.sql | psql -v ON_ERROR_STOP=0 -e "$MAIN_CONNECTION_STRING"
printf 'Creating roles and users: done.'
printf 'Creating schema'
envsubst < ../sql/atlas_create_schema.sql | psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING"
printf 'Creating schema: done.'
printf 'Done'

Просмотреть файл

@ -0,0 +1,29 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING" -f "../sql/atlas_create_security.sql"
psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING" -f "../sql/atlas_default_roles.sql"
count=1
for i in ${ATLAS_USERS//,/ }
do
if [ "$(("$count" % 2))" -eq "1" ]; then
username=$i
else
# shellcheck disable=SC2016
atlaspw=$(htpasswd -bnBC 4 "" "$i" | tr -d ':\n' | sed 's/$2y/$2a/')
psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING" -c "insert into webapi_security.security (email,password) values ('$username', E'$atlaspw');"
# this step adds some required rows/ids in the db
curl "$WEB_API_URL/user/login/db" --data-urlencode "login=$username" --data-urlencode "password=$i" --fail
if [ "$count" = "2" ]; then
psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING" -c "insert into webapi.sec_user_role (user_id, role_id) values ((select id from webapi.sec_user where login='$username'),2);" #admin role
else
psql -v ON_ERROR_STOP=1 -e "$OHDSI_ADMIN_CONNECTION_STRING" -c "insert into webapi.sec_user_role (user_id, role_id) values ((select id from webapi.sec_user where login='$username'),10);" #atlas user role
fi
fi
((count++))
done

Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
link_name="core"
result=$(az network private-dns link vnet list --resource-group "${RESOURCE_GROUP}" -z "${DNS_ZONE_NAME}" --query "[?name=='${link_name}'] | length(@)")
if [[ "${result}" == 0 ]];
then
az network private-dns link vnet create \
--name ${link_name} --resource-group "${RESOURCE_GROUP}" --virtual-network "${VNET}" --zone-name "${DNS_ZONE_NAME}" \
--registration-enabled false
else
echo "Zone already linked."
fi

Просмотреть файл

@ -0,0 +1,12 @@
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
export SQLCMDPASSWORD="${ADMIN_USER_PASSWORD}"
printf 'Initializing Results and Temp schemas'
sqlcmd -U "${ADMIN_USERNAME}" -S "${SYNAPSE_SERVER}" -d "${SYNAPSE_DATABASE}" -W -v RESULTS_SCHEMA_NAME="${RESULTS_SCHEMA_NAME}" -v TEMP_SCHEMA_NAME="${TEMP_SCHEMA_NAME}" -v ORIGIN_RESULTS_SCHEMA_NAME="${ORIGIN_RESULTS_SCHEMA_NAME}" -i "${SQL_FILE_PATH}"
printf 'Initializing Results and Temp schemas: done.'
printf 'Done'

Просмотреть файл

@ -0,0 +1,23 @@
CREATE ROLE ${OHDSI_ADMIN_ROLE} CREATEDB REPLICATION VALID UNTIL 'infinity';
COMMENT ON ROLE ${OHDSI_ADMIN_ROLE} IS 'Administration group for OHDSI applications';
CREATE ROLE ${OHDSI_APP_ROLE} VALID UNTIL 'infinity';
COMMENT ON ROLE ${OHDSI_APP_ROLE} IS 'Application groupfor OHDSI applications';
CREATE ROLE ${OHDSI_ADMIN_USERNAME} LOGIN ENCRYPTED PASSWORD ${admin_md5} VALID UNTIL 'infinity';
GRANT ${OHDSI_ADMIN_ROLE} TO ${OHDSI_ADMIN_USERNAME};
COMMENT ON ROLE ${OHDSI_ADMIN_USERNAME} IS 'Admin user account for OHDSI applications';
CREATE ROLE ${OHDSI_APP_USERNAME} LOGIN ENCRYPTED PASSWORD ${app_md5} VALID UNTIL 'infinity';
GRANT ${OHDSI_APP_ROLE} TO ${OHDSI_APP_USERNAME};
COMMENT ON ROLE ${OHDSI_APP_USERNAME} IS 'Application user account for OHDSI applications';
GRANT ALL ON DATABASE ${DATABASE_NAME} TO GROUP ${OHDSI_ADMIN_ROLE};
GRANT CONNECT, TEMPORARY ON DATABASE ${DATABASE_NAME} TO GROUP ${OHDSI_APP_ROLE};

Просмотреть файл

@ -0,0 +1,24 @@
CREATE SCHEMA ${SCHEMA_NAME}
AUTHORIZATION ${OHDSI_ADMIN_ROLE};
COMMENT ON SCHEMA ${SCHEMA_NAME} IS 'Schema containing tables to support WebAPI functionality';
GRANT USAGE ON SCHEMA ${SCHEMA_NAME} TO PUBLIC;
GRANT ALL ON SCHEMA ${SCHEMA_NAME} TO GROUP ${OHDSI_ADMIN_ROLE};
GRANT USAGE ON SCHEMA ${SCHEMA_NAME} TO GROUP ${OHDSI_APP_ROLE};
ALTER DEFAULT PRIVILEGES IN SCHEMA ${SCHEMA_NAME} GRANT
INSERT,
SELECT,
UPDATE,
DELETE, REFERENCES,
TRIGGER ON TABLES TO ${OHDSI_APP_ROLE};
ALTER DEFAULT PRIVILEGES IN SCHEMA ${SCHEMA_NAME} GRANT
SELECT, USAGE ON SEQUENCES TO ${OHDSI_APP_ROLE};
ALTER DEFAULT PRIVILEGES IN SCHEMA ${SCHEMA_NAME} GRANT EXECUTE ON FUNCTIONS TO ${OHDSI_APP_ROLE};
ALTER DEFAULT PRIVILEGES IN SCHEMA ${SCHEMA_NAME} GRANT USAGE ON TYPES TO ${OHDSI_APP_ROLE};

Просмотреть файл

@ -0,0 +1,49 @@
create schema IF NOT EXISTS webapi_security;
DROP TABLE IF EXISTS webapi_security.security;
CREATE TABLE webapi_security.security
(
email character varying(255),
password character varying(255)
);
GRANT USAGE ON SCHEMA webapi_security TO PUBLIC;
GRANT ALL ON SCHEMA webapi_security TO GROUP ohdsi_admin;
do $$
declare tables_count integer := 0;
declare roles_count integer := 0;
begin
while tables_count <> 3 loop
raise notice 'Waiting for application security tables to become ready...';
PERFORM pg_sleep(10);
tables_count := (
SELECT COUNT(*)
FROM pg_tables
WHERE schemaname = 'webapi'
AND tablename in ('sec_user', 'sec_role', 'sec_user_role')
);
end loop;
raise notice 'All tables are ready.';
while roles_count <> 3 loop
raise notice 'Waiting for application security roles to become ready...';
PERFORM pg_sleep(10);
roles_count := (
SELECT COUNT(*)
FROM webapi.sec_role
WHERE id in (1, 2, 10)
);
end loop;
raise notice 'All roles are ready.';
raise notice 'Done.';
end$$;

Просмотреть файл

@ -0,0 +1,27 @@
-- This gives each new user access to all sources plus 'Atlas User' role.
CREATE OR REPLACE FUNCTION function_default_user_roles() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO webapi.sec_user_role (role_id, user_id)
SELECT r.id as role_id, new.id as user_id
FROM webapi.sec_role as r
WHERE r.name LIKE 'Source user%' OR r.name = 'Atlas users';
RETURN new;
END;
$BODY$
language plpgsql;
DROP TRIGGER IF EXISTS trigger_sec_user_insert ON webapi.sec_user;
CREATE TRIGGER trigger_sec_user_insert
AFTER INSERT ON webapi.sec_user
FOR EACH ROW
EXECUTE PROCEDURE function_default_user_roles();
DO $$
BEGIN
RAISE NOTICE 'Finished setting up default roles procedures.';
END $$;

Просмотреть файл

@ -0,0 +1,33 @@
/****** Results and temp Schema creation ******/
CREATE SCHEMA [$(RESULTS_SCHEMA_NAME)]
GO
CREATE SCHEMA [$(TEMP_SCHEMA_NAME)]
GO
/****** Copy Data ******/
CREATE TABLE #tbl
WITH
( DISTRIBUTION = ROUND_ROBIN
)
AS
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Sequence
, [name]
, 'CREATE TABLE ' + N'$(RESULTS_SCHEMA_NAME)' + '.' + t.name + ' WITH (DISTRIBUTION = ' + d.distribution_policy_desc + ', CLUSTERED COLUMNSTORE INDEX) AS SELECT * FROM ' + N'$(ORIGIN_RESULTS_SCHEMA_NAME)' + '.' + t.name AS sql_code
FROM sys.tables AS t left join sys.pdw_table_distribution_properties AS d ON (t.object_id = d.object_id)
WHERE t.schema_id = (select schema_id from sys.schemas where name = N'$(ORIGIN_RESULTS_SCHEMA_NAME)')
;
DECLARE @nbr_statements INT = (SELECT COUNT(*) FROM #tbl)
, @i INT = 1
;
WHILE @i <= @nbr_statements
BEGIN
DECLARE @sql_code NVARCHAR(4000) = (SELECT sql_code FROM #tbl WHERE Sequence = @i);
EXEC sp_executesql @sql_code;
SET @i +=1;
END
DROP TABLE #tbl;

Просмотреть файл

@ -0,0 +1,377 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://github.com/microsoft/AzureTRE/templates/workspace_services/ohdsi/template_schema.json",
"type": "object",
"title": "OHDSI Workspace Service",
"description": "Provides OHDSI within the workspace",
"required": [],
"properties": {
"display_name": {
"type": "string",
"title": "Name for the workspace service",
"description": "The name of the workspace service to be displayed to users",
"default": "OHDSI ATLAS",
"updateable": true
},
"description": {
"type": "string",
"title": "Description of the workspace service",
"description": "Description of the workspace service",
"default": "OHDSI empowers researchers and collaborators to bring out the value of health data through large-scale analytics and open-source solutions.",
"updateable": true
},
"overview": {
"type": "string",
"title": "Workspace Service Overview",
"description": "Long form description of the workspace service, in markdown syntax",
"default": "OHDSI (Observational Health Data Sciences and Informatics) is a community-driven open-source project that provides tools and resources to help researchers and clinicians make better decisions about healthcare. OHDSI's Common Data Model (CDM) is a standardized format for storing and analyzing observational health data. The CDM can be used to create a variety of machine learning models, including patient-level prediction models, population-level effect estimation models, and risk prediction models.",
"updateable": true
},
"address_space": {
"$id": "#/properties/address_space",
"type": "string",
"title": "Address space",
"description": "Address space for PostgreSQL's subnet"
},
"configure_data_source": {
"type": "boolean",
"title": "Configure Data Source",
"default": true,
"updateable": true
}
},
"allOf": [
{
"if": {
"properties": {
"configure_data_source": {
"const": true
}
},
"required": [
"configure_data_source"
]
},
"then": {
"properties": {
"data_source_config": {
"type": "object",
"title": "Data Source Configuration",
"default": null,
"properties": {
"dialect": {
"type": "string",
"title": "Dialect",
"default": "Azure Synapse",
"enum": [
"Azure Synapse"
],
"updateable": true
},
"source_name": {
"type": "string",
"title": "Source Name"
},
"source_key": {
"type": "string",
"title": "Source Key",
"description": "A unique source key"
},
"connection_string": {
"type": "string",
"title": "Connection String"
},
"username": {
"type": "string",
"title": "Username"
},
"password": {
"type": "string",
"title": "Password",
"format": "password"
}
},
"required": [
"source_name",
"dialect",
"source_key",
"connection_string",
"username",
"password"
]
},
"data_source_daimons": {
"type": "object",
"title": "Source Daimons",
"description": "Configure source daimons",
"default": null,
"properties": {
"daimon_cdm": {
"type": "string",
"title": "CDM"
},
"daimon_vocabulary": {
"type": "string",
"title": "Vocabulary"
},
"daimon_results": {
"type": "string",
"title": "Results Schema to copy"
},
"daimon_cem": {
"type": "string",
"title": "CEM"
},
"daimon_cem_results": {
"type": "string",
"title": "CEMResults"
},
"daimon_temp": {
"type": "string",
"title": "Temp Schema to copy"
}
},
"required": [
"daimon_cdm",
"daimon_vocabulary",
"daimon_results"
]
}
}
}
}
],
"uiSchema": {
"address_space": {
"classNames": "tre-hidden"
}
},
"pipeline": {
"install": [
{
"stepId": "b9d7370e-c624-4263-921c-632329974872",
"stepTitle": "Upgrade workspace to ensure the existence of Postgres' address space",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": []
},
{
"stepId": "main"
},
{
"stepId": "7276dcc1-7d0e-496a-badf-87c8c25fc06e",
"stepTitle": "Add Atlas callback URI as AAD redirect URI",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": [
{
"name": "aad_redirect_uris",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "{{ resource.id }}",
"value": "{{ resource.properties.authentication_callback_uri }}"
}
}
]
},
{
"stepId": "37d7fbde-fd61-4096-ac4d-741960474995",
"stepTitle": "Add firewall rules for AAD",
"resourceTemplateName": "tre-shared-service-firewall",
"resourceType": "shared-service",
"resourceAction": "upgrade",
"properties": [
{
"name": "network_rule_collections",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "nrc_svc_{{ resource.id }}",
"action": "Allow",
"rules": [
{
"name": "AzureAD",
"description": "Allow access to AAD",
"source_addresses": "{{ resource.parent.properties.address_spaces }}",
"destination_addresses": [
"AzureActiveDirectory"
],
"destination_ports": [
"*"
],
"protocols": [
"TCP"
]
}
]
}
},
{
"name": "rule_collections",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "arc_svc_{{ resource.id }}",
"action": "Allow",
"rules": [
{
"name": "microsoft-aad",
"description": "Allow AAD FQDNs",
"source_addresses": "{{ resource.parent.properties.address_spaces }}",
"target_fqdns": [
"*.msftauth.net",
"*.msauth.net",
"login.microsoftonline.com",
"aadcdn.msftauthimages.net",
"aadcdn.msauthimages.net",
"*.login.live.com",
"*.microsoftonline-p.com",
"msft.sts.microsoft.com"
],
"protocols": [
{
"port": "80",
"type": "Http"
},
{
"port": "443",
"type": "Https"
}
]
}
]
}
}
]
}
],
"upgrade": [
{
"stepId": "01dfec9b-ecc5-42c4-a022-050930a29916",
"stepTitle": "Upgrade workspace to ensure the existence of Postgres' address space",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": []
},
{
"stepId": "main"
},
{
"stepId": "0d3961fd-1538-4b0e-a6ed-bf401b65c034",
"stepTitle": "Upgrade Atlas callback URI as AAD redirect URI",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": [
{
"name": "aad_redirect_uris",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "{{ resource.id }}",
"value": "{{ resource.properties.authentication_callback_uri }}"
}
}
]
},
{
"stepId": "3329d760-3e09-4721-8722-f369b123ca77",
"stepTitle": "Add firewall rules for AAD",
"resourceTemplateName": "tre-shared-service-firewall",
"resourceType": "shared-service",
"resourceAction": "upgrade",
"properties": [
{
"name": "network_rule_collections",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "nrc_svc_{{ resource.id }}",
"action": "Allow",
"rules": [
{
"name": "AzureAD",
"description": "Allow access to AAD",
"source_addresses": "{{ resource.parent.properties.address_spaces }}",
"destination_addresses": [
"AzureActiveDirectory"
],
"destination_ports": [
"*"
],
"protocols": [
"TCP"
]
}
]
}
},
{
"name": "rule_collections",
"type": "array",
"arraySubstitutionAction": "replace",
"arrayMatchField": "name",
"value": {
"name": "arc_svc_{{ resource.id }}",
"action": "Allow",
"rules": [
{
"name": "microsoft-aad",
"description": "Allow AAD FQDNs",
"source_addresses": "{{ resource.parent.properties.address_spaces }}",
"target_fqdns": [
"*.msftauth.net",
"*.msauth.net",
"login.microsoftonline.com",
"aadcdn.msftauthimages.net",
"aadcdn.msauthimages.net",
"*.login.live.com",
"*.microsoftonline-p.com",
"msft.sts.microsoft.com"
],
"protocols": [
{
"port": "80",
"type": "Http"
},
{
"port": "443",
"type": "Https"
}
]
}
]
}
}
]
}
],
"uninstall": [
{
"stepId": "e1986fe8-b1f9-4a9d-abb1-da1ea9a50b41",
"stepTitle": "Remove Atlas callback URI as AAD redirect URI",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": [
{
"name": "aad_redirect_uris",
"type": "array",
"arraySubstitutionAction": "remove",
"arrayMatchField": "name",
"value": {
"name": "{{ resource.id }}"
}
}
]
},
{
"stepId": "main"
}
]
}
}

Просмотреть файл

@ -0,0 +1,62 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/azurerm" {
version = "3.58.0"
constraints = "3.58.0"
hashes = [
"h1:Hvlt3hgTiip6xMeq8/EDGqF8NoVuZjYdTZdO79YNXsw=",
"zh:22b19802605ca3e2b811e33650438be3647748cf8f75474c78448c30ac1cad0b",
"zh:402ce010f4b68337abaccf8059c37294cabcbdbc3cefd9491dcd312e36ceea3c",
"zh:53d2cd15f1631c7ffb47918064d644899cc671d47c72f4dafee4e2a5e69afd14",
"zh:5a6b1c55629cff555472d1d43ad6e802693f7fd046c7d37718d4de6f52dbf66b",
"zh:6181dccb7bca7cd84b0295a0332f19a7347a9586101f0a5e51b53bda1ec74651",
"zh:854181d6a8821b3707775c913e91dd7944fcb55098953ef030168fa3cd0224aa",
"zh:b44c758424d1a037fd833e0c69b29e3ac4047ab95653bb3e080835e55bd9badb",
"zh:b6ee916a1579bba29b1aacce8897c6733fa97ba0dba2808f1ffa9ab492743fab",
"zh:b7ab57044649578410dadfdf4412fc5f8aa085a25ea0b061393e843b49b43b63",
"zh:cb68ddb922eb4be74dedf58c953d7f778b4e5f3cdcbe2ea83e02b12296ce4969",
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
"zh:fe9e86173134cd9dc8ed65eae8634abc6d6f6806b5b412f54fccf4047052daa0",
]
}
provider "registry.terraform.io/hashicorp/local" {
version = "2.4.0"
constraints = "2.4.0"
hashes = [
"h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=",
"zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9",
"zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf",
"zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:82a803f2f484c8b766e2e9c32343e9c89b91997b9f8d2697f9f3837f62926b35",
"zh:9708a4e40d6cc4b8afd1352e5186e6e1502f6ae599867c120967aebe9d90ed04",
"zh:973f65ce0d67c585f4ec250c1e634c9b22d9c4288b484ee2a871d7fa1e317406",
"zh:c8fa0f98f9316e4cfef082aa9b785ba16e36ff754d6aba8b456dab9500e671c6",
"zh:cfa5342a5f5188b20db246c73ac823918c189468e1382cb3c48a9c0c08fc5bf7",
"zh:e0e2b477c7e899c63b06b38cd8684a893d834d6d0b5e9b033cedc06dd7ffe9e2",
"zh:f62d7d05ea1ee566f732505200ab38d94315a4add27947a60afa29860822d3fc",
"zh:fa7ce69dde358e172bd719014ad637634bbdabc49363104f4fca759b4b73f2ce",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
constraints = "3.5.1"
hashes = [
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

Просмотреть файл

@ -0,0 +1,227 @@
resource "random_password" "postgres_admin_password" {
length = 32
special = false
}
resource "random_password" "postgres_webapi_admin_password" {
length = 32
special = false
}
resource "random_password" "postgres_webapi_app_password" {
length = 32
special = false
}
resource "azurerm_key_vault_secret" "postgres_admin_password" {
name = "postgres-admin-password-${local.short_service_id}"
key_vault_id = data.azurerm_key_vault.ws.id
value = random_password.postgres_admin_password.result
tags = local.tre_workspace_service_tags
}
resource "azurerm_key_vault_secret" "postgres_webapi_admin_password" {
name = "ohdsi-admin-password-${local.short_service_id}"
key_vault_id = data.azurerm_key_vault.ws.id
value = random_password.postgres_webapi_admin_password.result
tags = local.tre_workspace_service_tags
}
resource "azurerm_key_vault_secret" "postgres_webapi_app_password" {
name = "ohdsi-app-password-${local.short_service_id}"
key_vault_id = data.azurerm_key_vault.ws.id
value = random_password.postgres_webapi_app_password.result
tags = local.tre_workspace_service_tags
}
resource "azurerm_network_security_group" "postgres" {
name = "nsg-psql-${local.service_suffix}"
resource_group_name = data.azurerm_resource_group.ws.name
location = data.azurerm_resource_group.ws.location
tags = local.tre_workspace_service_tags
lifecycle { ignore_changes = [tags] }
security_rule {
name = "AllowWebAppsToPostgres"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "5432"
source_address_prefixes = [data.azurerm_subnet.web_app.address_prefix]
destination_address_prefixes = azurerm_subnet.postgres.address_prefixes
}
security_rule {
name = "AllowResourceProcessorToPostgres"
priority = 101
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "5432"
source_address_prefixes = [data.azurerm_subnet.resource_processor.address_prefix]
destination_address_prefixes = azurerm_subnet.postgres.address_prefixes
}
security_rule {
name = "DenyInboundOverride"
priority = 4096
direction = "Inbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
security_rule {
name = "DenyOutboundOverride"
priority = 4096
direction = "Outbound"
access = "Deny"
protocol = "*"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "*"
destination_address_prefix = "*"
}
}
resource "azurerm_subnet" "postgres" {
name = "PostgreSQLSubnet${local.short_service_id}"
virtual_network_name = data.azurerm_virtual_network.ws.name
resource_group_name = data.azurerm_resource_group.ws.name
address_prefixes = [var.address_space]
delegation {
name = "psql-delegation"
service_delegation {
name = "Microsoft.DBforPostgreSQL/flexibleServers"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
}
}
resource "azurerm_subnet_network_security_group_association" "postgres" {
subnet_id = azurerm_subnet.postgres.id
network_security_group_id = azurerm_network_security_group.postgres.id
}
resource "terraform_data" "postgres_core_dns_link" {
provisioner "local-exec" {
environment = {
RESOURCE_GROUP = local.core_resource_group_name
DNS_ZONE_NAME = data.azurerm_private_dns_zone.postgres.name
VNET = data.azurerm_virtual_network.core.name
}
command = "../scripts/postgres_dns_link.sh"
}
}
resource "terraform_data" "postgres_subnet_wait" {
provisioner "local-exec" {
command = "sleep 30"
}
depends_on = [
azurerm_subnet.postgres,
azurerm_subnet_network_security_group_association.postgres,
terraform_data.postgres_core_dns_link
]
}
resource "azurerm_postgresql_flexible_server" "postgres" {
name = "psql-server-${local.service_suffix}"
resource_group_name = data.azurerm_resource_group.ws.name
location = data.azurerm_resource_group.ws.location
delegated_subnet_id = azurerm_subnet.postgres.id
private_dns_zone_id = data.azurerm_private_dns_zone.postgres.id
sku_name = var.postgres_sku
version = local.postgres_version
administrator_login = local.postgres_admin_username
administrator_password = azurerm_key_vault_secret.postgres_admin_password.value
storage_mb = var.postgres_storage_size_in_mb
zone = "1"
tags = local.tre_workspace_service_tags
timeouts {
# If this doesn't complete in a realistic time, no point in waiting the full/default 60m
create = "15m"
}
depends_on = [
terraform_data.postgres_subnet_wait,
]
}
resource "azurerm_postgresql_flexible_server_database" "db" {
name = local.postgres_webapi_database_name
server_id = azurerm_postgresql_flexible_server.postgres.id
charset = "utf8"
collation = "en_US.utf8"
}
resource "azurerm_monitor_diagnostic_setting" "postgres" {
name = azurerm_postgresql_flexible_server.postgres.name
target_resource_id = azurerm_postgresql_flexible_server.postgres.id
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.workspace.id
dynamic "enabled_log" {
for_each = local.postgres_server_log_analytics_categories
content {
category = enabled_log.value
retention_policy {
enabled = true
days = 30
}
}
}
metric {
category = "AllMetrics"
enabled = true
retention_policy {
enabled = true
days = 365
}
}
}
resource "terraform_data" "deployment_ohdsi_webapi_init" {
triggers_replace = {
postgres_database_id = azurerm_postgresql_flexible_server_database.db.id
}
provisioner "local-exec" {
environment = {
MAIN_CONNECTION_STRING = "host=${azurerm_postgresql_flexible_server.postgres.fqdn} port=5432 dbname=${local.postgres_webapi_database_name} user=${local.postgres_admin_username} password=${azurerm_key_vault_secret.postgres_admin_password.value} sslmode=require"
OHDSI_ADMIN_CONNECTION_STRING = "host=${azurerm_postgresql_flexible_server.postgres.fqdn} port=5432 dbname=${local.postgres_webapi_database_name} user=${local.postgres_webapi_admin_username} password=${azurerm_key_vault_secret.postgres_webapi_admin_password.value} sslmode=require"
DATABASE_NAME = local.postgres_webapi_database_name
SCHEMA_NAME = local.postgres_schema_name
OHDSI_ADMIN_PASSWORD = azurerm_key_vault_secret.postgres_webapi_admin_password.value
OHDSI_APP_PASSWORD = azurerm_key_vault_secret.postgres_webapi_app_password.value
OHDSI_APP_USERNAME = local.postgres_webapi_app_username
OHDSI_ADMIN_USERNAME = local.postgres_webapi_admin_username
OHDSI_ADMIN_ROLE = local.postgres_webapi_admin_role
OHDSI_APP_ROLE = local.postgres_webapi_app_role
}
command = "sleep 60 && ../scripts/atlas_db_init.sh"
}
depends_on = [
terraform_data.postgres_core_dns_link,
azurerm_subnet_network_security_group_association.postgres
]
}

Просмотреть файл

@ -0,0 +1,36 @@
resource "random_password" "atlas_security_admin_password" {
length = 8
special = false
}
resource "azurerm_key_vault_secret" "atlas_security_admin_password" {
name = "atlas-security-admin-password-${local.short_service_id}"
key_vault_id = data.azurerm_key_vault.ws.id
value = random_password.atlas_security_admin_password.result
tags = local.tre_workspace_service_tags
}
resource "terraform_data" "deployment_atlas_security" {
triggers_replace = {
postgres_database_id = azurerm_postgresql_flexible_server_database.db.id
}
provisioner "local-exec" {
environment = {
OHDSI_ADMIN_CONNECTION_STRING = "host=${azurerm_postgresql_flexible_server.postgres.fqdn} port=5432 dbname=${local.postgres_webapi_database_name} user=${local.postgres_webapi_admin_username} password=${azurerm_key_vault_secret.postgres_webapi_admin_password.value} sslmode=require"
ATLAS_SECURITY_ADMIN_PASSWORD = azurerm_key_vault_secret.atlas_security_admin_password.value
ATLAS_USERS = "admin,${azurerm_key_vault_secret.atlas_security_admin_password.value}"
WEB_API_URL = local.ohdsi_webapi_url
}
command = "../scripts/atlas_security.sh"
}
depends_on = [
azurerm_postgresql_flexible_server_database.db,
terraform_data.deployment_ohdsi_webapi_init,
terraform_data.postgres_core_dns_link,
azurerm_private_endpoint.webapi_private_endpoint,
azurerm_subnet_network_security_group_association.postgres
]
}

Просмотреть файл

@ -0,0 +1,121 @@
resource "azurerm_storage_share" "atlas_ui" {
name = local.atlas_ui_storage_share_name
storage_account_name = data.azurerm_storage_account.stg.name
quota = 1
}
resource "local_file" "config_local" {
content = templatefile("${path.module}/config_local.tftpl", { OHDSI_WEBAPI_URL = local.ohdsi_webapi_url })
filename = local.config_local_file_path
}
resource "azurerm_storage_share_file" "config_local" {
name = "config-local.js"
storage_share_id = azurerm_storage_share.atlas_ui.id
source = local.config_local_file_path
depends_on = [
local_file.config_local
]
}
resource "azurerm_linux_web_app" "atlas_ui" {
name = local.atlas_ui_name
location = data.azurerm_resource_group.ws.location
resource_group_name = data.azurerm_resource_group.ws.name
virtual_network_subnet_id = data.azurerm_subnet.web_app.id
service_plan_id = data.azurerm_service_plan.workspace.id
https_only = true
client_affinity_enabled = false
site_config {
always_on = false
ftps_state = "Disabled"
application_stack {
docker_image = "index.docker.io/${local.atlas_ui_docker_image_name}"
docker_image_tag = local.atlas_ui_docker_image_tag
}
}
storage_account {
access_key = data.azurerm_storage_account.stg.primary_access_key
account_name = data.azurerm_storage_account.stg.name
name = "ui-storage-${local.service_suffix}"
share_name = local.atlas_ui_storage_share_name
type = "AzureFiles"
mount_path = local.atlas_ui_mount_path
}
app_settings = {
WEBSITES_ENABLE_APP_SERVICE_STORAGE = false
WEBSITES_PORT = "8080"
}
logs {
application_logs {
file_system_level = "Information"
}
http_logs {
file_system {
retention_in_days = 7
retention_in_mb = 100
}
}
}
depends_on = [
azurerm_storage_share_file.config_local,
]
tags = local.tre_workspace_service_tags
}
resource "azurerm_private_endpoint" "atlas_ui_private_endpoint" {
name = "pe-${azurerm_linux_web_app.atlas_ui.name}"
location = data.azurerm_resource_group.ws.location
resource_group_name = data.azurerm_resource_group.ws.name
subnet_id = data.azurerm_subnet.services.id
tags = local.tre_workspace_service_tags
private_service_connection {
private_connection_resource_id = azurerm_linux_web_app.atlas_ui.id
name = "psc-${azurerm_linux_web_app.atlas_ui.name}"
subresource_names = ["sites"]
is_manual_connection = false
}
private_dns_zone_group {
name = module.terraform_azurerm_environment_configuration.private_links["privatelink.azurewebsites.net"]
private_dns_zone_ids = [data.azurerm_private_dns_zone.azurewebsites.id]
}
}
resource "azurerm_monitor_diagnostic_setting" "atlas_ui" {
name = azurerm_linux_web_app.atlas_ui.name
target_resource_id = azurerm_linux_web_app.atlas_ui.id
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.workspace.id
dynamic "enabled_log" {
for_each = local.atals_ui_log_analytics_categories
content {
category = enabled_log.value
retention_policy {
enabled = true
days = 30
}
}
}
metric {
category = "AllMetrics"
enabled = true
retention_policy {
enabled = true
days = 365
}
}
}

Просмотреть файл

@ -0,0 +1,33 @@
define([], function () {
var configLocal = {};
// clearing local storage otherwise source cache will obscure the override settings
localStorage.clear();
// WebAPI
configLocal.api = {
name: 'OHDSI',
url: "${OHDSI_WEBAPI_URL}"
};
configLocal.cohortComparisonResultsEnabled = false;
configLocal.userAuthenticationEnabled = true;
configLocal.plpResultsEnabled = false;
configLocal.authProviders = [
{
"name": "OpenID",
"url": "user/login/openid",
"ajax": false,
"icon": "fab fa-openid"
},
{
"name": "Local Security Test DB",
"url": "user/login/db",
"ajax": true,
"icon": "fa fa-database",
"isUseCredentialsForm": true
}
];
return configLocal;
});

Просмотреть файл

@ -0,0 +1,76 @@
data "azurerm_resource_group" "ws" {
name = "rg-${var.tre_id}-ws-${local.short_workspace_id}"
}
data "azurerm_key_vault" "ws" {
name = local.key_vault_name
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_key_vault_secret" "aad_tenant_id" {
name = "auth-tenant-id"
key_vault_id = data.azurerm_key_vault.ws.id
}
data "azurerm_key_vault_secret" "workspace_client_id" {
name = "workspace-client-id"
key_vault_id = data.azurerm_key_vault.ws.id
}
data "azurerm_key_vault_secret" "workspace_client_secret" {
name = "workspace-client-secret"
key_vault_id = data.azurerm_key_vault.ws.id
}
data "azurerm_log_analytics_workspace" "workspace" {
name = "log-${var.tre_id}-ws-${local.short_workspace_id}"
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_storage_account" "stg" {
name = local.storage_name
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_service_plan" "workspace" {
name = "plan-${var.workspace_id}"
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_virtual_network" "ws" {
name = "vnet-${var.tre_id}-ws-${local.short_workspace_id}"
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_virtual_network" "core" {
name = "vnet-${var.tre_id}"
resource_group_name = local.core_resource_group_name
}
data "azurerm_subnet" "web_app" {
name = "WebAppsSubnet"
virtual_network_name = data.azurerm_virtual_network.ws.name
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_subnet" "services" {
name = "ServicesSubnet"
virtual_network_name = data.azurerm_virtual_network.ws.name
resource_group_name = data.azurerm_resource_group.ws.name
}
data "azurerm_subnet" "resource_processor" {
name = "ResourceProcessorSubnet"
resource_group_name = local.core_resource_group_name
virtual_network_name = data.azurerm_virtual_network.core.name
}
data "azurerm_private_dns_zone" "azurewebsites" {
name = module.terraform_azurerm_environment_configuration.private_links["privatelink.azurewebsites.net"]
resource_group_name = local.core_resource_group_name
}
data "azurerm_private_dns_zone" "postgres" {
name = module.terraform_azurerm_environment_configuration.private_links["privatelink.postgres.database.azure.com"]
resource_group_name = local.core_resource_group_name
}

Просмотреть файл

@ -0,0 +1,52 @@
resource "terraform_data" "add_data_source" {
count = var.configure_data_source ? 1 : 0
triggers_replace = {
postgres_database_id = azurerm_postgresql_flexible_server_database.db.id
}
provisioner "local-exec" {
environment = {
OHDSI_WEB_API_URL = local.ohdsi_webapi_fqdn
OHDSI_WEB_API_USER = "admin"
OHDSI_WEB_API_PASSWORD = azurerm_key_vault_secret.atlas_security_admin_password.value
DIALECT = local.dialects[local.data_source_config.dialect]
SOURCE_NAME = local.data_source_config.source_name
SOURCE_KEY = local.data_source_config.source_key
CONNECTION_STRING = local.data_source_config.connection_string
USERNAME = local.data_source_config.username
PASSWORD = local.data_source_config.password
DAIMON_CDM = try(local.data_source_daimons.daimon_cdm, null)
DAIMON_VOCABULARY = try(local.data_source_daimons.daimon_vocabulary, null)
DAIMON_RESULTS = local.results_schema_name
DAIMON_CEM = try(local.data_source_daimons.daimon_cem, null)
DAIMON_CEM_RESULTS = try(local.data_source_daimons.daimon_cem_results, null)
DAIMON_TEMP = local.temp_schema_name
}
command = "../scripts/add_data_source.sh"
}
depends_on = [terraform_data.deployment_atlas_security]
}
resource "terraform_data" "init_synapse_schemas" {
count = local.is_synapse_data_source && local.daimon_results != null && local.daimon_temp != null ? 1 : 0
provisioner "local-exec" {
environment = {
ADMIN_USERNAME = local.data_source_config.username
ADMIN_USER_PASSWORD = local.data_source_config.password
SCHEMA_NAME = local.short_service_id
SQL_FILE_PATH = "../sql/init_results_and_temp_schemas.sql"
SYNAPSE_SERVER = tostring(regex("(?:jdbc:sqlserver://)(.*)(:?:1433)", local.data_source_config.connection_string)[0])
SYNAPSE_DATABASE = tostring(regex("(?:database=)(.*)(:?;user)", local.data_source_config.connection_string)[0])
RESULTS_SCHEMA_NAME = local.results_schema_name
TEMP_SCHEMA_NAME = local.temp_schema_name
ORIGIN_RESULTS_SCHEMA_NAME = local.data_source_daimons.daimon_results
}
command = "../scripts/synapse_runner.sh"
}
}

Просмотреть файл

@ -0,0 +1,14 @@
#!/bin/bash
set -e
# This script is used to install the bundle directly without having to interact with Porter
# This script assumes you have created an .env from the sample and the variables
# will come from there.
# shellcheck disable=SC2154
terraform init -reconfigure -input=false -backend=true \
-backend-config="resource_group_name=${TF_VAR_mgmt_resource_group_name}" \
-backend-config="storage_account_name=${TF_VAR_mgmt_storage_account_name}" \
-backend-config="container_name=${TF_VAR_terraform_state_container_name}" \
-backend-config="key=tre-workspace-service-ohdsi-${TF_VAR_tre_resource_id}"
terraform apply -auto-approve

Просмотреть файл

@ -0,0 +1,15 @@
#!/bin/bash
set -e
# This script is used to uninstall the bundle directly without having to interact with Porter
# This script assumes you have created an .env from the sample and the variables
# will come from there.
# shellcheck disable=SC2154
terraform init -reconfigure -input=false -backend=true \
-backend-config="resource_group_name=${TF_VAR_mgmt_resource_group_name}" \
-backend-config="storage_account_name=${TF_VAR_mgmt_storage_account_name}" \
-backend-config="container_name=${TF_VAR_terraform_state_container_name}" \
-backend-config="key=tre-workspace-service-ohdsi-${TF_VAR_tre_resource_id}"
terraform destroy -auto-approve

Просмотреть файл

@ -0,0 +1,70 @@
locals {
short_service_id = substr(var.tre_resource_id, -4, -1)
short_workspace_id = substr(var.workspace_id, -4, -1)
workspace_resource_name_suffix = "${var.tre_id}-ws-${local.short_workspace_id}"
core_resource_group_name = "rg-${var.tre_id}"
service_suffix = "${local.workspace_resource_name_suffix}-svc-${local.short_service_id}"
key_vault_name = lower("kv-${substr(local.workspace_resource_name_suffix, -20, -1)}")
storage_name = lower(replace("stg${substr(local.workspace_resource_name_suffix, -8, -1)}", "-", ""))
porter_yaml = yamldecode(file("${path.module}/../porter.yaml"))
# ATLAS Database
postgres_admin_username = "postgres_admin"
postgres_webapi_admin_username = "ohdsi_admin_user"
postgres_webapi_admin_role = "ohdsi_admin"
postgres_webapi_app_username = "ohdsi_app_user"
postgres_webapi_app_role = "ohdsi_app"
postgres_webapi_database_name = "atlas_webapi_db"
postgres_schema_name = "webapi"
postgres_version = "14"
postgres_server_log_analytics_categories = [
"PostgreSQLLogs"
]
# ATLAS UI
atlas_ui_name = "app-ohdsi-atlas-${local.service_suffix}"
atlas_ui_fqdn = "${local.atlas_ui_name}.${module.terraform_azurerm_environment_configuration.web_app_suffix}"
atlas_ui_url = "https://${local.atlas_ui_fqdn}/atlas"
atlas_ui_url_welcome = "${local.atlas_ui_url}/#/welcome"
atlas_ui_storage_share_name = "atlas-${local.service_suffix}"
atlas_ui_mount_path = "/etc/atlas"
atlas_ui_docker_image_name = "ohdsi/atlas"
atlas_ui_docker_image_tag = "2.12.1"
config_local_file_path = "/tmp/config-local.js"
atals_ui_log_analytics_categories = [
"AppServiceAppLogs",
"AppServiceConsoleLogs",
"AppServiceHTTPLogs"
]
# OHDSI WEB API
ohdsi_webapi_name = "app-ohdsi-webapi-${local.service_suffix}"
ohdsi_webapi_fqdn = "${local.ohdsi_webapi_name}.${module.terraform_azurerm_environment_configuration.web_app_suffix}"
ohdsi_webapi_url = "https://${local.ohdsi_webapi_fqdn}/WebAPI/"
ohdsi_webapi_url_auth_callback = "${local.ohdsi_webapi_url}user/oauth/callback"
ohdsi_api_docker_image_name = "ohdsi/webapi"
ohdsi_api_docker_image_tag = "2.12.1"
ohdsi_api_flyway_baseline_version = "2.2.5.20180212152023"
ohdsi_api_log_analytics_categories = [
"AppServiceAppLogs",
"AppServiceConsoleLogs",
"AppServiceHTTPLogs"
]
tre_workspace_service_tags = {
tre_id = var.tre_id
tre_workspace_id = var.workspace_id
tre_workspace_service_id = var.tre_resource_id
}
# Data Source configuration
dialects = local.porter_yaml["custom"]["dialects"]
data_source_config = try(jsondecode(base64decode(var.data_source_config)), {})
data_source_daimons = try(jsondecode(base64decode(var.data_source_daimons)), {})
data_source_dialect = try(local.data_source_config.dialect, null)
is_synapse_data_source = var.configure_data_source && local.data_source_dialect == "Azure Synapse"
daimon_results = try(local.data_source_daimons.daimon_results, null)
daimon_temp = try(local.data_source_daimons.daimon_temp, null)
results_schema_name = local.is_synapse_data_source && local.daimon_results != null ? "${local.data_source_daimons.daimon_results}_${replace(var.tre_resource_id, "-", "_")}" : local.daimon_results
temp_schema_name = local.is_synapse_data_source && local.daimon_temp != null ? "${local.data_source_daimons.daimon_temp}_${replace(var.tre_resource_id, "-", "_")}" : local.daimon_temp
}

Просмотреть файл

@ -0,0 +1,162 @@
resource "azurerm_key_vault_secret" "jdbc_connection_string_webapi_admin" {
name = "jdbc-connectionstring-${local.short_service_id}"
key_vault_id = data.azurerm_key_vault.ws.id
value = "jdbc:postgresql://${azurerm_postgresql_flexible_server.postgres.fqdn}:5432/${local.postgres_webapi_database_name}?user=${local.postgres_webapi_admin_username}&password=${azurerm_key_vault_secret.postgres_webapi_admin_password.value}&sslmode=require"
tags = local.tre_workspace_service_tags
}
resource "azurerm_user_assigned_identity" "ohdsi_webapi_id" {
name = "id-ohdsi-webapi-${local.service_suffix}"
location = data.azurerm_resource_group.ws.location
resource_group_name = data.azurerm_resource_group.ws.name
tags = local.tre_workspace_service_tags
}
resource "azurerm_key_vault_access_policy" "ohdsi_webapi" {
key_vault_id = data.azurerm_key_vault.ws.id
tenant_id = azurerm_user_assigned_identity.ohdsi_webapi_id.tenant_id
object_id = azurerm_user_assigned_identity.ohdsi_webapi_id.principal_id
secret_permissions = [
"Get", "List"
]
}
resource "azurerm_linux_web_app" "ohdsi_webapi" {
name = local.ohdsi_webapi_name
location = data.azurerm_resource_group.ws.location
resource_group_name = data.azurerm_resource_group.ws.name
virtual_network_subnet_id = data.azurerm_subnet.web_app.id
service_plan_id = data.azurerm_service_plan.workspace.id
https_only = true
client_affinity_enabled = false
site_config {
always_on = true
ftps_state = "Disabled"
application_stack {
docker_image = "index.docker.io/${local.ohdsi_api_docker_image_name}"
docker_image_tag = local.ohdsi_api_docker_image_tag
}
}
key_vault_reference_identity_id = azurerm_user_assigned_identity.ohdsi_webapi_id.id
identity {
type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.ohdsi_webapi_id.id]
}
app_settings = {
"DATASOURCE_DRIVERCLASSNAME" = "org.postgresql.Driver"
"DATASOURCE_OHDSI_SCHEMA" = local.postgres_schema_name
"DATASOURCE_USERNAME" = local.postgres_webapi_app_username
"DATASOURCE_PASSWORD" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.postgres_webapi_app_password.name})"
"DATASOURCE_URL" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.jdbc_connection_string_webapi_admin.name})"
"FLYWAY_BASELINEDESCRIPTION" = "Base Migration"
"FLYWAY_BASELINEONMIGRATE" = "true"
"flyway_baselineVersionAsString" = local.ohdsi_api_flyway_baseline_version
"FLYWAY_DATASOURCE_DRIVERCLASSNAME" = "org.postgresql.Driver"
"FLYWAY_DATASOURCE_USERNAME" = local.postgres_webapi_admin_username
"FLYWAY_DATASOURCE_PASSWORD" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.postgres_webapi_admin_password.name})"
"FLYWAY_DATASOURCE_URL" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.jdbc_connection_string_webapi_admin.name})"
"FLYWAY_LOCATIONS" = "classpath:db/migration/postgresql"
"FLYWAY_PLACEHOLDERS_OHDSISCHEMA" = local.postgres_schema_name
"FLYWAY_SCHEMAS" = local.postgres_schema_name
"FLYWAY_TABLE" = "schema_history"
"MANAGED_IDENTITY_CLIENT_ID" = azurerm_user_assigned_identity.ohdsi_webapi_id.id
"SECURITY_SSL_ENABLED" = "false"
"SECURITY_CORS_ENABLED" = "true"
"SECURITY_DB_DATASOURCE_AUTHENTICATIONQUERY" = "select password from webapi_security.security where email = ?"
"SECURITY_DB_DATASOURCE_PASSWORD" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.postgres_webapi_admin_password.name})"
"SECURITY_DB_DATASOURCE_SCHEMA" = "webapi_security"
"SECURITY_DB_DATASOURCE_URL" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${azurerm_key_vault_secret.jdbc_connection_string_webapi_admin.name})"
"SECURITY_DB_DATASOURCE_USERNAME" = local.postgres_webapi_admin_username
"SECURITY_DURATION_INCREMENT" = "10"
"SECURITY_DURATION_INITIAL" = "10"
"SECURITY_MAXLOGINATTEMPTS" = "3"
"SECURITY_ORIGIN" = "*"
"SECURITY_PROVIDER" = "AtlasRegularSecurity"
"SPRING_BATCH_REPOSITORY_TABLEPREFIX" = "webapi.BATCH_"
"SPRING_JPA_PROPERTIES_HIBERNATE_DEFAULT_SCHEMA" = local.postgres_schema_name
"SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT" = "org.hibernate.dialect.PostgreSQLDialect"
"WEBSITES_CONTAINER_START_TIME_LIMIT" = "1800"
"WEBSITES_ENABLE_APP_SERVICE_STORAGE" = false
"WEBSITES_PORT" = "8080"
"security.oid.clientId" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${data.azurerm_key_vault_secret.workspace_client_id.name})"
"security.oid.apiSecret" = "@Microsoft.KeyVault(VaultName=${data.azurerm_key_vault.ws.name};SecretName=${data.azurerm_key_vault_secret.workspace_client_secret.name})"
"security.oid.url" = "${module.terraform_azurerm_environment_configuration.active_directory_endpoint}/${data.azurerm_key_vault_secret.aad_tenant_id.value}/v2.0/.well-known/openid-configuration"
"security.oauth.callback.api" = local.ohdsi_webapi_url_auth_callback
"security.oauth.callback.ui" = local.atlas_ui_url_welcome
"security.oid.redirectUrl" = local.atlas_ui_url_welcome
"security.oid.logoutUrl" = local.atlas_ui_url_welcome
}
logs {
application_logs {
file_system_level = "Information"
}
http_logs {
file_system {
retention_in_days = 7
retention_in_mb = 100
}
}
}
tags = local.tre_workspace_service_tags
depends_on = [
terraform_data.deployment_ohdsi_webapi_init
]
}
resource "azurerm_private_endpoint" "webapi_private_endpoint" {
name = "pe-${azurerm_linux_web_app.ohdsi_webapi.name}"
location = data.azurerm_resource_group.ws.location
resource_group_name = data.azurerm_resource_group.ws.name
subnet_id = data.azurerm_subnet.services.id
tags = local.tre_workspace_service_tags
private_service_connection {
private_connection_resource_id = azurerm_linux_web_app.ohdsi_webapi.id
name = "psc-${azurerm_linux_web_app.ohdsi_webapi.name}"
subresource_names = ["sites"]
is_manual_connection = false
}
private_dns_zone_group {
name = module.terraform_azurerm_environment_configuration.private_links["privatelink.azurewebsites.net"]
private_dns_zone_ids = [data.azurerm_private_dns_zone.azurewebsites.id]
}
}
resource "azurerm_monitor_diagnostic_setting" "ohdsi_webapi" {
name = azurerm_linux_web_app.ohdsi_webapi.name
target_resource_id = azurerm_linux_web_app.ohdsi_webapi.id
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.workspace.id
dynamic "enabled_log" {
for_each = local.ohdsi_api_log_analytics_categories
content {
category = enabled_log.value
retention_policy {
enabled = true
days = 30
}
}
}
metric {
category = "AllMetrics"
enabled = true
retention_policy {
enabled = true
days = 365
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
output "connection_uri" {
value = local.atlas_ui_url
description = "Atlas Endpoint"
precondition {
condition = local.atlas_ui_fqdn == azurerm_linux_web_app.atlas_ui.default_hostname
error_message = "Computed FQDN is different than actual one."
}
}
output "webapi_uri" {
value = local.ohdsi_webapi_url
description = "WebAPI Endpoint"
precondition {
condition = local.ohdsi_webapi_fqdn == azurerm_linux_web_app.ohdsi_webapi.default_hostname
error_message = "Computed FQDN is different than actual one."
}
}
output "authentication_callback_uri" {
value = "${local.ohdsi_webapi_url_auth_callback}?client_name=OidcClient"
}
output "is_exposed_externally" {
value = false
}

Просмотреть файл

@ -0,0 +1,40 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.58.0"
}
local = {
source = "hashicorp/local"
version = "=2.4.0"
}
random = {
source = "hashicorp/random"
version = "=3.5.1"
}
}
backend "azurerm" {
}
}
provider "azurerm" {
features {
key_vault {
# Don't purge on destroy (this would fail due to purge protection being enabled on keyvault)
purge_soft_delete_on_destroy = false
purge_soft_deleted_secrets_on_destroy = false
purge_soft_deleted_certificates_on_destroy = false
purge_soft_deleted_keys_on_destroy = false
# When recreating an environment, recover any previously soft deleted secrets - set to true by default
recover_soft_deleted_key_vaults = true
recover_soft_deleted_secrets = true
recover_soft_deleted_certificates = true
recover_soft_deleted_keys = true
}
}
}
module "terraform_azurerm_environment_configuration" {
source = "git::https://github.com/microsoft/terraform-azurerm-environment-configuration.git?ref=0.3.0"
arm_environment = var.arm_environment
}

Просмотреть файл

@ -0,0 +1,36 @@
variable "workspace_id" {}
variable "tre_id" {}
variable "tre_resource_id" {}
variable "arm_environment" {}
variable "address_space" {
type = string
description = "Address space for PostgreSQL's subnet"
}
# ATLAS Database
variable "postgres_sku" {
type = string
default = "B_Standard_B1ms"
description = "The SKU of the PostgreSQL database"
}
variable "postgres_storage_size_in_mb" {
type = number
default = 32768
description = "The storage size of the PostgreSQL database in MB"
}
# Data Source Configuration
variable "configure_data_source" {
type = bool
}
variable "data_source_config" {
type = string
default = null
}
variable "data_source_daimons" {
type = string
default = null
}