Onboards one virtual machine to an Automanage configuration profile β and carries the fact that the provider does not: the service retires on 30 September 2027. Targets
hashicorp/azurerm ~> 4.0.
- ποΈ Enrolls a virtual machine in an Automanage configuration profile.
- 1οΈβ£ Exactly one assignment per machine β the provider hard-codes the name, so there is no
nameargument. - β³ Emits the retirement date and the migration target as values, not as prose.
- π Reports where the profile lives, so the read permission can be granted in the right place.
β οΈ Names what the assignment causes: Azure onboards the machine to the services the profile lists, unobserved by Terraform, and removing the assignment undoes none of it.
π‘ Why it matters: Microsoft has announced that the Azure Automanage Best Practices service retires on 30 September 2027, with Azure Policy as the documented migration target β and nothing in Terraform says so. The resource is not flagged deprecated in the provider schema and the provider's documentation carries no notice, so a caller reading only Terraform has no way to find out. This module makes that fact part of the plan.
If this module saved you time:
- β Star the repository β it genuinely helps other people find it.
- πΌ Connect on LinkedIn β linkedin.com/in/microsoftexpert
- β Buy me a coffee β buymeacoffee.com/microsoftexpert
flowchart TB
CFG["terraform-azurerm-automanage-configuration -- the profile"]
VM["terraform-azurerm-linux-virtual-machine or -windows-virtual-machine"]
ASSIGN["terraform-azurerm-virtual-machine-automanage-configuration-assignment"]
SVC["services the PROFILE enables -- backup, update management, monitoring"]
POLICY["Azure Policy -- the documented migration target after 2027-09-30"]
CFG -->|"id to configuration_id"| ASSIGN
VM -->|"id to virtual_machine_id -- ONE assignment per machine, name fixed to default"| ASSIGN
ASSIGN -->|"Automanage onboards the machine, unobserved by Terraform"| SVC
ASSIGN -.->|"THE SERVICE RETIRES 2027-09-30 -- migrate to this"| POLICY
classDef self fill:#0078D4,stroke:#004578,color:#ffffff
classDef keystone fill:#004578,stroke:#002B4A,color:#ffffff
classDef ext fill:#F3F2F1,stroke:#8A8886,color:#201F1E
class ASSIGN self
class VM keystone
class CFG,SVC,POLICY ext
The dotted edge is the one to read. It does not represent anything this module builds β it records where Microsoft says configurations should end up after the retirement date, and it is drawn because a family diagram that omitted it would be quietly out of date.
flowchart TB
IN1["virtual_machine_id -- force-new"]
IN2["configuration_id -- force-new, either namespace casing accepted"]
IN3["timeouts -- THREE keys, there is no update function"]
RES["azurerm_virtual_machine_automanage_configuration_assignment.this -- name fixed to default"]
OUT1["id ending in the literal segment default"]
OUT2["the retirement date and the migration target"]
OUT3["spans_subscriptions and the parsed names"]
OUT4["posture -- onboarding is unobserved, removal undoes nothing"]
IN1 --> RES
IN2 --> RES
IN3 --> RES
RES --> OUT1
RES --> OUT2
RES --> OUT3
RES --> OUT4
classDef self fill:#0078D4,stroke:#004578,color:#ffffff
classDef ext fill:#F3F2F1,stroke:#8A8886,color:#201F1E
class RES self
class IN1,IN2,IN3,OUT1,OUT2,OUT3,OUT4 ext
| Resource | Name | Cardinality |
|---|---|---|
azurerm_virtual_machine_automanage_configuration_assignment |
this |
single β and one per machine, because the name is fixed to default |
Both arguments are force-new and there is no update function, so the timeouts type carries three keys.
| Requirement | Value |
|---|---|
| Terraform | >= 1.12.0 |
hashicorp/azurerm |
~> 4.0 |
| Provider block | None in this module β the caller configures provider "azurerm" { features {} }, including authentication |
- π΄ The service is retiring on 2027-09-30 and the schema says nothing.
deprecatedisfalseon this resource, and the provider documentation carries no notice. Microsoft states that as a result of the retirement, creating a new configuration profile or onboarding a new subscription will return an error. - π΄ The assignment name is HARD-CODED to
default. The provider's own comment says the API requires it. There is nonameargument, and a machine has one Automanage assignment or none. - π΄ Changing the profile leaves the machine briefly onboarded to nothing. Both arguments are force-new and the replacement reuses the identical Resource ID, so create-before-destroy is impossible.
- π The real namespace casing is
Microsoft.AutoManageβ capital M β in both the provider's import example and the sibling configuration resource. A case-sensitive check on the other spelling would reject the sibling module's ownidoutput, so this module accepts either. - π This IS a real ARM record, unlike the gallery application assignment in the same family. Its ID is returned by Azure rather than composed, and its delete is a real delete.
- π A successful create means the assignment exists, not that the machine is configured. Automanage onboards the machine to the profile's services on its own schedule, and no timeout here bounds that.
- π‘ The ID uses the CALLER'S provider subscription, with the machine's resource group and name.
- π‘ No lock is taken on the machine β unlike both of its neighbours in this family, which are read-modify-write operations and do lock it.
- β An import guard exists, and since the name is fixed it is effectively a test of whether the machine is onboarded at all.
- β
No
tags, no sensitive field, no credential β the resource names two Resource IDs and nothing else.
| Permission | Scope | Why |
|---|---|---|
Microsoft.AutoManage/configurationProfileAssignments/read |
the target machine | Refresh, and the create's import check. |
Microsoft.AutoManage/configurationProfileAssignments/write |
the target machine | Create. |
Microsoft.AutoManage/configurationProfileAssignments/delete |
the target machine | Destroy β and, since there is no update, every change. |
Microsoft.Compute/virtualMachines/read |
the target machine | The provider resolves the machine. |
Microsoft.AutoManage/configurationProfiles/read |
the configuration profile | Resolving the profile β which may be in another subscription. |
π The permission understates what applying this does. Onboarding a machine causes Azure to create and configure resources this module never mentions β a Recovery Services vault, an Automation account, a Log Analytics workspace, agents on the machine. None is a Terraform object, none appears in any plan, and
terraform destroyremoves none of them. Read the profile before assigning it.
β οΈ Microsoft documents wider permissions for first-time enablement: Owner, or Contributor together with User Access Administrator, at the subscription, because the service creates role assignments of its own. The table above covers an assignment on a subscription already onboarded.
βΉοΈ Automanage acts continuously with its own identity. It monitors for drift and remediates it, so a setting changed by hand on the machine may be changed back with no Terraform run involved.
- The
Microsoft.Automanageresource provider registered in the target subscription. - The subscription already onboarded to Automanage, or the wider first-time permissions above.
- The configuration profile already created β the sibling module produces one, and Microsoft publishes built-in production and dev/test profiles.
- The machine running, with a healthy Azure VM agent, on a supported operating system.
- π΄ A plan for 30 September 2027. After that date the service is retired and Microsoft's migration target is Azure Policy.
terraform-azurerm-virtual-machine-automanage-configuration-assignment/
βββ providers.tf # required_version >= 1.12.0, azurerm ~> 4.0, no provider block
βββ variables.tf # 3 inputs, 2 validations -- the machine, the profile, the deadlines
βββ main.tf # one keystone `this`; ID parsing read from the END; a THREE-key timeouts block
βββ outputs.tf # 30 outputs -- id first, then the retirement facts, then posture
βββ README.md # this file
βββ SCOPE.md # the cross-module contract
βββ LICENSE # MIT
βββ .gitignore
provider "azurerm" {
features {}
}
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}
β οΈ Readthe_automanage_service_is_announced_for_retirementbefore adopting this. See example 1.
| Input | Type | Source |
|---|---|---|
virtual_machine_id |
string |
terraform-azurerm-linux-virtual-machine β id |
configuration_id |
string |
terraform-azurerm-automanage-configuration β id |
timeouts |
object(...) |
the caller β three keys |
| Output | Description | Consumed by |
|---|---|---|
id |
.../virtualMachines/<vm>/providers/Microsoft.AutoManage/configurationProfileAssignments/default |
reporting |
automanage_retirement_date |
2027-09-30, as a comparable value |
planning |
automanage_migration_target |
Azure Policy |
planning |
spans_subscriptions |
Where the read permission on the profile is needed | pre-apply review |
1 Β· The retirement, first
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}
output "automanage_end_of_life" {
value = {
retiring = module.automanage_assignment.the_automanage_service_is_announced_for_retirement
on = module.automanage_assignment.automanage_retirement_date
migrate_to = module.automanage_assignment.automanage_migration_target
}
}π΄ Microsoft has announced that the Azure Automanage Best Practices service retires on 30 September 2027, and states that as a result, creating a new configuration profile or onboarding a new subscription to the service will return an error. The documented migration target is Azure Policy.
β οΈ Nothing in Terraform tells you this. The resource is not flagged deprecated in the provider schema and the provider's documentation carries no notice β which is exactly why this module emits it. A schema that saysdeprecated = falseis not evidence that a service has a future.
π‘ The date is emitted as a value, not only as a boolean, so a policy check can compare it against a planning horizon instead of hard-coding it in every consuming configuration.
2 Β· One assignment per machine, and why there is no name
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}
# A SECOND assignment on the same machine is not a second assignment.
module "automanage_assignment_devtest" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id # <-- the same machine
configuration_id = var.devtest_profile_id
}π΄ The provider hard-codes the assignment name to the literal string
default, with a comment stating the API requires it. Both module instances above therefore compose the same Resource ID, and the second fails its create with a requires-import error.
π‘ That is the good outcome β a loud failure rather than one profile silently replacing the other. But it is worth knowing before you write the second block, not after.
βΉοΈ This is also why the module has no
namevariable. Its absence is a design fact, reported throughone_assignment_per_machine_the_name_is_hard_coded.
3 Β· Changing the profile leaves a gap
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
# Changing this DELETES the assignment and creates a new one.
configuration_id = var.hardened_profile_id
}
β οΈ Both arguments are force-new, and the replacement reuses the identical Resource ID β because the name is fixed. Terraform therefore cannot create the new assignment before destroying the old one, and there is a window during which the machine is onboarded to nothing.
βΉοΈ Usually harmless; occasionally not, if the profile is what enables backup on a machine that matters.
changing_the_profile_leaves_the_machine_unonboarded_brieflyis a constanttrueso the fact is visible rather than discovered.
π‘
fields_that_can_change_after_creationreturns an empty list, deliberately. There is no update operation on this resource at all.
4 Β· Either namespace casing is accepted
# Both of these are accepted. Azure and the provider emit the FIRST spelling.
configuration_id = "/subscriptions/.../providers/Microsoft.AutoManage/configurationProfiles/prod"
configuration_id = "/subscriptions/.../providers/Microsoft.Automanage/configurationProfiles/prod"βΉοΈ The real casing has a capital M:
Microsoft.AutoManage. It is what the provider's own import example uses and what the sibling configuration resource emits.
π A case-sensitive check on the other spelling would have rejected the sibling module's own
idoutput β a validation that refuses legal input, which is the worst kind, because avalidation {}failure blocksterraform destroyas well as apply. This module accepts either casing for exactly that reason.
5 Β· A profile in another subscription
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = var.platform_production_profile_id # a central profile
}
check "cross_subscription_read_was_granted" {
assert {
condition = !module.automanage_assignment.spans_subscriptions || var.platform_profile_reader_granted
error_message = "The profile is in another subscription and read access must be granted there."
}
}
β οΈ Legal, common, and a likely cause of a confusing failure. A central platform profile in its own subscription is good practice, but a role assignment scoped to the machine's own subscription does not reach it.
π‘
spans_subscriptionsandspans_resource_groupsexist so a review can see where the read permission has to be granted, before the apply rather than after.
6 Β· What NOT to do
# WRONG -- an Arc-enabled server where a virtual machine belongs.
virtual_machine_id = var.arc_machine_idβΉοΈ Refused at
terraform plan, offline and without credentials. Arc machines have their own Automanage assignment resource. The two are easy to confuse because Microsoft's Automanage documentation covers both in one place.
# WRONG -- a scale set.
virtual_machine_id = var.app_scale_set_idβΉοΈ Also refused. This resource targets a single machine.
# WRONG -- a child of the profile.
configuration_id = var.profile_version_idβΉοΈ Refused. The pattern is anchored at both ends, so anything below the profile cannot pass.
# WRONG -- a virtual machine ID where the profile belongs.
configuration_id = var.app_vm_idβΉοΈ Refused, with a message naming the shape that is expected.
7 Β· A green apply is not a configured machine
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
timeouts = {
create = "45m"
}
}
β οΈ The timeout bounds recording the assignment, not the onboarding. Automanage then enrolls the machine in each service the profile names β backup, update management, monitoring β on its own schedule, remediating drift continuously thereafter. None of that is observed by this resource or bounded by any deadline on it.
βΉοΈ
a_successful_create_does_not_mean_the_machine_is_configuredis a constanttruefor this reason. The machine's actual state is visible in the Automanage blade in the portal, not in a Terraform plan.
8 Β· The profile decides the blast radius
module "production_profile" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-automanage-configuration.git?ref=v1.0.0"
name = "conf-production"
resource_group_name = module.rg.name
location = module.rg.location
}
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}π Applying the assignment can create resources this module never mentions β a Recovery Services vault, an Automation account, a Log Analytics workspace, agents on the machine, and their contents. They are not Terraform objects, they appear in no plan, and
terraform destroyremoves none of them.
β οΈ The profile decides all of that; the assignment is only the switch. Read the profile before assigning it, and read it again when someone edits it, because the edit changes what every assigned machine gets without any of the assignments changing.
βΉοΈ
this_resource_configures_services_it_does_not_nameandremoving_the_assignment_does_not_undo_what_it_configuredare both constanttrue.
9 Β· The agent change that already happened
output "automanage_agent_caveat" {
value = module.automanage_assignment.monitoring_agent_dependency_was_already_cut
}
β οΈ From 1 February 2025 Automanage began halting support and enforcement for services that depended on the retired Microsoft Monitoring Agent β change tracking, VM insights, update management and Azure Automation among them. The replacement is the Azure Monitor Agent.
βΉοΈ A profile written before that change may therefore name services Automanage no longer enforces, and nothing in this resource, in the plan, or in the assignment's state reveals which. This is emitted separately from the retirement because it is not a future date β it has already taken effect.
10 Β· Removing the assignment
# Removing this block stops Automanage MANAGING the machine.
# It does not undo what Automanage configured.π΄ Destroy stops the management; it does not reverse the onboarding. The backup vault, the automation account, the workspace and the agents all remain, and any setting the profile changed on the machine stays changed. Nothing in Terraform tracks those effects.
β οΈ If the assignment is removed outside Terraform β through the portal's disable flow, for instance β the next refresh marks it gone and the following plan proposes to create it again. Re-enrolling is not a quiet no-op: Automanage onboards the machine to the profile's services once more.
11 Β· No lock, and what that means
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}
module "restore_disk" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-implicit-data-disk-from-source.git?ref=v1.0.0"
name = "disk-restored-01"
virtual_machine_id = module.app_vm.id # the SAME machine
source_resource_id = var.nightly_snapshot_id
create_option = "Copy"
disk_size_gb = 256
lun = 1
}βΉοΈ This resource takes no lock on the machine, because it is a genuine ARM child record rather than a read-modify-write of the machine. Its two neighbours in this family β the implicit data disk above, and the gallery application assignment β both rewrite the machine and both lock it.
β οΈ The corollary is that this resource does not serialise against them either. An Automanage assignment and a disk attachment can reach one machine concurrently. That is usually fine, because they touch different things, and it is worth knowing when it is not.
12 Β· ποΈ End-to-end composition
provider "azurerm" {
features {}
}
module "rg" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
name = "rg-app-eastus"
location = "eastus"
tags = {
environment = "production"
workload = "line-of-business-app"
}
}
module "app_vnet" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-network.git?ref=v1.0.0"
name = "vnet-app-eastus"
resource_group_name = module.rg.name
location = module.rg.location
address_space = ["10.90.0.0/16"]
subnets = {
snet-app = {
address_prefixes = ["10.90.1.0/24"]
}
}
tags = module.rg.tags
}
module "app_nic" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-network-interface.git?ref=v1.0.0"
name = "nic-app01"
resource_group_name = module.rg.name
location = module.rg.location
ip_configurations = {
internal = {
subnet_id = module.app_vnet.subnet_ids["snet-app"]
private_ip_address_allocation = "Dynamic"
}
}
tags = module.rg.tags
}
module "app_vm" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-linux-virtual-machine.git?ref=v1.0.0"
name = "vm-app01"
resource_group_name = module.rg.name
location = module.rg.location
size = "Standard_D2s_v5"
admin_username = "azureadmin"
network_interface_ids = [module.app_nic.id]
tags = module.rg.tags
}
# The profile decides which services the machine is enrolled in.
module "production_profile" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-automanage-configuration.git?ref=v1.0.0"
name = "conf-production"
resource_group_name = module.rg.name
location = module.rg.location
}
module "automanage_assignment" {
source = "git::https://github.com/microsoftexpert/terraform-azurerm-virtual-machine-automanage-configuration-assignment.git?ref=v1.0.0"
virtual_machine_id = module.app_vm.id
configuration_id = module.production_profile.id
}
output "automanage" {
value = {
id = module.automanage_assignment.id
profile = module.automanage_assignment.configuration_name
machine = module.automanage_assignment.virtual_machine_name
cross_sub = module.automanage_assignment.spans_subscriptions
retires_on = module.automanage_assignment.automanage_retirement_date
migrate_to = module.automanage_assignment.automanage_migration_target
agent_caveat = module.automanage_assignment.monitoring_agent_dependency_was_already_cut
}
}π΄ The retirement date is in the composition's own output, deliberately. A configuration adopting Automanage today should record, in its plan, when it is expected to stop working and where Microsoft says it should go instead.
β οΈ This is also the shape most affected by the retirement notice: it creates the profile as well as the assignment, and Microsoft's statement says profile creation is what errors first.
π‘
spans_subscriptionsisfalsehere because the profile is in the same subscription. A platform topology would put it elsewhere β see example 5.
Required: virtual_machine_id, configuration_id
Optional: timeouts
There is no name variable β the provider hard-codes the assignment name β and no tags variable,
because the resource exposes none.
Full input schemas
variable "virtual_machine_id" {
type = string
# REQUIRED, FORCE-NEW. Anchored to .../virtualMachines/<vm> at both ends.
# Arc-enabled servers use a DIFFERENT resource and are refused here.
}
variable "configuration_id" {
type = string
# REQUIRED, FORCE-NEW. Anchored to .../configurationProfiles/<name>.
# EITHER namespace casing is accepted -- Microsoft.AutoManage is what Azure
# and the sibling configuration module actually emit.
}
variable "timeouts" {
type = object({
create = optional(string)
read = optional(string)
delete = optional(string)
})
default = null
# THREE KEYS. Both arguments are force-new, so there is no update operation
# and no update timeout; Terraform would discard an `update` key silently.
}| Output | Type | Notes |
|---|---|---|
id |
string |
Ends in the literal segment default |
virtual_machine_id / virtual_machine_name |
string |
The onboarded machine |
resource_group_name / subscription_id |
string |
Parsed from the machine's ID |
configuration_id / configuration_name |
string |
The profile |
configuration_resource_group_name / configuration_subscription_id |
string |
Where the profile lives |
spans_subscriptions / spans_resource_groups |
bool |
Where read access on the profile is needed |
the_automanage_service_is_announced_for_retirement |
bool |
Constant true |
automanage_retirement_date |
string |
2027-09-30 |
automanage_migration_target |
string |
Azure Policy |
monitoring_agent_dependency_was_already_cut |
bool |
Constant true β already in force |
one_assignment_per_machine_the_name_is_hard_coded |
bool |
Constant true |
changing_the_profile_leaves_the_machine_unonboarded_briefly |
bool |
Constant true |
a_successful_create_does_not_mean_the_machine_is_configured |
bool |
Constant true |
this_resource_configures_services_it_does_not_name |
bool |
Constant true |
removing_the_assignment_does_not_undo_what_it_configured |
bool |
Constant true |
the_provider_takes_no_lock_on_the_machine |
bool |
Constant true |
this_is_a_real_azure_resource_not_a_patch_of_the_machine |
bool |
Constant true |
an_import_guard_exists_on_this_resource |
bool |
Constant true |
a_removed_assignment_disappears_from_state_without_an_error |
bool |
Constant true |
arc_enabled_servers_use_a_different_resource |
bool |
Constant true |
no_secret_is_accepted_or_emitted_by_this_module |
bool |
Constant true |
this_resource_supports_no_azure_resource_tags |
bool |
Constant true |
force_new_fields |
list(string) |
Both arguments |
fields_that_can_change_after_creation |
list(string) |
Empty, deliberately |
fields_azure_returns_on_read |
list(string) |
Both, re-parsed and normalised |
The retirement is the first architectural fact. Microsoft has announced that the Azure Automanage Best
Practices service retires on 30 September 2027, and states that as a result, creating a new configuration
profile or onboarding a new subscription will return an error; the migration target is Azure Policy. Nothing
in Terraform says so β the schema's deprecated flag is false and the provider documentation carries no
notice. That combination has now been seen three times in this library, and it is why a schema flag is treated
as necessary but never sufficient evidence that a resource has a future. The module emits the date as a value
so a policy check can act on it.
The module is authored rather than withheld, which is a deliberate departure from how Azure Blueprints and the Key Vault managed storage account were handled. Those were services already gone or already refusing new work. This one has a future end date, a live resource in the pinned provider, and an already-authored sibling that creates the profiles β so withholding it would leave the library able to create a profile it cannot assign. Honest documentation beats an absent module here.
There is no name argument, and that shapes everything. The provider hard-codes the assignment name to
default because the API requires it, so a machine has one Automanage assignment or none. Two module
instances on one machine compose the same Resource ID and the second fails on the import guard; changing the
profile is a delete-then-create on that same ID, which makes create-before-destroy impossible and leaves the
machine briefly onboarded to nothing.
This is a real ARM record, unlike its neighbours. The gallery application assignment and the implicit data
disk are both read-modify-write operations on the machine with synthetic IDs; this one creates a genuine
configurationProfileAssignments object whose ID Azure returns, whose delete is a real delete, and which can
be used where an ARM Resource ID is expected. It also takes no lock β and therefore does not serialise against
the two that do.
The permission table understates the effect. The profile, not the assignment, decides which Azure services
are enabled and how; applying an assignment can create a Recovery Services vault, an Automation account, a Log
Analytics workspace and their contents. None is a Terraform object and terraform destroy removes none of
them. Read the profile before assigning it β and note that editing the profile changes what every assigned
machine gets, without any assignment changing.
The namespace casing nearly became a defect. Azure and the provider emit Microsoft.AutoManage with a
capital M; a case-sensitive check on the other spelling would have rejected the sibling configuration
module's own id output. Because a validation {} failure blocks terraform destroy as well as apply, that
would have been the worst class of defect β a check that refuses legal input. This module accepts either
casing, as the sibling snapshot module in this library does for its own ID check.
lifecycle is not valid inside a module block, so a caller cannot add prevent_destroy here.
| Concern | This module's position | What the caller must type to change it |
|---|---|---|
| The retirement | Emitted as a boolean, a date and a migration target β comparable, not just readable | β |
| Authoring at all | Authored with the end date attached, because the sibling profile module already exists | β |
| Namespace casing | Either casing accepted β a strict check would reject the sibling's own output | β |
The absent name |
Documented as a design fact, with its three consequences emitted separately | β |
| Resource-ID inputs | Anchored at both ends β Arc machines, scale sets and children refused | β |
| What the profile causes | Named in the permissions table, not left to the profile's own documentation | β |
tags |
Absent by design β the resource exposes none | tag the machine or the profile |
π The security question here is not a setting on this resource. It is what the profile enables, and who can edit the profile.
terraform init -backend=false
terraform validate
terraform fmt -checkPin the module by tag β ?ref=v1.0.0 β never a branch.
Plan-only: this repository performs no cloud apply. A human applies from CI β and for this module, after someone has read both the profile and the retirement date.
What terraform validate covers, offline and without credentials:
- The anchored virtual machine ID check, including refusing Arc machines and scale sets.
- The anchored configuration profile ID check, in both namespace casings.
What only terraform plan exercises (credentials required):
- That the machine and the profile exist.
- The import guard, if the machine is already onboarded by any means.
What only terraform apply reveals:
- Whether the subscription is onboarded to Automanage.
- Whether the machine's operating system is supported.
- Whether the identity can read a profile in another subscription.
What nothing reveals at all:
- Whether Automanage has finished onboarding the machine.
- Which of the profile's services are still enforced after the 2025 monitoring-agent change.
- What the profile will create in your subscription.
βΉοΈ
terraform consolefires root-module variable validations, unliketerraform validateon a calling configuration. It is the honest offline harness for the first list and for every derived flag β including both branches of each span flag and both namespace casings.
Outputs:
id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-app-eastus/providers/Microsoft.Compute/virtualMachines/vm-app01/providers/Microsoft.AutoManage/configurationProfileAssignments/default"
virtual_machine_name = "vm-app01"
configuration_name = "conf-production"
spans_subscriptions = false
spans_resource_groups = false
the_automanage_service_is_announced_for_retirement = true
automanage_retirement_date = "2027-09-30"
automanage_migration_target = "Azure Policy"
monitoring_agent_dependency_was_already_cut = true
fields_that_can_change_after_creation = []
βΉοΈ Look at the last segment of the
id. It is the literal stringdefault, not a name you chose β and the twoproviderssegments mark this as an extension resource living under the machine.
| Symptom | Cause | Fix |
|---|---|---|
must be a virtual machine Resource ID |
An Arc machine, a scale set, or a child of the machine | Arc servers have their own assignment resource |
configuration profile Resource ID |
A machine ID, or a child of the profile | The pattern is anchored at both ends; either namespace casing is fine |
| Apply fails with a requires-import error | The machine is already onboarded, by any means | There is one assignment per machine; import it or remove the other |
| Apply fails with an authorization error on the profile | The profile is in another subscription | See spans_subscriptions; grant read at the profile |
| Apply fails on a first-time subscription | Automanage was never enabled here | Microsoft documents wider permissions for first-time enablement |
| Apply succeeds, the machine shows nothing | Onboarding runs after the assignment is recorded | Expected; check the Automanage blade, not the plan |
| A service in the profile is not being enforced | The 2025 monitoring-agent change | See monitoring_agent_dependency_was_already_cut; move to Azure Monitor Agent |
| Changing the profile produced a destroy and a create | Both arguments are force-new and the ID is fixed | Expected. There is a brief window with no assignment |
| A setting keeps reverting on the machine | Automanage remediates drift continuously | That is the service working; change the profile, not the machine |
| The assignment vanished | Someone disabled it in the portal | The refresh marks it gone; the next apply re-enrolls the machine |
terraform destroy left a backup vault behind |
Destroy stops management, it does not undo onboarding | Remove what the profile created, deliberately |
| Planning for the service ending | It retires 2027-09-30 | automanage_retirement_date and automanage_migration_target |
azurerm_virtual_machine_automanage_configuration_assignmentβ provider reference- Azure Automanage machine best practices β including the retirement notice
- Configuration profiles β what a profile decides
- Sibling modules:
terraform-azurerm-automanage-configuration,terraform-azurerm-virtual-machine-implicit-data-disk-from-source,terraform-azurerm-linux-virtual-machine,terraform-azurerm-windows-virtual-machine,terraform-azurerm-resource-group - This module's
SCOPE.mdβ the cross-module contract
π "Infrastructure as Code should be standardized, consistent, and secure."