# Set up Azure SQL Database in the AppHost

<Image
  src={sqlServerIcon}
  alt="Azure SQL Database logo"
  width={100}
  height={100}
  class:list={'float-inline-left icon'}
  data-zoom-off
/>

This article is the reference for the Aspire Azure SQL Database Hosting integration. It enumerates the AppHost APIs — with examples for both `AppHost.cs` and `apphost.mts` — that you use to model Azure SQL server and database resources in your [`AppHost`](/get-started/app-host/) project.

If you're new to the Azure SQL Database integration, start with the [Get started with Azure SQL Database integrations](/integrations/cloud/azure/azure-sql-database/azure-sql-database-get-started/) guide. For how consuming apps read the connection information this page exposes, see [Connect to Azure SQL Database](../azure-sql-database-connect/). For the Azure SQL Database Entity Framework Core client integration, see [Azure SQL Database EF Core integration](/integrations/databases/efcore/azure-sql/azure-sql-get-started/).

## Installation

To start building an Aspire app that uses Azure SQL Database, install the [📦 Aspire.Hosting.Azure.Sql](https://www.nuget.org/packages/Aspire.Hosting.Azure.Sql) NuGet package:

```bash title="Terminal"
aspire add azure-sql
```

<LearnMore>
  Learn more about [`aspire add`](/reference/cli/commands/aspire-add/) in the
  command reference.
</LearnMore>

Or, choose a manual installation approach:

```csharp title="C# — AppHost.cs"
#:package Aspire.Hosting.Azure.Sql@*
```

```xml title="XML — AppHost.csproj"
<PackageReference Include="Aspire.Hosting.Azure.Sql" Version="*" />
```

```bash title="Terminal"
aspire add azure-sql
```

<LearnMore>
  Learn more about [`aspire add`](/reference/cli/commands/aspire-add/) in the
  command reference.
</LearnMore>

This updates your `aspire.config.json` with the Azure SQL Database hosting integration package:

```json title="aspire.config.json" ins={3}
{
  "packages": {
    "Aspire.Hosting.Azure.Sql": "13.3.0"
  }
}
```

## Add Azure SQL server resource and database

Once you've installed the hosting integration in your AppHost project, you can add an Azure SQL server resource and then add a database resource as shown in the following examples:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var sql = builder.AddAzureSqlServer("sql");
var db = sql.AddDatabase("database");

var exampleProject = builder.AddProject<Projects.ExampleProject>("apiservice")
.WithReference(db);

// After adding all resources, run the app...
builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const sql = await builder.addAzureSqlServer("sql");
const db = await sql.addDatabase("database");

await builder.addNodeApp("api", "./api", "index.js")
    .withReference(db);

// After adding all resources, run the app...
await builder.build().run();
````

1. The `AddAzureSqlServer` (or `addAzureSqlServer`) call models an Azure SQL Server that is provisioned in Azure when you publish your app.

1. Calling `AddDatabase` (or `addDatabase`) on the returned resource builder models a named database within that server. The `WithReference` (or `withReference`) call configures a named connection in the consuming project that matches the database resource name.

1. When you call `AddAzureSqlServer`, it implicitly calls `AddAzureProvisioning` — which adds support for generating Azure resources dynamically during app startup. The app must configure the appropriate subscription and location.
**Caution:** When you call `AddAzureSqlServer`, it implicitly calls `AddAzureProvisioning`
  — which adds support for generating Azure resources dynamically during app
  startup. The app must configure the appropriate subscription and location. For
  more information, see [Local provisioning:
  Configuration](/integrations/cloud/azure/local-provisioning/#configuration).
**Note:** When you reference an Azure SQL Database resource from the AppHost, Aspire
  makes several properties available to the consuming project, such as the
  server hostname, port, connection URI, and JDBC connection string. For a
  complete list of these properties and per-language connection examples, see
  [Connect to Azure SQL Database](../azure-sql-database-connect/).

## Run Azure SQL server resource as a container

During local development and testing, you can run an Azure SQL server as a local SQL Server container instead of provisioning an actual Azure resource. Call `RunAsContainer` (or `runAsContainer`) to switch to a local container:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var sql = builder.AddAzureSqlServer("sql")
.RunAsContainer();

var db = sql.AddDatabase("database");

var exampleProject = builder.AddProject<Projects.ExampleProject>("apiservice")
.WithReference(db);

// After adding all resources, run the app...
builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const sql = await builder.addAzureSqlServer("sql");
await sql.runAsContainer();

const db = await sql.addDatabase("database");

await builder.addNodeApp("api", "./api", "index.js")
    .withReference(db);

// After adding all resources, run the app...
await builder.build().run();
````

When `RunAsContainer` is active, Aspire pulls a SQL Server container image and runs it locally. The consuming project receives the same connection environment variables it would receive when deployed to Azure, so your code works without modification between local and cloud environments.
**Tip:** `RunAsContainer` accepts an optional delegate (C#) or options object
  (TypeScript) that lets you customize the underlying `SqlServerServerResource`
  configuration. For example, you can add a data volume or data bind mount. For
  more information, see [SQL Server hosting
  integration](/integrations/databases/sql-server/sql-server-host/#add-sql-server-resource-with-data-volume).

## Connect to an existing Azure SQL server

You might have an existing Azure SQL Database service that you want to connect to. Chain a call to `AsExisting` (or `asExisting`) to annotate that your resource already exists in Azure:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var existingName = builder.AddParameter("existingSqlServerName");
var existingResourceGroup = builder.AddParameter("existingSqlServerResourceGroup");

var sql = builder.AddAzureSqlServer("sql")
.AsExisting(existingName, existingResourceGroup)
.AddDatabase("database");

builder.AddProject<Projects.ExampleProject>("apiservice")
.WithReference(sql);

// After adding all resources, run the app...
builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const existingName = await builder.addParameter("existingSqlServerName");
const existingResourceGroup = await builder.addParameter("existingSqlServerResourceGroup");

const sql = await builder.addAzureSqlServer("sql");
await sql.asExisting(existingName, existingResourceGroup);
const db = await sql.addDatabase("database");

await builder.addNodeApp("api", "./api", "index.js")
    .withReference(db);

// After adding all resources, run the app...
await builder.build().run();
````

For more information on treating Azure resources as existing resources, see [Use existing Azure resources](/integrations/cloud/azure/overview/#use-existing-azure-resources).

## Provisioning-generated Bicep

If you're new to [Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/overview), it's a domain-specific language for defining Azure resources. With Aspire, you don't need to write Bicep by hand — the provisioning APIs generate Bicep for you. When you publish your app, the generated Bicep is output alongside the manifest file. When you add an Azure SQL Server resource, the following Bicep is generated:

```bicep title="Generated Bicep — sql.bicep"
@description('The location for the resource(s) to be deployed.')
param location string = resourceGroup().location

param principalId string

param principalName string

resource sqlServerAdminManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: take('sql-admin-${uniqueString(resourceGroup().id)}', 63)
  location: location
}

resource sql 'Microsoft.Sql/servers@2024-05-01-preview' = {
  name: take('sql-${uniqueString(resourceGroup().id)}', 63)
  location: location
  properties: {
    administrators: {
      administratorType: 'ActiveDirectory'
      login: sqlServerAdminManagedIdentity.name
      sid: sqlServerAdminManagedIdentity.properties.principalId
      tenantId: subscription().tenantId
      azureADOnlyAuthentication: true
    }
    minimalTlsVersion: '1.2'
    publicNetworkAccess: 'Enabled'
    version: '12.0'
  }
  tags: {
    'aspire-resource-name': 'sql'
  }
}

resource sqlFirewallRule_AllowAllAzureIps 'Microsoft.Sql/servers/firewallRules@2024-05-01-preview' = {
  name: 'AllowAllAzureIps'
  properties: {
    endIpAddress: '0.0.0.0'
    startIpAddress: '0.0.0.0'
  }
  parent: sql
}

resource database 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
  name: 'database'
  location: location
  properties: {
    freeLimitExhaustionBehavior: 'AutoPause'
    useFreeLimit: true
  }
  sku: {
    name: 'GP_S_Gen5_2'
  }
  parent: sql
}

output sqlServerFqdn string = sql.properties.fullyQualifiedDomainName

output name string = sql.name

output sqlServerAdminName string = sql.properties.administrators.login
```

The generated Bicep provisions an Azure SQL Server with a managed identity administrator, TLS 1.2 minimum, and a General Purpose Serverless database with the Azure free offer enabled.

The generated Bicep is a starting point and is influenced by changes to the provisioning infrastructure in C#. Customizations to the Bicep file directly will be overwritten, so make changes through the C# provisioning APIs to ensure they are reflected in the generated files.

### Customize provisioning infrastructure

All Aspire Azure resources are subclasses of the `AzureProvisioningResource` type. This type enables customization of the generated Bicep by providing a fluent API to configure the Azure resources using the `ConfigureInfrastructure` API. For example, you can configure the `sku`, `version`, and more. The following example demonstrates how to customize the Azure SQL Database resource:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var sql = builder.AddAzureSqlServer("sql")
    .ConfigureInfrastructure(infra =>
    {
        var sqlServer = infra.GetProvisionableResources()
            .OfType<SqlServer>()
            .Single();

        sqlServer.MinTlsVersion = SqlMinimalTlsVersion.Tls1_3;

        var database = infra.GetProvisionableResources()
            .OfType<SqlDatabase>()
            .Single();

        database.Sku = new SqlSku { Name = "HS_Gen5_2" }; // Hyperscale
    })
    .AddDatabase("database");

builder.Build().Run();
```
**Note:** TypeScript AppHosts can use curated provisioning helper APIs when an
  integration exposes them. This example directly customizes Azure.Provisioning
  objects through `ConfigureInfrastructure`, which is currently C#-only unless
  the integration wraps the scenario in a polyglot-friendly helper.

The preceding code:

- Chains a call to the `ConfigureInfrastructure` API where the `infra` parameter is an instance of `AzureResourceInfrastructure`.
- Retrieves provisionable resources by calling `GetProvisionableResources`.
- Configures the `SqlServer` with TLS 1.3 minimum.
- Configures the `SqlDatabase` with a Hyperscale SKU instead of the default serverless tier.

For more information, see [Customize Azure resources](/integrations/cloud/azure/customize-resources/). For the full list of configurable properties, see the [Azure.Provisioning.Sql](https://learn.microsoft.com/dotnet/api/azure.provisioning.sql) API documentation.

## Admin deployment script

When you deploy an Azure SQL Server resource, Aspire runs a deployment script that grants your application's managed identity access to the SQL database. This script executes on an [Azure Container Instance (ACI)](https://learn.microsoft.com/azure/container-instances/) and connects to the SQL Server to create the necessary database user and role assignments.

### Private endpoint considerations

When you add a [private endpoint](/integrations/cloud/azure/azure-virtual-network/#add-private-endpoints) to the Azure SQL Server resource, public network access is disabled on the SQL Server. For the deployment script to execute successfully, the ACI it runs on needs access to the SQL Server through the private network. This requires:

- A **subnet** delegated to ACI, so the container runs inside the virtual network.
- An **Azure Storage account**, so ACI can mount a file share for the deployment script contents and logs.

Aspire automatically creates both of these resources when a private endpoint is detected on the Azure SQL Server.

### Customize the deployment script behavior

You can modify the default behavior in the following ways:

#### Disable the deployment script

Call `ClearDefaultRoleAssignments` (or `clearDefaultRoleAssignments`) to disable the deployment script entirely:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var sql = builder.AddAzureSqlServer("sql")
.ClearDefaultRoleAssignments();

builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const sql = await builder.addAzureSqlServer("sql");
await sql.clearDefaultRoleAssignments();

await builder.build().run();
````
**Caution:** When you call `ClearDefaultRoleAssignments`, the deployment script no longer
  adds the app's managed identity access to the database. You must ensure your
  application has the appropriate database access configured separately.

#### Specify a custom subnet

Call `WithAdminDeploymentScriptSubnet` (or `withAdminDeploymentScriptSubnet`) to provide your own subnet for the deployment script container:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var vnet = builder.AddAzureVirtualNetwork("vnet");
var peSubnet = vnet.AddSubnet("pe-subnet", "10.0.2.0/24");
var aciSubnet = vnet.AddSubnet("aci-subnet", "10.0.3.0/29");

var sql = builder.AddAzureSqlServer("sql")
.WithAdminDeploymentScriptSubnet(aciSubnet);
var db = sql.AddDatabase("database");

peSubnet.AddPrivateEndpoint(sql);

builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const vnet = await builder.addAzureVirtualNetwork("vnet");
const peSubnet = await vnet.addSubnet("pe-subnet", "10.0.2.0/24");
const aciSubnet = await vnet.addSubnet("aci-subnet", "10.0.3.0/29");

const sql = await builder.addAzureSqlServer("sql");
await sql.withAdminDeploymentScriptSubnet(aciSubnet);
const db = await sql.addDatabase("database");

await peSubnet.addPrivateEndpoint(sql);

await builder.build().run();
````

#### Specify a custom storage account

Call `WithAdminDeploymentScriptStorage` (or `withAdminDeploymentScriptStorage`) to provide your own storage account for the deployment script:

```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var vnet = builder.AddAzureVirtualNetwork("vnet");
var peSubnet = vnet.AddSubnet("pe-subnet", "10.0.2.0/24");

var storage = builder.AddAzureStorage("scriptstorage");
var sql = builder.AddAzureSqlServer("sql")
.WithAdminDeploymentScriptStorage(storage);
var db = sql.AddDatabase("database");

peSubnet.AddPrivateEndpoint(sql);

builder.Build().Run();

````
```typescript title="TypeScript — apphost.mts" twoslash
import { createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const vnet = await builder.addAzureVirtualNetwork("vnet");
const peSubnet = await vnet.addSubnet("pe-subnet", "10.0.2.0/24");

const storage = await builder.addAzureStorage("scriptstorage");
const sql = await builder.addAzureSqlServer("sql");
await sql.withAdminDeploymentScriptStorage(storage);
const db = await sql.addDatabase("database");

await peSubnet.addPrivateEndpoint(sql);

await builder.build().run();
````
**Caution:** The storage account must be network-accessible from the deployment script's
  Azure Container Instance and must have `AllowSharedKeyAccess` enabled so the
  deployment script can mount a file share.

## Connection properties

For the full reference of Azure SQL Database connection properties — and how consuming apps in C#, TypeScript, Python, and Go read them — see [Connect to Azure SQL Database](../azure-sql-database-connect/).

## Hosting integration health checks

The Azure SQL Database hosting integration automatically adds a health check for the Azure SQL Server resource. The health check verifies that the SQL Server instance is running and that a connection can be established to it.

The hosting integration relies on the [📦 AspNetCore.HealthChecks.SqlServer](https://www.nuget.org/packages/AspNetCore.HealthChecks.SqlServer) NuGet package.