diff --git a/.gitignore b/.gitignore
index e8f6f2147d..f625cec7da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,9 @@ bld/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
+#Visual Studio Code
+.vscode/
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
diff --git a/BaseARMTemplate.json b/BaseARMTemplate.json
index f7fb631b6b..bba9190493 100644
--- a/BaseARMTemplate.json
+++ b/BaseARMTemplate.json
@@ -1,5 +1,5 @@
{
- "$schema": "http://schemas.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceName": {
diff --git a/docs/DefaultDeployment.md b/docs/DefaultDeployment.md
new file mode 100644
index 0000000000..3f5db1793d
--- /dev/null
+++ b/docs/DefaultDeployment.md
@@ -0,0 +1,165 @@
+Microsoft Open Source FHIR Server Deployment
+============================================
+
+This document describes how to deploy the [Microsoft Open Source FHIR Server](https://github.com/Microsoft/fhir-server). The deployment is a two-step process:
+
+1. (Optional) Create and Azure Active Directory (AAD) application registration to secure access to the FHIR server.
+2. Deploy Cosmos DB, Azure Web App, and source code using an Azure Resource Manager template.
+
+Azure Active Directory Application Registration
+-----------------------------------------------
+
+The FHIR server supports token based (JWT) authorization. If authorization is enabled, a user or an application accessing the server must present a token from a specified authority and with a specified audience.
+
+To use the FHIR server, two Azure Active Directory applications must be registered, one for the FHIR server itself and one for a client application accessing the FHIR server. Please refer to the Azure Active Directory documentation for details on the [Web application to Web API scenario](https://docs.microsoft.com/en-us/azure/active-directory/develop/authentication-scenarios#web-application-to-web-api).
+
+Both Azure Active Directory applications can be registered using the Azure Portal. Please refer to the [Azure Active Directory application registration documentation](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v1-integrate-apps-with-azure-ad).
+
+You can also register the applications using the [AzureAD PowerShell module](https://docs.microsoft.com/en-us/powershell/module/azuread/). First register an app for the FHIR server API:
+
+```PowerShell
+# Make sure you are connected to Azure AD:
+Connect-AzureAd
+
+# FHIR audience URL.
+# A good choice is the URL of your FHIR service, e.g:
+$fhirServiceName = "myfhirservice"
+
+# Note: use "https://${fhirServiceName}.azurewebsites.us" for US Government
+$fhirApiAudience = "https://${fhirServiceName}.azurewebsites.net"
+
+# Create the App Registration:
+$apiAppReg = New-AzureADApplication -DisplayName $fhirApiAudience -IdentifierUris $fhirApiAudience
+New-AzureAdServicePrincipal -AppId $apiAppReg.AppId
+
+# Gather some information for the deployment:
+$aadEndpoint = (Get-AzureADCurrentSessionInfo).Environment.Endpoints["ActiveDirectory"]
+$aadTenantId = (Get-AzureADCurrentSessionInfo).Tenant.Id.ToString()
+
+$securityAuthenticationAuthority = "${aadEndpoint}${aadTenantId}"
+$securityAuthenticationAudience = $fhirApiAudience
+
+# Display deployment information
+Write-Host @"
+FHIR Service Name (serviceName): ${fhirServiceName}
+Authority (securityAuthenticationAuthority): ${securityAuthenticationAuthority}
+Audience (securityAuthenticationAudience): ${securityAuthenticationAudience}
+"@
+```
+
+To access the FHIR server from a client application, you will also need a client app registration with a client secret. This client application registration will need to have appropriate application permissions and reply URLs configured. Here is how to register a client app for use with [Postman](https://getpostman.com):
+
+```PowerShell
+# First specify which permissions this app should have.
+
+# Required App permission for Azure AD sign-in
+$reqAad = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
+$reqAad.ResourceAppId = "00000002-0000-0000-c000-000000000000"
+$reqAad.ResourceAccess = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" `
+-ArgumentList "311a71cc-e848-46a1-bdf8-97ff7156d8e6","Scope"
+
+# Required App Permission for the API application registration.
+$reqApi = New-Object -TypeName "Microsoft.Open.AzureAD.Model.RequiredResourceAccess"
+$reqApi.ResourceAppId = $apiAppReg.AppId #From API App registration above
+
+# Just add the first scope (user impersonation)
+$reqApi.ResourceAccess = New-Object -TypeName "Microsoft.Open.AzureAD.Model.ResourceAccess" `
+-ArgumentList $apiAppReg.Oauth2Permissions[0].id,"Scope"
+
+# Application registration for API
+$postmanReplyUrl="https://www.getpostman.com/oauth2/callback"
+
+$clientAppReg = New-AzureADApplication -DisplayName "${fhirServiceName}-postman" `
+-IdentifierUris "https://${fhirServiceName}-postman" `
+-RequiredResourceAccess $reqAad,$reqApi -ReplyUrls $postmanReplyUrl
+
+# Create a client secret
+$clientAppPassword = New-AzureADApplicationPasswordCredential -ObjectId $clientAppReg.ObjectId
+
+# Create Service Principal
+New-AzureAdServicePrincipal -AppId $clientAppReg.AppId
+
+# Write some information needed to use client app
+$clientId = $clientAppReg.AppId
+$clientSecret = $clientAppPassword.Value
+$replyUrl = $clientAppReg.ReplyUrls[0]
+$authUrl = "${securityAuthenticationAuthority}/oauth2/authorize?resource=${securityAuthenticationAudience}"
+$tokenUrl = "${securityAuthenticationAuthority}/oauth2/token"
+
+Write-Host @"
+=== Settings for Postman OAuth2 authentication ===
+
+ Callback URL: ${replyUrl}
+ Auth URL: ${authUrl}
+ Access Token URL: ${tokenUrl}
+ Client ID: ${clientId}
+ Client Secret: ${clientSecret}
+
+"@
+```
+
+Deploying the FHIR Server Template
+----------------------------------
+
+To deploy the backend Cosmos DB, Azure Web App, and FHIR server code, use the buttons below to deploy through the Azure Portal. If you would like to protect the FHIR API with token authorization, you will need to supply application registration details as described above.
+
+
+
+
+
+
+
+
+
+The template can also be deployed using PowerShell. Here is an example of how the authorization details from above can be provided:
+
+```PowerShell
+$rg = New-AzureRmResourceGroup -Name "RG-NAME" -Location westus2
+
+New-AzureRmResourceGroupDeployment `
+-TemplateUri "https://raw.githubusercontent.com/Microsoft/fhir-server/master/samples/templates/default-azuredeploy.json" `
+-ResourceGroupName $rg.ResourceGroupName -serviceName $fhirServiceName `
+-securityAuthenticationAuthority $securityAuthenticationAuthority `
+-securityAuthenticationAudience $securityAuthenticationAudience
+```
+
+To deploy without Authentication/Authorization:
+
+```PowerShell
+$rg = New-AzureRmResourceGroup -Name "RG-NAME" -Location westus2
+
+New-AzureRmResourceGroupDeployment `
+-TemplateUri "https://raw.githubusercontent.com/Microsoft/fhir-server/master/samples/templates/default-azuredeploy.json" `
+-ResourceGroupName $rg.ResourceGroupName -serviceName $fhirServiceName
+```
+
+Testing FHIR Server with Postman
+--------------------------------
+
+You can use [Postman](https://getpostman.com) to test the FHIR server. If you have deployed with Authentication/Authorization enabled, you will need to [configure Azure AD authorization](https://blog.jongallant.com/2017/03/azure-active-directory-access-tokens-postman/
+) to obtain a token. Use type "OAuth 2.0" and set the following parameters:
+
+*Grant Type*: `Auhorization Code`
+
+*Callback URL*: `https://www.getpostman.com/oauth2/callback`
+
+*Auth URL*: `https://login.microsoftonline.com/{TENANT-ID}/oauth2/authorize?resource={AUDIENCE}`
+
+*Access Token URL*: `https://login.microsoftonline.com/{TENANT-ID}/oauth2/token`
+
+*Client ID*: `CLIENT-APP-ID` (see above)
+
+*Client Secret*: `SECRET` (see above)
+
+*Scope*: `Ignored` (not used for Azure AD v1.0 endpoints)
+
+*State*: e.g., `12345`
+
+*Client Authentication*: `Send client credentials in body`
+
+Verify that the following requests return status `200 OK`:
+
+```
+GET https://myfhirservice.azurewebsites.net/metadata
+GET https://myfhirservice.azurewebsites.net/Patient
+```
diff --git a/samples/templates/default-azuredeploy.json b/samples/templates/default-azuredeploy.json
new file mode 100644
index 0000000000..345f246d03
--- /dev/null
+++ b/samples/templates/default-azuredeploy.json
@@ -0,0 +1,238 @@
+{
+ "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ "serviceName": {
+ "type": "string",
+ "metadata": {
+ "description": "Name of the FHIR service Web App."
+ }
+ },
+ "appServicePlanResourceGroup": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Resource group containing App Service Plan. If empty, deployment resource group is used."
+ }
+ },
+ "appServicePlanName": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Name of App Service Plan (existing or new). If empty, a name will be generated."
+ }
+ },
+ "appServicePlanSku": {
+ "type": "string",
+ "allowedValues": [
+ "F1",
+ "D1",
+ "B1",
+ "B2",
+ "B3",
+ "S1",
+ "S2",
+ "S3",
+ "P1",
+ "P2",
+ "P3",
+ "P4"
+ ],
+ "defaultValue": "S1"
+ },
+ "securityAuthenticationAuthority": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "OAuth Authority"
+ }
+ },
+ "securityAuthenticationAudience": {
+ "type": "string",
+ "defaultValue": "",
+ "metadata": {
+ "description": "Audience (aud) to validate in JWT"
+ }
+ },
+ "repositoryUrl": {
+ "type": "string",
+ "defaultValue": "https://github.com/Microsoft/fhir-server",
+ "metadata": {
+ "description": "Respository to pull source code from. If blank, source code will not be deployed."
+ }
+ },
+ "repositoryBranch": {
+ "type": "string",
+ "defaultValue": "master",
+ "metadata": {
+ "description": "Source code branch to deploy."
+ }
+ }
+ },
+ "variables": {
+ "serviceName": "[toLower(parameters('serviceName'))]",
+ "identityResourceId": "[concat(resourceId('Microsoft.Web/sites', variables('serviceName')),'/providers/Microsoft.ManagedIdentity/Identities/default')]",
+ "keyvaultEndpoint": "[concat('https://', variables('serviceName'), '.vault.azure.net/')]",
+ "appServicePlanResourceGroup": "[if(empty(parameters('appServicePlanResourceGroup')), resourceGroup().name, parameters('appServicePlanResourceGroup'))]",
+ "appServicePlanName": "[if(empty(parameters('appServicePlanName')),concat(variables('serviceName'),'-asp'),parameters('appServicePlanName'))]",
+ "securityAuthenticationEnabled": "[and(not(empty(parameters('securityAuthenticationAuthority'))),not(empty(parameters('securityAuthenticationAudience'))))]"
+ },
+ "resources": [
+ {
+ "condition": "[empty(parameters('appServicePlanResourceGroup'))]",
+ "apiVersion": "2017-05-10",
+ "name": "nestedTemplate",
+ "type": "Microsoft.Resources/deployments",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {},
+ "variables": {},
+ "resources": [
+ {
+ "apiVersion": "2015-08-01",
+ "name": "[variables('appServicePlanName')]",
+ "type": "Microsoft.Web/serverfarms",
+ "location": "[resourceGroup().location]",
+ "sku": {
+ "name": "[parameters('appServicePlanSku')]"
+ },
+ "properties": {
+ "name": "[variables('appServicePlanName')]"
+ }
+ }
+ ]
+ }
+ }
+ },
+ {
+ "apiVersion": "2015-08-01",
+ "type": "Microsoft.Web/sites",
+ "name": "[variables('serviceName')]",
+ "location": "[resourceGroup().location]",
+ "identity": {
+ "type": "SystemAssigned"
+ },
+ "properties": {
+ "clientAffinityEnabled": false,
+ "serverFarmId": "[resourceId(variables('appServicePlanResourceGroup'), 'Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
+ "siteConfig": {
+ "appSettings": [
+ {
+ "name": "WEBSITE_NODE_DEFAULT_VERSION",
+ "value": "6.9.4"
+ },
+ {
+ "name": "KEYVAULT_ENDPOINT",
+ "value": "[variables('keyvaultEndpoint')]"
+ },
+ {
+ "name": "Security:Enabled",
+ "value": "[variables('securityAuthenticationEnabled')]"
+ },
+ {
+ "name": "Security:Authentication:Authority",
+ "value": "[parameters('securityAuthenticationAuthority')]"
+ },
+ {
+ "name": "Security:Authentication:Audience",
+ "value": "[parameters('securityAuthenticationAudience')]"
+ }
+ ]
+ }
+ },
+ "dependsOn": [
+ "nestedTemplate"
+ ],
+ "resources": [
+ {
+ "apiVersion": "2015-08-01",
+ "name": "web",
+ "type": "sourcecontrols",
+ "dependsOn": [
+ "[resourceId('Microsoft.Web/Sites', variables('serviceName'))]"
+ ],
+ "properties": {
+ "RepoUrl": "[parameters('repositoryUrl')]",
+ "branch": "[parameters('repositoryBranch')]",
+ "IsManualIntegration": true
+ }
+ }
+ ]
+ },
+ {
+ "apiVersion": "2015-04-08",
+ "type": "Microsoft.DocumentDb/databaseAccounts",
+ "name": "[variables('serviceName')]",
+ "location": "[resourceGroup().location]",
+ "properties": {
+ "name": "[variables('serviceName')]",
+ "databaseAccountOfferType": "Standard",
+ "locations": [
+ {
+ "locationName": "[resourceGroup().location]",
+ "failoverPriority": 0
+ }
+ ]
+ }
+ },
+ {
+ "type": "Microsoft.KeyVault/vaults",
+ "name": "[variables('serviceName')]",
+ "apiVersion": "2015-06-01",
+ "location": "[resourceGroup().location]",
+ "tags": {},
+ "properties": {
+ "sku": {
+ "family": "A",
+ "name": "Standard"
+ },
+ "tenantId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').tenantId]",
+ "accessPolicies": [
+ {
+ "tenantId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').tenantId]",
+ "objectId": "[reference(variables('identityResourceId'), '2015-08-31-PREVIEW').principalId]",
+ "permissions": {
+ "secrets": [
+ "get",
+ "list"
+ ]
+ }
+ }
+ ],
+ "enabledForDeployment": false
+ },
+ "dependsOn": [
+ "[concat('Microsoft.Web/sites/', variables('serviceName'))]"
+ ]
+ },
+ {
+ "type": "Microsoft.KeyVault/vaults/secrets",
+ "name": "[concat(variables('serviceName'), '/DataStore--Host')]",
+ "apiVersion": "2015-06-01",
+ "properties": {
+ "contentType": "text/plain",
+ "value": "[reference(concat('Microsoft.DocumentDb/databaseAccounts/', variables('serviceName'))).documentEndpoint]"
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.KeyVault/vaults', variables('serviceName'))]",
+ "[resourceId('Microsoft.DocumentDb/databaseAccounts', variables('serviceName'))]"
+ ]
+ },
+ {
+ "type": "Microsoft.KeyVault/vaults/secrets",
+ "name": "[concat(variables('serviceName'), '/DataStore--Key')]",
+ "apiVersion": "2015-06-01",
+ "properties": {
+ "contentType": "text/plain",
+ "value": "[listKeys(resourceId('Microsoft.DocumentDb/databaseAccounts', variables('serviceName')), '2015-04-08').primaryMasterKey]"
+ },
+ "dependsOn": [
+ "[resourceId('Microsoft.KeyVault/vaults', variables('serviceName'))]",
+ "[resourceId('Microsoft.DocumentDb/databaseAccounts', variables('serviceName'))]"
+ ]
+ }
+ ]
+}
\ No newline at end of file