{
  "schemaVersion": "1.0",
  "code": "530003",
  "name": null,
  "title": "AADSTS530003: Your device is required to be managed (fix)",
  "url": "https://brambrinkmeier.com/entra/aadsts/530003/",
  "alternates": {
    "markdown": "https://brambrinkmeier.com/entra/aadsts/530003.md",
    "json": "https://brambrinkmeier.com/api/aadsts/530003.json",
    "html": "https://brambrinkmeier.com/entra/aadsts/530003/"
  },
  "description": "AADSTS530003: Conditional Access requires a managed device and this one isn't. Common with Azure CLI and Terraform. Not in Microsoft's reference. KQL.",
  "summary": "AADSTS530003 means a Conditional Access policy requires a managed device and the device asking for the token isn't one, or can't prove it. Microsoft's error reference doesn't list it. Users report it from the Azure CLI, Terraform and Cloud Shell. Usually it is a tool on an unmanaged machine. From a new device or IP, check whether the account is in the right hands.",
  "inMicrosoftReference": false,
  "unlistedChecked": "2026-10-30",
  "meaningText": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS530003 (checked 30 October 2026).\n\nMicrosoft documents it on another page. The Viva Goals troubleshooting page for integration errors lists it under the heading *\"AADSTS530003: Your device is required to be managed to access this resource\"* and says:\n\n> The device on which this connection was first created might not have been in a managed state. Try reauthenticating or creating a new connection.\n\nThat page introduces its list as errors you get *\"if you encounter an AAD invalid grant error for an integration\"*, so it covers a stored connection whose token is refreshed later.\n\nUsers report the same message from command-line tools. On HashiCorp Discuss, `terraform plan` on Ubuntu with the azurerm provider failed while reading a token from the Azure CLI:\n\n> ERROR: AADSTS530003: Your device is required to be managed to access this resource.\n\nOn Microsoft Q&A, `az account get-access-token --resource https://graph.microsoft.com` in Azure Cloud Shell returned the same text. Neither thread has an answer.\n\nOur reading: a Conditional Access policy requires a managed device (compliant or hybrid joined), and the device asking for the token is not one or can't prove it. Microsoft doesn't publish a name or a precise definition for 530003. The listed codes for the same family are [53000](https://brambrinkmeier.com/entra/aadsts/53000/) (device not compliant) and [53001](https://brambrinkmeier.com/entra/aadsts/53001/) (device not domain joined).",
  "sections": [
    {
      "heading": "What does AADSTS530003 mean?",
      "text": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS530003 (checked 30 October 2026).\n\nMicrosoft documents it on another page. The Viva Goals troubleshooting page for integration errors lists it under the heading *\"AADSTS530003: Your device is required to be managed to access this resource\"* and says:\n\n> The device on which this connection was first created might not have been in a managed state. Try reauthenticating or creating a new connection.\n\nThat page introduces its list as errors you get *\"if you encounter an AAD invalid grant error for an integration\"*, so it covers a stored connection whose token is refreshed later.\n\nUsers report the same message from command-line tools. On HashiCorp Discuss, `terraform plan` on Ubuntu with the azurerm provider failed while reading a token from the Azure CLI:\n\n> ERROR: AADSTS530003: Your device is required to be managed to access this resource.\n\nOn Microsoft Q&A, `az account get-access-token --resource https://graph.microsoft.com` in Azure Cloud Shell returned the same text. Neither thread has an answer.\n\nOur reading: a Conditional Access policy requires a managed device (compliant or hybrid joined), and the device asking for the token is not one or can't prove it. Microsoft doesn't publish a name or a precise definition for 530003. The listed codes for the same family are [53000](https://brambrinkmeier.com/entra/aadsts/53000/) (device not compliant) and [53001](https://brambrinkmeier.com/entra/aadsts/53001/) (device not domain joined)."
    },
    {
      "heading": "Is it an attack?",
      "text": "Usually not. Most reports come from tools that can't present a device identity: a CLI on Linux, a container, Cloud Shell, or a stored integration.\n\n| Pattern | Likely meaning |\n|---|---|\n| 530003 from Azure CLI, Terraform or PowerShell on a build agent or Linux machine | The tool runs on a machine that isn't managed by the tenant. Expected under a managed-device policy. |\n| 530003 right after `az login --use-device-code` | Microsoft says device code flow can't carry device state (see below), so expect it under a managed-device policy. |\n| 530003 from Cloud Shell | Probably because Cloud Shell runs on a Microsoft host, not on the user's device. Unconfirmed; based on one Q&A report. |\n| 530003 for a privileged account from a new IP or country | Our reasoning: the account got far enough for a device control to apply. Check whether the sign-in was the user. |"
    },
    {
      "heading": "How do I find it in my logs (KQL)?",
      "text": "Microsoft doesn't document which sign-in table 530003 is logged in. Check where it lands first:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"530003\"\n| summarize Rows = count() by SourceTable\n```\n\nWhich users, apps and device states hit it. `DeviceDetail` is dynamic in `SigninLogs` and a string in `AADNonInteractiveUserSignInLogs`, so each leg parses it before the union. The property names come from the Graph `deviceDetail` resource:\n\n```kusto\nlet rows =\n    union\n        (SigninLogs | extend D = parse_json(tostring(DeviceDetail)) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D),\n        (AADNonInteractiveUserSignInLogs | extend D = parse_json(DeviceDetail) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D);\nrows\n| where TimeGenerated > ago(7d)\n| where ResultType == \"530003\"\n| extend DeviceId = tostring(D.deviceId),\n         TrustType = tostring(D.trustType),\n         IsManaged = tostring(D.isManaged),\n         IsCompliant = tostring(D.isCompliant),\n         OS = tostring(D.operatingSystem)\n| summarize Rows = count(), IPs = make_set(IPAddress, 10), Agents = make_set(UserAgent, 5)\n          by UserPrincipalName, AppDisplayName, ClientAppUsed, DeviceId, TrustType, IsManaged, IsCompliant, OS\n| extend Unregistered = isempty(DeviceId)\n| sort by Rows desc\n```\n\n530003 from an IP where the same user had no successful sign-in in the last 30 days. A starting point for triage, not a detection rule:\n\n```kusto\nlet lookback = 30d;\nlet knownUserIPs =\n    union\n        (SigninLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType),\n        (AADNonInteractiveUserSignInLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType)\n    | where TimeGenerated > ago(lookback)\n    | where ResultType == \"0\"\n    | distinct UserPrincipalName, IPAddress;\nunion\n    (SigninLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent),\n    (AADNonInteractiveUserSignInLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent)\n| where TimeGenerated > ago(1d)\n| where ResultType == \"530003\"\n| join kind=leftanti knownUserIPs on UserPrincipalName, IPAddress\n| sort by TimeGenerated desc\n```"
    },
    {
      "heading": "How does Conditional Access affect it?",
      "text": "- 530003 most likely comes from a device grant control: **Require device to be marked as compliant**, **Require Microsoft Entra hybrid joined device**, or both. The Conditional Access tab on the failed sign-in shows which policy applied.\n- Microsoft says device code flow can't satisfy these controls: *\"When you use the device-code OAuth flow, the required grant control for the managed device or a device state condition isn't supported. This is because the device that is performing authentication can't provide its device state to the device that is providing a code.\"* Microsoft's advice there: use **Require multifactor authentication** instead.\n- Microsoft also says Edge in InPrivate mode doesn't count as compliant or hybrid joined.\n- By default, a policy with several controls requires all of them. With \"require one of the selected controls\", MFA can stand in for the device."
    },
    {
      "heading": "How do I fix AADSTS530003?",
      "text": "**For the user:** run the tool on your managed work device, or sign in with a browser on that device. If you need the tool somewhere else, such as a build server, ask IT how they want that done.\n\n**For developers:** Microsoft's MSAL.NET page on device authentication says the device must prove its identity, and *\"to satisfy this requirement you will have to leverage WAM on Windows or the system browser. On mobile platforms, you'll need to enable the brokers (Microsoft Authenticator and Company portal).\"* An embedded web view or a plain HTTP client can't do that. For automation, don't use a user account at all. Use a service principal or managed identity. Our reading is that user device-state controls don't apply to those, but check your own policies.\n\n**For admins:**\n1. Open the failed sign-in in **Entra ID** > **Monitoring & health** > **Sign-in logs** and select the **Conditional Access** tab. It names the policy. Microsoft adds that the **Troubleshooting and support** tab may show the reason directly.\n2. Check the device in the second query. An empty `DeviceId` means the device isn't registered, so it can't be managed.\n3. Decide whether the policy should cover that app. If admins need Azure CLI from unmanaged machines, scope an exception for those users or allow MFA as an alternative. Use the What If tool before you change a production policy.\n4. If the device should be managed, enroll or join it and sign in again. The Viva Goals page gives the same advice for stored connections: reauthenticate or create a new connection."
    }
  ],
  "microsoftWording": {
    "text": "The device on which this connection was first created might not have been in a managed state. Try reauthenticating or creating a new connection.",
    "source": "Quoted on the page from the lab or from user reports; the code is not in Microsoft's error code reference",
    "sourceUrl": null
  },
  "securityReading": {
    "category": {
      "id": "device",
      "label": "Devices"
    },
    "kind": {
      "id": "failure",
      "label": "Failure"
    },
    "attackSignal": {
      "level": "medium",
      "label": "Medium: worth a look in context"
    },
    "mitre": [],
    "tables": [],
    "isItAnAttack": "Usually not. Most reports come from tools that can't present a device identity: a CLI on Linux, a container, Cloud Shell, or a stored integration.\n\n| Pattern | Likely meaning |\n|---|---|\n| 530003 from Azure CLI, Terraform or PowerShell on a build agent or Linux machine | The tool runs on a machine that isn't managed by the tenant. Expected under a managed-device policy. |\n| 530003 right after `az login --use-device-code` | Microsoft says device code flow can't carry device state (see below), so expect it under a managed-device policy. |\n| 530003 from Cloud Shell | Probably because Cloud Shell runs on a Microsoft host, not on the user's device. Unconfirmed; based on one Q&A report. |\n| 530003 for a privileged account from a new IP or country | Our reasoning: the account got far enough for a device control to apply. Check whether the sign-in was the user. |",
    "conditionalAccess": {
      "heading": "How does Conditional Access affect it?",
      "text": "- 530003 most likely comes from a device grant control: **Require device to be marked as compliant**, **Require Microsoft Entra hybrid joined device**, or both. The Conditional Access tab on the failed sign-in shows which policy applied.\n- Microsoft says device code flow can't satisfy these controls: *\"When you use the device-code OAuth flow, the required grant control for the managed device or a device state condition isn't supported. This is because the device that is performing authentication can't provide its device state to the device that is providing a code.\"* Microsoft's advice there: use **Require multifactor authentication** instead.\n- Microsoft also says Edge in InPrivate mode doesn't count as compliant or hybrid joined.\n- By default, a policy with several controls requires all of them. With \"require one of the selected controls\", MFA can stand in for the device."
    }
  },
  "kql": [
    {
      "title": null,
      "leadIn": "Microsoft doesn't document which sign-in table 530003 is logged in. Check where it lands first:",
      "query": "union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"530003\"\n| summarize Rows = count() by SourceTable"
    },
    {
      "title": null,
      "leadIn": "Which users, apps and device states hit it. `DeviceDetail` is dynamic in `SigninLogs` and a string in `AADNonInteractiveUserSignInLogs`, so each leg parses it before the union. The property names come from the Graph `deviceDetail` resource:",
      "query": "let rows =\n    union\n        (SigninLogs | extend D = parse_json(tostring(DeviceDetail)) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D),\n        (AADNonInteractiveUserSignInLogs | extend D = parse_json(DeviceDetail) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D);\nrows\n| where TimeGenerated > ago(7d)\n| where ResultType == \"530003\"\n| extend DeviceId = tostring(D.deviceId),\n         TrustType = tostring(D.trustType),\n         IsManaged = tostring(D.isManaged),\n         IsCompliant = tostring(D.isCompliant),\n         OS = tostring(D.operatingSystem)\n| summarize Rows = count(), IPs = make_set(IPAddress, 10), Agents = make_set(UserAgent, 5)\n          by UserPrincipalName, AppDisplayName, ClientAppUsed, DeviceId, TrustType, IsManaged, IsCompliant, OS\n| extend Unregistered = isempty(DeviceId)\n| sort by Rows desc"
    },
    {
      "title": null,
      "leadIn": "530003 from an IP where the same user had no successful sign-in in the last 30 days. A starting point for triage, not a detection rule:",
      "query": "let lookback = 30d;\nlet knownUserIPs =\n    union\n        (SigninLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType),\n        (AADNonInteractiveUserSignInLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType)\n    | where TimeGenerated > ago(lookback)\n    | where ResultType == \"0\"\n    | distinct UserPrincipalName, IPAddress;\nunion\n    (SigninLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent),\n    (AADNonInteractiveUserSignInLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent)\n| where TimeGenerated > ago(1d)\n| where ResultType == \"530003\"\n| join kind=leftanti knownUserIPs on UserPrincipalName, IPAddress\n| sort by TimeGenerated desc"
    }
  ],
  "kqlSectionText": "Microsoft doesn't document which sign-in table 530003 is logged in. Check where it lands first:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"530003\"\n| summarize Rows = count() by SourceTable\n```\n\nWhich users, apps and device states hit it. `DeviceDetail` is dynamic in `SigninLogs` and a string in `AADNonInteractiveUserSignInLogs`, so each leg parses it before the union. The property names come from the Graph `deviceDetail` resource:\n\n```kusto\nlet rows =\n    union\n        (SigninLogs | extend D = parse_json(tostring(DeviceDetail)) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D),\n        (AADNonInteractiveUserSignInLogs | extend D = parse_json(DeviceDetail) | project TimeGenerated, ResultType, UserPrincipalName, AppDisplayName, ClientAppUsed, UserAgent, IPAddress, D);\nrows\n| where TimeGenerated > ago(7d)\n| where ResultType == \"530003\"\n| extend DeviceId = tostring(D.deviceId),\n         TrustType = tostring(D.trustType),\n         IsManaged = tostring(D.isManaged),\n         IsCompliant = tostring(D.isCompliant),\n         OS = tostring(D.operatingSystem)\n| summarize Rows = count(), IPs = make_set(IPAddress, 10), Agents = make_set(UserAgent, 5)\n          by UserPrincipalName, AppDisplayName, ClientAppUsed, DeviceId, TrustType, IsManaged, IsCompliant, OS\n| extend Unregistered = isempty(DeviceId)\n| sort by Rows desc\n```\n\n530003 from an IP where the same user had no successful sign-in in the last 30 days. A starting point for triage, not a detection rule:\n\n```kusto\nlet lookback = 30d;\nlet knownUserIPs =\n    union\n        (SigninLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType),\n        (AADNonInteractiveUserSignInLogs | project TimeGenerated, UserPrincipalName, IPAddress, ResultType)\n    | where TimeGenerated > ago(lookback)\n    | where ResultType == \"0\"\n    | distinct UserPrincipalName, IPAddress;\nunion\n    (SigninLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent),\n    (AADNonInteractiveUserSignInLogs | project TimeGenerated, ResultType, UserPrincipalName, IPAddress, AppDisplayName, UserAgent)\n| where TimeGenerated > ago(1d)\n| where ResultType == \"530003\"\n| join kind=leftanti knownUserIPs on UserPrincipalName, IPAddress\n| sort by TimeGenerated desc\n```",
  "fix": {
    "short": "User: run the tool on your managed work device, or sign in with a browser there. For automation, use a service principal or managed identity, not a user account. Admin: find the policy on the failed sign-in's Conditional Access tab and decide whether it should cover that app.",
    "steps": "**For the user:** run the tool on your managed work device, or sign in with a browser on that device. If you need the tool somewhere else, such as a build server, ask IT how they want that done.\n\n**For developers:** Microsoft's MSAL.NET page on device authentication says the device must prove its identity, and *\"to satisfy this requirement you will have to leverage WAM on Windows or the system browser. On mobile platforms, you'll need to enable the brokers (Microsoft Authenticator and Company portal).\"* An embedded web view or a plain HTTP client can't do that. For automation, don't use a user account at all. Use a service principal or managed identity. Our reading is that user device-state controls don't apply to those, but check your own policies.\n\n**For admins:**\n1. Open the failed sign-in in **Entra ID** > **Monitoring & health** > **Sign-in logs** and select the **Conditional Access** tab. It names the policy. Microsoft adds that the **Troubleshooting and support** tab may show the reason directly.\n2. Check the device in the second query. An empty `DeviceId` means the device isn't registered, so it can't be managed.\n3. Decide whether the policy should cover that app. If admins need Azure CLI from unmanaged machines, scope an exception for those users or allow MFA as an alternative. Use the What If tool before you change a production policy.\n4. If the device should be managed, enroll or join it and sign in again. The Viva Goals page gives the same advice for stored connections: reauthenticate or create a new connection."
  },
  "labReproduced": null,
  "labSectionText": null,
  "labRuns": [],
  "verification": "documentation",
  "related": [
    "53000",
    "53001",
    "50097",
    "53003",
    "530002",
    "530004"
  ],
  "coveredBy": [],
  "sources": [
    {
      "title": "Microsoft Entra authentication and authorization error codes",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes"
    },
    {
      "title": "Troubleshoot AAD errors for integrations (Viva Goals)",
      "url": "https://learn.microsoft.com/en-gb/viva/goals/integration-aad-errors"
    },
    {
      "title": "Error: AADSTS530003 (HashiCorp Discuss)",
      "url": "https://discuss.hashicorp.com/t/error-aadsts530003-your-device-is-required-to-be-managed-to-access-this-resource/58943"
    },
    {
      "title": "Get access token against microsoft graph failing (Microsoft Q&A 1441736)",
      "url": "https://learn.microsoft.com/en-us/answers/questions/1441736/get-access-token-against-microsoft-graph-failing"
    },
    {
      "title": "Conditional Access: Grant",
      "url": "https://learn.microsoft.com/en-gb/entra/identity/conditional-access/concept-conditional-access-grant"
    },
    {
      "title": "Device authentication errors (MSAL.NET)",
      "url": "https://learn.microsoft.com/en-us/entra/msal/dotnet/advanced/exceptions/device-authentication-errors"
    },
    {
      "title": "Troubleshoot sign-in problems with Conditional Access",
      "url": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/troubleshoot-conditional-access"
    },
    {
      "title": "deviceDetail resource type (Microsoft Graph)",
      "url": "https://learn.microsoft.com/en-us/graph/api/resources/devicedetail"
    },
    {
      "title": "SigninLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/signinlogs"
    },
    {
      "title": "AADNonInteractiveUserSignInLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/AADNonInteractiveUserSignInLogs"
    }
  ],
  "published": "2026-10-30",
  "lastUpdated": "2026-10-30",
  "lastVerified": null,
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "licenseNote": "CC BY 4.0 covers the author's own text and classification fields. Quoted Microsoft error wording belongs to Microsoft, is quoted with its source and can change.",
  "attribution": "Bram Brinkmeier, \"AADSTS530003: Your device is required to be managed (fix)\", https://brambrinkmeier.com/entra/aadsts/530003/ (CC BY 4.0)",
  "microsoftErrorLookup": "https://login.microsoftonline.com/error?code=530003"
}
