Skip to content

Commit

Permalink
Add documentation to configure dicomcast along with privatelink enabl…
Browse files Browse the repository at this point in the history
…ed dicomservice (#2969)

* Add documentsation for dicom cast

* Update template

* Update the document

* Added one more step

* Update document

* Update document

* Added link to the main document.

---------

Co-authored-by: Pooja Adhikari <[email protected]>
  • Loading branch information
poadhika and Pooja Adhikari authored Aug 9, 2023
1 parent 90bf987 commit 2c17002
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 2 deletions.
340 changes: 340 additions & 0 deletions converter/dicom-cast/docs/DicomcastDeploymentTemplate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
```json
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceName": {
"minLength": 3,
"maxLength": 24,
"type": "String",
"metadata": {
"description": "Name of the DICOM Cast service container group."
}
},
"image": {
"defaultValue": "dicomoss.azurecr.io/linux_dicom-cast",
"type": "String",
"metadata": {
"description": "Container image to deploy. Should be of the form repoName/imagename:tag for images stored in public Docker Hub, or a fully qualified URI for other registries. Images from private registries require additional registry credentials."
}
},
"storageAccountSku": {
"defaultValue": "Standard_LRS",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_RAGRS",
"Standard_ZRS",
"Premium_LRS",
"Premium_ZRS",
"Standard_GZRS",
"Standard_RAGZRS"
],
"type": "String"
},
"deployApplicationInsights": {
"defaultValue": true,
"type": "Bool",
"metadata": {
"description": "Deploy Application Insights for the DICOM server. Disabled for Microsoft Azure Government (MAG)"
}
},
"applicationInsightsLocation": {
"defaultValue": "[resourceGroup().location]",
"allowedValues": [
"southeastasia",
"northeurope",
"westeurope",
"eastus",
"southcentralus",
"westus2"
],
"type": "String"
},
"cpuCores": {
"defaultValue": "1.0",
"type": "String",
"metadata": {
"description": "The number of CPU cores to allocate to the container."
}
},
"memoryInGb": {
"defaultValue": "1.5",
"type": "String",
"metadata": {
"description": "The amount of memory to allocate to the container in gigabytes."
}
},
"location": {
"defaultValue": "[resourceGroup().location]",
"type": "String",
"metadata": {
"description": "Location for all resources."
}
},
"restartPolicy": {
"defaultValue": "always",
"allowedValues": [
"never",
"always",
"onfailure"
],
"type": "String",
"metadata": {
"description": "The behavior of Azure runtime if container has stopped."
}
},
"dicomWebEndpoint": {
"type": "String",
"metadata": {
"description": "The endpoint of the DICOM Web server."
}
},
"fhirEndpoint": {
"type": "String",
"metadata": {
"description": "The endpoint of the FHIR server."
}
},
"patientSystemId": {
"type": "String",
"metadata": {
"description": "Patient SystemId configured by the user"
}
},
"isIssuerIdUsed": {
"defaultValue": false,
"type": "Bool",
"metadata": {
"description": "Issuer id or patient system id used based on this boolean value"
}
},
"enforceValidationOfTagValues": {
"defaultValue": false,
"type": "Bool",
"metadata": {
"description": "Enforce validation of all tag values and do not store to FHIR even if only non-required tags are invalid"
}
},
"ignoreJsonParsingErrors": {
"defaultValue": false,
"type": "Bool",
"metadata": {
"description": "Ignore json parsing errors for DICOM instances with malformed DICOM json"
}
},
"additionalEnvironmentVariables": {
"defaultValue": [],
"type": "Array",
"metadata": {
"description": "Array of additional enviornment variables with objects with properties 'name' and 'value'. ex: [{\"name\": \"testName\", \"value\": \"testValue\"}]"
}
},
"virtualNetworkName": {
"type": "String",
"metadata": {
"description": "Virtual network where ACi will be deployed to"
}
},
"subnetName": {
"type": "String",
"metadata": {
"description": "Subnet within a virtual network which is delegated to ACI"
}
}
},
"variables": {
"networkProfileName": "aci-networkProfile",
"interfaceConfigName": "eth0",
"interfaceIpConfig": "ipconfigprofile1",
"isMAG": "[or(contains(resourceGroup().location,'usgov'),contains(resourceGroup().location,'usdod'))]",
"serviceName": "[toLower(parameters('serviceName'))]",
"virtualNetworkName": "[parameters('virtualNetworkName')]",
"subnetName": "[parameters('subnetName')]",
"keyvaultName": "[concat(substring(replace(variables('serviceName'), '-', ''), 0, min(11, length(variables('serviceName')))), uniquestring(resourceGroup().id))]",
"containerGroupResourceId": "[resourceId('Microsoft.ContainerInstance/containerGroups/', variables('serviceName'))]",
"deployAppInsights": "[and(parameters('deployApplicationInsights'),not(variables('isMAG')))]",
"appInsightsName": "[concat('AppInsights-', variables('serviceName'))]",
"storageAccountName": "[concat(substring(replace(variables('serviceName'), '-', ''), 0, min(11, length(variables('serviceName')))), uniquestring(resourceGroup().id))]",
"storageResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
"keyVaultEndpoint": "[if(variables('isMAG'), concat('https://', variables('keyvaultName'), '.vault.usgovcloudapi.net/'), concat('https://', variables('keyvaultName'), '.vault.azure.net/'))]",
"keyVaultResourceId": "[resourceId('Microsoft.KeyVault/vaults', variables('keyvaultName'))]",
"networkProfileResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]",
"environmentVariables": [
{
"name": "Fhir__Endpoint",
"value": "[parameters('fhirEndpoint')]"
},
{
"name": "DicomWeb__Endpoint",
"value": "[parameters('dicomWebEndpoint')]"
},
{
"name": "KeyVault__Endpoint",
"value": "[variables('keyVaultEndpoint')]"
},
{
"name": "DicomCast__Features__EnforceValidationOfTagValues",
"value": "[parameters('enforceValidationOfTagValues')]"
},
{
"name": "DicomCast__Features__IgnoreJsonParsingErrors",
"value": "[parameters('ignoreJsonParsingErrors')]"
},
{
"name": "Patient__PatientSystemId",
"value": "[parameters('patientSystemId')]"
},
{
"name": "Patient__IsIssuerIdUsed",
"value": "[parameters('isIssuerIdUsed')]"
}
]
},
"resources": [
{
"type": "Microsoft.Network/networkProfiles",
"apiVersion": "2020-11-01",
"name": "[variables('serviceName')]",
"location": "[parameters('location')]",
"properties": {
"containerNetworkInterfaceConfigurations": [
{
"name": "[variables('interfaceConfigName')]",
"properties": {
"ipConfigurations": [
{
"name": "[variables('interfaceIpConfig')]",
"properties": {
"subnet": {
"id": "[variables('networkProfileResourceId')]"
}
}
}
]
}
}
]
}
},
{
"type": "Microsoft.ContainerInstance/containerGroups",
"apiVersion": "2018-10-01",
"name": "[variables('serviceName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[concat('Microsoft.Insights/components/', variables('appInsightsName'))]"
],
"identity": {
"type": "SystemAssigned"
},
"properties": {
"containers": [
{
"name": "[variables('serviceName')]",
"properties": {
"image": "[parameters('image')]",
"resources": {
"requests": {
"cpu": "[parameters('cpuCores')]",
"memoryInGb": "[parameters('memoryInGb')]"
}
},
"environmentVariables": "[concat(variables('environmentVariables'), parameters('additionalEnvironmentVariables'), array(createObject('name', 'ApplicationInsights__InstrumentationKey', 'value', if(variables('deployAppInsights'), reference(concat('Microsoft.Insights/components/', variables('appInsightsName'))).InstrumentationKey, ''))))]"
}
}
],
"osType": "Linux",
"networkProfile": {
"id": "[resourceId('Microsoft.Network/networkProfiles', variables('serviceName'))]"
},
"restartPolicy": "[parameters('restartPolicy')]"
}
},
{
"type": "Microsoft.Insights/components",
"apiVersion": "2015-05-01",
"name": "[variables('appInsightsName')]",
"location": "[parameters('applicationInsightsLocation')]",
"tags": {
"[concat('hidden-link:', variables('containerGroupResourceId'))]": "Resource",
"displayName": "AppInsightsComponent"
},
"kind": "web",
"properties": {
"Application_Type": "web",
"ApplicationId": "[variables('serviceName')]"
},
"condition": "[variables('deployAppInsights')]"
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageAccountName')]",
"location": "[resourceGroup().location]",
"tags": {},
"sku": {
"name": "[parameters('storageAccountSku')]"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": "true"
}
},
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2015-06-01",
"name": "[variables('keyvaultName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[variables('containerGroupResourceId')]"
],
"tags": {},
"properties": {
"sku": {
"family": "A",
"name": "Standard"
},
"tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
"accessPolicies": [
{
"tenantId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]",
"objectId": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]",
"permissions": {
"secrets": [
"get",
"list",
"set"
]
}
}
],
"enabledForDeployment": false
}
},
{
"type": "Microsoft.KeyVault/vaults/secrets",
"apiVersion": "2015-06-01",
"name": "[concat(variables('keyvaultName'), '/TableStore--ConnectionString')]",
"dependsOn": [
"[variables('keyVaultResourceId')]",
"[variables('storageResourceId')]"
],
"properties": {
"contentType": "text/plain",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageResourceId'), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value, ';')]"
}
}
],
"outputs": {
"containerTenantId": {
"type": "String",
"value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.tenantId]"
},
"containerPrincipalId": {
"type": "String",
"value": "[reference(variables('containerGroupResourceId'), '2018-10-01', 'Full').Identity.principalId]"
}
}
}
Binary file added converter/dicom-cast/docs/image-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added converter/dicom-cast/docs/image-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added converter/dicom-cast/docs/image-3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added converter/dicom-cast/docs/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions converter/dicom-cast/docs/workingWithPrivateLink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Configuration steps for DICOM Cast to work with Private Link enabled DICOM

1. Create a Virtual Network with two subnets within the same subscription and region as you would plan to create the Health Data Services Workspace.
1. Default subnet
2. Subnet delegated to Microsoft.containerinstance/containergroups
![Alt text](image.png)

2. Provision Health Data Services workspace, DICOM and FHIR in the same region.
3. Enable Private Link to Health Data Service Workspace. This Private Link would use the Virtual Network created in step 1 and default subnet
![Alt text](image-1.png)

4. Use the template given [here](DicomcastDeploymentTemplate.md) to deploy DICOM Cast within a Virtual Network created in step 1. This will use the subnet that is delegated to Microsoft.containerinstance/containergroups as shown in picture in step 1.
5. Add the following role assignments to Health Data Service Workspace on container instances System Assigned Managed Identity.
1. DICOM Data Owner
2. FHIR Data Contributor
![Alt text](image-2.png)

6. DICOM Cast needs to talk to table storage within the storage account for processing Change Feed. Enable Private Link to the storage account created as a part of template deployment in step 4. This Private Link should also be in same vnet and default subnet that is created in step 1.
![Alt text](image-3.png)

7. Disable public network access for the storage account (`Security + Networking` > `Networking` > `Firewalls and virtual networks` > `Public Network Acccess` > `Disabled`)

8. Ensure all the fields mentioned here are populated in the key vault provisioned in step 4. https://github.com/microsoft/dicom-server/blob/main/docs/how-to-guides/sync-dicom-metadata-to-fhir.md#update-key-vault-for-dicom-cast

9. Restart DICOM Cast container. It should be running successfully.

2 changes: 1 addition & 1 deletion docs/how-to-guides/sync-dicom-metadata-to-fhir.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ For both your FHIR and DICOM servers, you will set the Audience and Authority fo
| DICOM--Endpoint | ```<dicom-server-url>``` |
| DicomWeb--Authentication--Enabled | true |
| DicomWeb--Authentication--AuthenticationType | ManagedIdentity |
| DicomWeb--Authentication--ManagedIdentityCredential--Resource | ```<dicom-server-url>``` |
| DicomWeb--Authentication--ManagedIdentityCredential--Resource | ```<dicom-audience>``` |

#### FHIR Server Secrets

Expand Down
2 changes: 1 addition & 1 deletion docs/quickstarts/deploy-dicom-cast.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,4 @@ These three instances will be hosted within the same Resource Group and App Serv

## Summary

In this Quickstart, you will learned how to deploy DICOM Cast for your Medical Imaging Server for DICOM. Reference the [DICOM Cast Concept](../concepts/dicom-cast.md) to learn more. To start using DICOM Cast, see [Sync DICOM Metadata to FHIR&trade; with DICOM Cast](../how-to-guides/sync-dicom-metadata-to-fhir.md).
In this Quickstart, you will learned how to deploy DICOM Cast for your Medical Imaging Server for DICOM. Reference the [DICOM Cast Concept](../concepts/dicom-cast.md) to learn more. To start using DICOM Cast, see [Sync DICOM Metadata to FHIR&trade; with DICOM Cast](../how-to-guides/sync-dicom-metadata-to-fhir.md). To configure DICOM Cast with Private Link enabled DICOM Service and FHIR Service, see the instructions here: [DICOM Cast Configuration with Private Link enabled DICOM and FHIR](../../converter/dicom-cast/docs/workingWithPrivateLink.md)

0 comments on commit 2c17002

Please sign in to comment.