Names the Entra ID administrator of an Azure SQL Managed Instance and decides whether SQL authentication survives alongside it — targeting
hashicorp/azurerm ~> 4.0.
- 🔐 Names the Entra ID (Azure AD) administrator of a SQL Managed Instance — a user, group or service principal.
- 🚪 Decides whether SQL authentication survives alongside it.
- 🧬 One resource, two ARM records. The administrator and the Entra-only setting are separate sub-resources; both IDs are emitted.
- 🔀
azuread_authentication_onlydefaults totruehere and tofalsein the provider — and the provider's zero value actively turns the setting off. - ⏳ Its delete timeout is three hours, six times the create.
💡 Why it matters: the default is not a no-op either way. A bare
resourceblock sendsazuread_authentication_only = falseunconditionally, so it disables Entra-only authentication on an instance that may have had it on. This module sendstrueinstead — which is the secure value and a disruptive one, because it stops every SQL login on the instance working. Both directions deserve a decision, so this README states the cost of its own default as plainly as the risk of the alternative.
If this module saved you time:
- ⭐ Star the repository
- 💼 Connect on LinkedIn
- ☕ Buy me a coffee
flowchart TB
MI["terraform-azurerm-mssql-managed-instance"]
THIS["terraform-azurerm-mssql-managed-instance-active-directory-administrator"]
TDE["terraform-azurerm-mssql-managed-instance-transparent-data-encryption"]
SAP["terraform-azurerm-mssql-managed-instance-security-alert-policy"]
KV["terraform-azurerm-key-vault"]
SRV["terraform-azurerm-mssql-server"]
MI -->|"id"| THIS
MI -->|"id"| TDE
MI -->|"name and resource_group_name"| SAP
KV -->|"key_ids or key_versionless_ids"| TDE
MI -->|"identity_principal_id wraps the key"| KV
THIS -.->|"Entra-only auth is a separate ARM record"| MI
SRV -.->|"the logical server sets its admin inline"| THIS
style THIS fill:#0078D4,stroke:#004578,color:#ffffff
style MI fill:#004578,stroke:#004578,color:#ffffff
style TDE fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style SAP fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style KV fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style SRV fill:#F3F2F1,stroke:#8A8886,color:#201F1E
The managed instance feeds its id here and to the encryption-protector module; the security alert policy
takes the parent as a name instead, which is why that node's edge is labelled differently. The dotted
edge back to the instance is the one to read carefully: Entra-only authentication is a separate ARM record
under the instance, written by this same resource. The other dotted edge is a reminder that a logical SQL
server sets its administrator inline on the server resource — there is no separate module for it.
flowchart TB
MID["managed_instance_id (required, force-new)"]
IDENT["login_username plus object_id plus tenant_id"]
AAD["azuread_authentication_only (module TRUE, provider false)"]
ADM["azurerm_mssql_managed_instance_active_directory_administrator.this"]
OID["id"]
OAAD["azuread_only_authentication_id"]
OSQL["sql_authentication_still_permitted"]
ODES["destroying_this_resource_re_enables_sql_authentication"]
MID --> ADM
IDENT --> ADM
AAD --> ADM
ADM --> OID
ADM --> OAAD
ADM --> OSQL
ADM --> ODES
style ADM fill:#0078D4,stroke:#004578,color:#ffffff
style MID fill:#004578,stroke:#004578,color:#ffffff
style IDENT fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style AAD fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style OID fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style OAAD fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style OSQL fill:#F3F2F1,stroke:#8A8886,color:#201F1E
style ODES fill:#F3F2F1,stroke:#8A8886,color:#201F1E
| Resource | Count | Notes |
|---|---|---|
azurerm_mssql_managed_instance_active_directory_administrator.this |
1 | One per instance; writes two ARM records — /administrators/ActiveDirectory and /azureADOnlyAuthentications/Default |
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module — the caller configures the provider, its auth, and the mandatory features {} block |
Schema notes that bite:
- 🔴 One resource, two ARM APIs. The administrator record and the Entra-only authentication setting are separate sub-resources, written by two different clients in the same create and read back from two different APIs.
- 🔴
azuread_authentication_onlyhas NO schema default and is sent unconditionally, so its zero value offalsemeans a bare resource block turns Entra-only authentication off rather than leaving it alone. This module defaults it totrue. - 🔴
terraform destroyre-enables SQL authentication. The service refuses to remove an Entra administrator while Entra-only authentication is on, so the provider disables it first as a required step. - 🔴
object_idis not force-new. Handing administration of the instance to a different principal is an ordinary in-place attribute change, rendered no more prominently in a plan than a display-name correction. ⚠️ The delete timeout is 180 minutes, against 30 for create and update and 5 for read. That is the provider's own value, and it is a signal about how long the service takes.⚠️ login_usernamecarries only a non-empty check and is never reconciled withobject_id. A display name left stale after a group rename is accepted and simply misleads the next reader.- ℹ️ There IS a requires-import guard on create — unlike the managed instance's security alert policy and vulnerability assessment, which silently overwrite. A clean apply here is evidence that no administrator existed.
- ℹ️
tenant_idis required here, while the logical server'sazuread_administratorblock defaults it to the provider's tenant. Same idea, two conventions. - ℹ️ No
tags, nolocation. Tag the managed instance instead.
| Permission | Scope | Why |
|---|---|---|
Microsoft.Sql/managedInstances/administrators/read and /write |
the managed instance | create, update and remove the administrator |
Microsoft.Sql/managedInstances/azureADOnlyAuthentications/read and /write |
the managed instance | the second ARM record this resource writes |
SQL Security Manager |
the managed instance | the role Microsoft documents for enabling and disabling Entra-only authentication, and the least-privilege choice that covers the second ARM record above. Broad roles carrying Microsoft.Sql/*/write (Owner, Contributor) also suffice and grant far more |
SQL Managed Instance Contributor / SQL Server Contributor |
the managed instance | NOT sufficient. Microsoft documents that these can check Entra-only authentication but cannot change it — the administrator write succeeds and the Entra-only write is refused, leaving the half-finished apply described in a_failed_entra_only_write_leaves_the_administrator_in_state |
🔴 Whoever can write this resource can hand instance administration to any principal, in place, with no replacement shown in the plan. That is a higher-privilege operation than the resource's size suggests.
⚠️ No directory permission is needed to name an administrator. Azure accepts an object ID without the Terraform principal being able to read it in Entra, so a typo inobject_idis caught by nothing — it produces an administrator nobody can use.🔒 No secret passes through this module. An administrator is identified here, never authenticated.
- The
Microsoft.Sqlresource provider registered on the subscription. - An existing Azure SQL Managed Instance.
- An existing Entra ID user, group or service principal, and its object ID and tenant ID.
- 🔴 A cutover plan for SQL logins, if
azuread_authentication_onlystays at this module's default oftrue— they stop working at apply. - The caller configures
provider "azurerm" { features {} }, auth and subscription.
terraform-azurerm-mssql-managed-instance-active-directory-administrator/
├── providers.tf # required_version + pinned azurerm; no provider block
├── variables.tf # 6 inputs, deeply typed, with the identity and Entra-only rules
├── main.tf # the single keystone `this`
├── outputs.tf # id first, then both ARM record IDs, then the posture facts
├── README.md # this file
├── SCOPE.md # the cross-module contract
├── LICENSE # MIT
└── .gitignore
provider "azurerm" {
features {}
}
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
}Read example 2 before applying this. The default disables SQL authentication for every login on the instance.
Consumes
| Input | Type | Source |
|---|---|---|
managed_instance_id |
Resource ID | terraform-azurerm-mssql-managed-instance → id |
login_username |
display name | the caller |
object_id |
GUID | the caller |
tenant_id |
GUID | the caller |
Emits
| Output | Description |
|---|---|
id |
Resource ID of the administrator record (first) |
azuread_only_authentication_id |
the second ARM record this module writes |
sql_authentication_still_permitted |
true when the flag was set to false |
object_id / login_username / tenant_id |
who administers the instance |
destroying_this_resource_re_enables_sql_authentication |
constant true |
1 · The secure call, with a group
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
}🔒
azuread_authentication_onlydefaults totrue. Entra ID becomes the only way in.💡 Naming a GROUP rather than a person is the difference between rotating an administrator and rebuilding a configuration. Membership changes without a Terraform apply, and it survives someone leaving. This module cannot tell which kind of principal an object ID names, so it recommends rather than enforces.
2 · What the default actually does to SQL logins
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
# The default, stated explicitly so the plan reads as a decision.
azuread_authentication_only = true
}🔴 Every SQL-authenticated login on the instance stops working at apply — including the built-in administrator login created with the instance. Applications connecting with a SQL username and password fail immediately.
⚠️ It is reversible by setting the flag back tofalse, but it is not a quiet change. Schedule it rather than discovering it.
3 · Keeping SQL authentication during a migration
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
azuread_authentication_only = false
}
output "sql_logins_still_work" {
value = module.mi_entra_admin.sql_authentication_still_permitted # true
}
⚠️ This is the opt-out, and the module makes you type it. It is the right setting while applications are still migrating off SQL logins — and the output exists so "which instances are still like this?" is one expression rather than an audit.
4 · The second ARM record
output "the_two_records_this_writes" {
value = {
administrator = module.mi_entra_admin.id
entra_only = module.mi_entra_admin.azuread_only_authentication_id
}
}🔴 This one Terraform resource writes two ARM records. The administrator lives under
/administrators/ActiveDirectory; Entra-only authentication is a separate sub-resource under/azureADOnlyAuthentications/Default, written by a second client in the same create and read back from a different API.💡 Both IDs are emitted because a policy exemption, an access review or an incident note needs to name the record that actually changed — and only one of them is this resource's
id.
5 · What a destroy quietly does
# Removing this module block does more than remove an administrator.🔴
terraform destroyre-enables SQL authentication. The service refuses to remove an Entra administrator while Entra-only authentication is on, so the provider disables that setting first, as a required step, and then removes the administrator.
⚠️ Nothing in the plan says so. If SQL authentication must stay off, revoke the SQL logins themselves rather than relying on this resource's continued existence.
6 · Handing administration to a different principal
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins-v2"
object_id = var.new_admin_group_object_id # <-- in-place change
tenant_id = var.tenant_id
}🔴 Only
managed_instance_idis force-new. Changingobject_idhands administration of the instance to a different Entra principal as an ordinary in-place attribute change — rendered in a plan no more prominently than the display-name correction beside it.
⚠️ Review changes to this argument the way you would review a role assignment, not the way you would review a tag.
7 · The display name is only a label
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "SQL MI Admins" # a label; spaces are fine
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
}ℹ️
object_idgrants access;login_usernameis what the portal shows. Azure does not reconcile them, so a name left stale after a group rename is accepted and simply misleads. Neither the provider nor this module can detect that.
⚠️ This module refuses only the probable mistake — a GUID or a Resource ID pasted where a display name belongs. It imposes no other shape, because a real display name has none.
8 · A service principal for automation
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sp-sqlmi-automation"
object_id = var.automation_sp_object_id
tenant_id = var.tenant_id
}💡 A service principal works, and with
azuread_authentication_only = trueit connects with a token rather than a password — which is the point: Entra-only authentication removes the credential rather than protecting it.
⚠️ Only one administrator exists per instance, so this replaces a group administrator rather than joining it. Nesting the service principal inside an admin group is usually better.
9 · An explicit tenant, because it is required here
data "azurerm_client_config" "current" {}
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = data.azurerm_client_config.current.tenant_id
}ℹ️
tenant_idis required on this resource, unlike the logical server'sazuread_administratorblock, which falls back to the provider's tenant. Reading it from the provider's own configuration is the closest equivalent.
10 · A three-hour delete deadline
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
timeouts = {
create = "45m"
delete = "180m" # the provider's own default -- do not shorten without a reason
}
}
⚠️ The provider defaults delete to 180 minutes against 30 for create. That six-fold gap is a signal about how long the service can take to disable Entra-only authentication and then remove the administrator.ℹ️ All four deadlines are honoured on this resource — which is not true of every neighbour in this family.
11 · Several instances from one map
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
for_each = module.managed_instances
managed_instance_id = each.value.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
}
output "instances_where_sql_logins_still_work" {
value = [for k, m in module.mi_entra_admin : k if m.sql_authentication_still_permitted]
}💡 Keying by a stable name means adding or removing an instance never re-indexes the rest, and the output turns "which instances still allow SQL authentication?" into one expression.
12 · Adopting an instance that already has an administrator
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = var.tenant_id
}✅ This apply will FAIL rather than overwrite. The provider's create checks for an existing administrator and returns a requires-import error, so an instance already configured in the portal is not silently replaced.
💡 That is worth calling out because two of this module's closest neighbours — the managed instance's security alert policy and vulnerability assessment — have no such guard and do overwrite. Run
terraform importto adopt the existing record.
13 · 🏗️ End-to-end composition
provider "azurerm" {
features {}
}
data "azurerm_client_config" "current" {}
module "sqlmi" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance.git?ref=v1.0.0"
name = "sqlmi-platform-eus2"
resource_group_name = "rg-data-platform"
location = "eastus2"
sku_name = "GP_Gen5"
vcores = 8
storage_size_in_gb = 512
subnet_id = var.managed_instance_subnet_id
license_type = "BasePrice"
# Both are required through this module: the provider pairs each with an
# inline Entra administrator block that this module does not expose.
administrator_login = "sqladmin"
administrator_login_password = var.mi_admin_password # out of band; never committed
}
module "mi_entra_admin" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-active-directory-administrator.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
login_username = "sql-mi-admins"
object_id = var.sql_admin_group_object_id
tenant_id = data.azurerm_client_config.current.tenant_id
azuread_authentication_only = true
}
# The other identity/encryption control on the same instance.
module "mi_tde" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-mssql-managed-instance-transparent-data-encryption.git?ref=v1.0.0"
managed_instance_id = module.sqlmi.id
# No key -- service-managed encryption, which is already secure.
}
output "identity_posture" {
value = {
administrator = module.mi_entra_admin.object_id
entra_only_record = module.mi_entra_admin.azuread_only_authentication_id
sql_logins_work = module.mi_entra_admin.sql_authentication_still_permitted
encryption_key = module.mi_tde.key_kind
}
}🔒 No credential anywhere in this composition. Entra-only authentication removes the SQL password rather than protecting it, and the encryption protector references a Microsoft-managed key.
🔴 Applying this stops SQL logins on
sqlmi-platform-eus2working. That is the intended posture; it is not a quiet one.
Identity of the instance — managed_instance_id (required, force-new).
Identity of the administrator — login_username, object_id, tenant_id (all required, none force-new).
Authentication — azuread_authentication_only.
Tail — timeouts. (There is no tags: the resource exposes none.)
Full schemas
| Name | Type | Default | Notes |
|---|---|---|---|
managed_instance_id |
string |
— | Required, force-new. Anchored; a logical server ID is refused. |
login_username |
string |
— | Required. A label, not an identity. A GUID or Resource ID is refused. |
object_id |
string |
— | Required. GUID. Not force-new — changing it hands over administration. |
tenant_id |
string |
— | Required. GUID. No fallback to the provider's tenant. |
azuread_authentication_only |
bool |
true |
Diverges from the provider's false. Disables all SQL logins. |
timeouts |
object({create, read, update, delete}) |
null |
All four real; delete defaults to 180m. |
| Output | Description |
|---|---|
id |
Resource ID of the administrator record (first) |
azuread_only_authentication_id |
the second ARM record this module writes |
managed_instance_id |
the parent, as configured |
managed_instance_name / resource_group_name / subscription_id |
parsed from the parent ID |
login_username / object_id / tenant_id |
who administers the instance |
azuread_authentication_only |
whether SQL authentication is disabled |
sql_authentication_still_permitted |
true when the flag was set to false |
force_new_fields / fields_that_can_change_after_creation |
lifecycle |
fields_azure_returns_on_read |
where drift is detectable |
one_terraform_resource_writes_two_arm_records |
constant true |
module_default_diverges_from_provider_default |
constant true |
enabling_entra_only_breaks_every_sql_login |
constant true |
destroying_this_resource_re_enables_sql_authentication |
constant true |
the_delete_timeout_is_three_hours |
constant true |
an_existing_administrator_blocks_creation |
true BY DEFAULT — read with the next row |
the_import_guard_can_be_switched_off_by_the_caller |
constant true — the guard is a provider feature flag |
the_entra_administrator_must_exist_before_entra_only_can_be_enabled |
constant true — Microsoft's ordering requirement |
a_failed_entra_only_write_leaves_the_administrator_in_state |
constant true — the half-finished apply |
sql_managed_instance_contributor_cannot_change_entra_only_authentication |
constant true — needs SQL Security Manager |
changing_the_administrator_is_an_in_place_update |
constant true |
login_username_is_a_label_not_an_identity |
constant true |
a_group_is_a_better_administrator_than_a_person |
constant true |
the_tenant_is_required_here_unlike_on_the_logical_server |
constant true |
one_administrator_per_managed_instance |
constant true |
no_secret_is_accepted_or_emitted_by_this_module |
constant true |
this_resource_supports_no_azure_resource_tags |
constant true |
🔒 No secret is accepted or emitted. An administrator is identified here, never authenticated.
One Terraform resource, two ARM records. The administrator lives under /administrators/ActiveDirectory;
Entra-only authentication is a separate sub-resource under /azureADOnlyAuthentications/Default, written by
a second client in the same create and read back from a different API. They can drift independently. Both
Resource IDs are emitted, because only one of them is this resource's id and a review needs to name the
other.
The divergence here prevents an active weakening, not just a no-op. The provider declares
azuread_authentication_only with no default, so its zero value is false — and the create sends that value
unconditionally. A bare resource block therefore disables Entra-only authentication on an instance that may
have had it on. This module defaults to true, matching this suite's Entra-first convention for SQL and the
sibling logical-server module.
And that default is disruptive, which is not a reason to hide it. Enabling Entra-only authentication
stops every SQL-authenticated login working, including the built-in administrator created with the instance.
The README, the variable description and an output all say so, and azuread_authentication_only = false
remains a first-class choice for a migration period.
Destroy is a security change. Because the service will not remove an Entra administrator while Entra-only
authentication is enabled, the provider disables it first as a required step. A terraform destroy therefore
hands SQL authentication back. If that matters, revoke the SQL logins themselves — this resource's absence
guarantees nothing.
Handing over administration is an in-place update. Only managed_instance_id is force-new, so a changed
object_id reads in a plan like any other attribute change while being the most consequential edit this
module allows.
Lifecycle. A lifecycle block is not valid inside a module block, so a caller who wants deletion
protection wants a CanNotDelete management lock on the instance — which prevents deletion, not replacement.
| Concern | Secure default (empty call) | Opt-out (caller must type it) |
|---|---|---|
| Authentication | azuread_authentication_only = true — diverging from the provider's false |
set to false |
| Administrator principal | none assumed — required input | — |
| Directory object shape | a group is recommended and reported, never enforced | name a user or service principal |
| Display-name mistakes | a GUID or Resource ID is refused | — |
| Secret handling | none — no credential passes through this module | not available |
| Existing administrator | refused with a requires-import error | terraform import |
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module with ?ref=v1.0.0 — never a branch. This module is plan-only in this repository; a human
applies from CI.
terraform validate proves, offline and with no credentials: the type schemas, the anchored
managed_instance_id pattern (a logical server ID and a child ID are both refused), the non-empty
login_username check, the GUID-or-Resource-ID-as-display-name refusal, and the UUID shape of object_id
and tenant_id.
Only terraform plan — which needs credentials — exercises: whether the instance exists, whether an
administrator is already configured (the requires-import guard), and whether the object ID names anything
real in the tenant.
Nothing at either stage catches an object ID that is well-formed and wrong. Azure accepts it without the Terraform principal being able to read it in Entra, and the result is an administrator nobody can use.
Note the asymmetry: a validation failure blocks terraform destroy as well as apply, which is why this
module refuses only what the provider itself would refuse, plus the one display-name mistake.
id = "/subscriptions/.../resourceGroups/rg-data-platform/providers/Microsoft.Sql/managedInstances/sqlmi-platform-eus2/administrators/ActiveDirectory"
azuread_only_authentication_id = "/subscriptions/.../managedInstances/sqlmi-platform-eus2/azureADOnlyAuthentications/Default"
managed_instance_name = "sqlmi-platform-eus2"
login_username = "sql-mi-admins"
object_id = "1a2b3c4d-5e6f-7081-92a3-b4c5d6e7f809"
azuread_authentication_only = true
sql_authentication_still_permitted = false
force_new_fields = ["managed_instance_id"]
| Symptom | Cause | Fix |
|---|---|---|
managed_instance_id must be a SQL MANAGED INSTANCE Resource ID |
a logical server ID, or a child of the instance, was passed | Use the managed instance module's id. A logical server sets its administrator inline on the server resource |
login_username looks like a GUID or a Resource ID |
the object ID was pasted into the display-name field | Put the GUID in object_id; login_username is the portal label |
object_id must be a UUID / tenant_id must be a UUID |
a display name or a Resource ID was passed | Pass bare GUIDs |
login_username is empty or whitespace |
blank display name | Supply the principal's display name |
| Applications suddenly cannot connect after an apply | azuread_authentication_only defaults to true here and disables SQL logins |
Expected. Set it to false while migrating, or move the applications to Entra authentication |
| A requires-import error on first apply | an administrator already exists on the instance | Expected, and better than overwriting. terraform import the existing record |
terraform destroy left SQL authentication working again |
the provider must disable Entra-only authentication before removing the administrator | Expected. Revoke the SQL logins separately |
| A destroy timed out | the provider's own delete deadline is 180 minutes for a reason | Do not shorten timeouts.delete; raise it if needed |
| The portal shows an administrator name that no longer matches the group | login_username is a label Azure never reconciles with object_id |
Update login_username; nothing detects this for you |
| The administrator was set but nobody can sign in | the object ID is well-formed but names nothing, or the wrong tenant was supplied | Verify the object in Entra; no permission or validation check catches this |
azurerm_mssql_managed_instance_active_directory_administrator- Microsoft Entra authentication for Azure SQL
- Configure Microsoft Entra-only authentication
- Sibling modules:
terraform-azurerm-mssql-managed-instance,terraform-azurerm-mssql-managed-instance-transparent-data-encryption,terraform-azurerm-mssql-managed-instance-security-alert-policy,terraform-azurerm-mssql-server - This module's
SCOPE.md
💙 "Infrastructure as Code should be standardized, consistent, and secure."