Skip to content

About

Terraform Azure Verified Resource Module for Maintenance Configuration

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

6 watching

Forks

Latest commit

 

History

70 Commits

Folders and files

Repository files navigation

terraform-azurerm-avm-res-maintenance-maintenanceconfiguration

This modules is used to create and manage a Maintenance Configuration which is a supporting service of Azure Update Manager.

For information on how to consume this module, consult the examples directory.

Upgrading from v0.1.0

The maintenance configuration already used AzAPI in v0.1.0. This upgrade renames its Terraform address and migrates the AzureRM lock and role assignments to AzAPI at the same module and collection boundaries. Existing consumer module keys and role assignment map keys must stay unchanged.

Use Terraform >= 1.9, < 2.0 and AzAPI ~> 2.12. Replace the module's resource_group_name argument with the existing resource group's fully-qualified parent_id, for example:

parent_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example"

Remove the optional subscription_id argument if configured; the subscription is now derived from parent_id.

The name and resource_id outputs retain their existing meanings and types. The new timeouts object has flat create, read, update, and delete fields and defaults to provider timeouts. Non-empty ignore_body_changes lists require Terraform 1.11 or later; the default empty lists do not.

Automatic state conversion

The module owns these declarative moves. Do not copy them into the consumer or run terraform state mv for the standard upgrade:

Released address (relative to the consumer module) New address Mechanism
azapi_resource.maintenance_configuration azapi_resource.this Same-provider move
azurerm_management_lock.this[0] azapi_resource.lock[0] Provider state conversion
azurerm_role_assignment.this["key"] azapi_resource.role_assignments["key"] Provider state conversion, preserving each key and existing assignment GUID
modtm_telemetry.telemetry and random_uuid.telemetry No replacement state binding Managed telemetry removed blocks with destroy = false

There are no inline ARM children to import and no avm_managed_migration_enabled flag for the standard upgrade. Leave unrelated configuration unchanged during the migration.

  1. Back up state securely outside version control and record the existing ARM resource IDs, role assignment GUIDs, lock name and notes. Keep the original AzureRM provider configuration (including features {}) available through the upgrade apply. Retain the old modtm and random providers for initialization while state still refers to the old telemetry resources.

  2. Remove any ReadOnly lock before upgrading: it blocks the reconciliation PUT. Set lock = null and apply using the previous module version first, then upgrade with the lock still disabled. Restore it in a separate apply after the upgrade. A CanNotDelete lock may remain configured.

  3. Change the module source/version and parent_id, retaining the same resource group, name, location, scope, role assignment keys and other settings.

  4. Initialize and save a plan:

    terraform init -upgrade
    terraform plan -out=migration.tfplan
    terraform show -json migration.tfplan > migration.plan.json
  5. Inspect all moves, output changes and actions before applying. Do not apply a plan that deletes or replaces the maintenance configuration, lock or role assignments. In-place reconciliation is possible. New random UUID state inside the interface utility does not replace existing assignment GUIDs. A lock-removal timer is created only when a lock is configured.

  6. Apply the inspected saved plan with terraform apply migration.tfplan. Check that the resource IDs, assignment GUIDs and lock details are unchanged, then run another plan to establish convergence. Remove obsolete provider configurations only after their state references are gone.

Current managed telemetry creates empty subscription-scoped Azure deployments when enabled, including in the interface utility. The deployment identity needs Microsoft.Resources/deployments/read, write, and delete at the active subscription scope. Telemetry intentionally updates only its body.properties.template.outputs.apply_id.value timestamp on each normal plan. With enable_telemetry = false, require a completely empty follow-up plan; with telemetry enabled, require no infrastructure drift beyond those exact deployment timestamps. The opt-out is forwarded to the utility.

Cross-tenant role assignment exception

This exception applies only to assignments with a composite ID in AzureRM state. Ordinary assignment IDs use the automatic moves above and do not need state removal or consumer-root imports.

AzureRM can store a cross-tenant assignment ID as <ARM-ID>|<tenant-ID>. AzAPI's provider move does not strip that suffix, so this case is not covered by the automatic move. Before planning the candidate, back up state, obtain the existing assignment's ARM ID and GUID, and remove only its old binding:

terraform state rm 'module.maintenance.azurerm_role_assignment.this["delegated"]'

In the consumer root, add an import targeting the corresponding new address:

import {
  to = module.maintenance.azapi_resource.role_assignments["delegated"]
  id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example/providers/Microsoft.Maintenance/maintenanceConfigurations/example/providers/Microsoft.Authorization/roleAssignments/11111111-1111-1111-1111-111111111111"
}

Use the actual consumer module path, including any module instance keys, and the actual existing ARM ID without the tenant suffix. Preserve the original assignment inputs and provider tenant context. Inspect and apply the saved import plan, verify the GUID and delegated identity, then remove the root import block. Never remove a binding without its matching import. This exceptional procedure requires separate state-backed verification in the consumer's tenant context.

Behavior and recovery

Lock names and default notes are preserved. Lock deletion waits 30 seconds before deleting covered resources, including role assignments, when the lock is being removed. This teardown ordering does not automatically remove an unchanged lock for a role-assignment-only replacement. Role definition names are resolved case-insensitively at the existing resource group; tenant-level definition IDs are subscription-qualified. skip_service_principal_aad_check = true supplies principalType = "ServicePrincipal" unless an explicit principal type overrides it. Changing immutable assignment inputs still requires replacement; perform such changes separately from the upgrade. The name input controls new assignments; an adopted assignment retains its GUID, and a later explicit name change triggers replacement.

Changing role assignments under a lock

Do not combine the state migration with intentional assignment replacement. After completing the upgrade, use separate applies for changes to immutable assignment inputs when a module-owned lock is configured:

  1. Keep the assignment inputs unchanged, set lock = null, and apply a reviewed plan that removes only the lock and its associated timer. Confirm the lock is absent before continuing.
  2. Change the assignment inputs, inspect the planned replacement and its permission impact, then apply that separately reviewed plan.
  3. Restore the original lock input and apply a final reviewed plan.

This is a staged operational procedure, not automatic lock orchestration. The combined lock-removal, assignment-replacement and lock-restoration sequence has not been state-backed verified for this migration candidate. Neither has the ReadOnly remove-and-restore upgrade exception. Verify these exceptional paths in an isolated consumer environment before using them in production; successful unchanged-input migration and full teardown do not prove either sequence.

Do not assume reverting the module version rolls back converted provider state. After an apply, recovery requires reconciling both state and Azure, using the protected backup and verified live identities. A saved state alone does not undo Azure mutations.

Requirements

The following requirements are needed by this module:

Resources

The following resources are used by this module:

Required Inputs

The following input variables are required:

Description: (Required) Azure region where the resource should be deployed.

Type: string

Description: (Required) The name of the this resource.

Type: string

Description: The Azure resource ID of the parent resource group, in the form /subscriptions/{subscription_id}/resourceGroups/{resource_group_name}.

Type: string

Description: (Required) The scope of the Maintenance Configuration. Possible values are Extension, Host, InGuestPatch, OSImage, SQLDB or SQLManagedInstance.

Type: string

Optional Inputs

The following input variables are optional (have default values):

Description: This variable controls whether or not telemetry is enabled for the module.
For more information see https://aka.ms/avm/telemetryinfo.
If it is set to false, then no telemetry will be collected.

Type: bool

Default: true

Description: (Optional) The extension properties of the Maintenance Configuration. Must be specified when scope is Extension.

Type: map(string)

Default: {}

Description: Body-relative paths to ignore for each AzAPI resource, using dot notation. Ignored configuration is not sent to Azure until the path is removed. Changes take effect only after apply. Non-empty lists require Terraform 1.11 or later.

  • authorization_locks - Paths ignored on management locks.
  • authorization_role_assignments - Paths ignored on role assignments.
  • maintenance_maintenance_configurations - Paths ignored on the maintenance configuration.

Type:

object({
    authorization_locks                    = optional(list(string), [])
    authorization_role_assignments         = optional(list(string), [])
    maintenance_maintenance_configurations = optional(list(string), [])
  })

Default: {}

Description: (Optional) The install patches of the Maintenance Configuration. Must be specified when scope is InGuestPatch.

  • linux - (Optional) The Linux parameters of the Maintenance Configuration. This property only applies when scope is set to InGuestPatch.
    • classifications_to_include - (Optional) The classifications to include. Defaults to Critical & Security
    • package_name_masks_to_exclude - (Optional) The package name masks to exclude.
    • package_name_masks_to_include - (Optional) The package name masks to include.
  • reboot_setting - (Optional) Possible reboot preference as defined by the user based on which it would be decided to reboot the machine or not after the patch operation is completed. Possible values are Always, IfRequired and Never. This property only applies when scope is set to InGuestPatch.
  • windows - (Optional) The Windows parameters of the Maintenance Configuration. This property only applies when scope is set to InGuestPatch.
    • classifications_to_include - (Optional) List of Classification category of patches to be patched. Possible values are Critical, Security, UpdateRollup, FeaturePack, ServicePack, Definition, Tools and Updates. Defaults to Critical & Security
    • exclude_kbs_requiring_reboot - (Optional) The exclude Kbs requiring reboot.
    • kb_numbers_to_exclude - (Optional) The KB numbers to exclude.
    • kb_numbers_to_include - (Optional) The KB numbers to include.

Type:

object({
    linux = optional(object({
      classifications_to_include    = optional(list(string), ["Critical", "Security"])
      package_name_masks_to_exclude = optional(list(string), [])
      package_name_masks_to_include = optional(list(string), [])
    }))
    reboot_setting = optional(string)
    windows = optional(object({
      classifications_to_include   = optional(list(string), ["Critical", "Security"])
      exclude_kbs_requiring_reboot = optional(bool)
      kb_numbers_to_exclude        = optional(list(string), [])
      kb_numbers_to_include        = optional(list(string), [])
    }))
  })

Default: {}

Description: Controls the Resource Lock configuration for this resource. The following properties can be specified:

  • kind - (Required) The type of lock. Possible values are \"CanNotDelete\" and \"ReadOnly\".
  • name - (Optional) The name of the lock. If not specified, a name will be generated based on the kind value. Changing this forces the creation of a new resource.
  • notes - (Optional) Notes about the lock. This value maps to Microsoft.Authorization/locks.properties.notes.

Type:

object({
    kind  = string
    name  = optional(string, null)
    notes = optional(string, null)
  })

Default: null

Description: AzAPI resource types and API versions used by this module.

  • authorization_locks - Resource type and API version for management locks.
  • authorization_role_assignments - Resource type and API version for role assignments.
  • maintenance_maintenance_configurations - Resource type and API version for the maintenance configuration.

Type:

object({
    authorization_locks                    = optional(string, "Microsoft.Authorization/locks@2020-05-01")
    authorization_role_assignments         = optional(string, "Microsoft.Authorization/roleAssignments@2022-04-01")
    maintenance_maintenance_configurations = optional(string, "Microsoft.Maintenance/maintenanceConfigurations@2023-04-01")
  })

Default: {}

Description: Retry configuration applied to every supported AzAPI resource declared by the module.

  • error_message_regex - Error patterns triggering a retry. Defaults to ReferencedResourceNotProvisioned.
  • interval_seconds - Initial interval in seconds. Defaults to 10.
  • max_interval_seconds - Maximum interval in seconds. Defaults to 180.

Type:

object({
    error_message_regex  = optional(list(string), ["ReferencedResourceNotProvisioned"])
    interval_seconds     = optional(number, 10)
    max_interval_seconds = optional(number, 180)
  })

Default: {}

Description: A map of role assignments to create on the maintenance configuration. The map key is deliberately arbitrary to avoid issues where map keys maybe unknown at plan time.

  • name - (Optional) The name of the role assignment. If not set, a random UUID will be generated. Changing this forces the creation of a new resource.
  • role_definition_id_or_name - The ID or name of the role definition to assign to the principal.
  • principal_id - The ID of the principal to assign the role to.
  • description - (Optional) The description of the role assignment.
  • skip_service_principal_aad_check - (Optional) If set to true, skips the Azure Active Directory check for the service principal in the tenant. Defaults to false.
  • condition - (Optional) The condition which will be used to scope the role assignment.
  • condition_version - (Optional) The version of the condition syntax. Leave as null if you are not using a condition, if you are then valid values are '2.0'.
  • delegated_managed_identity_resource_id - (Optional) The delegated Azure Resource Id which contains a Managed Identity. Changing this forces a new resource to be created. This field is only used in cross-tenant scenario.
  • principal_type - (Optional) The type of the principal_id. Possible values are User, Group and ServicePrincipal. It is necessary to explicitly set this attribute when creating role assignments if the principal creating the assignment is constrained by ABAC rules that filters on the PrincipalType attribute.

Note: only set skip_service_principal_aad_check to true if you are assigning a role to a service principal.

Type:

map(object({
    name                                   = optional(string, null)
    role_definition_id_or_name             = string
    principal_id                           = string
    description                            = optional(string, null)
    skip_service_principal_aad_check       = optional(bool, false)
    condition                              = optional(string, null)
    condition_version                      = optional(string, null)
    delegated_managed_identity_resource_id = optional(string, null)
    principal_type                         = optional(string, null)
  }))

Default: {}

Description: (Optional) Tags of the resource.

Type: map(string)

Default: null

Description: Per-operation timeouts applied to every supported AzAPI resource declared by the module. Defaults to null (provider defaults). Each value is a Go duration string, for example 30m.

  • create - Timeout for create operations.
  • read - Timeout for read operations.
  • update - Timeout for update operations.
  • delete - Timeout for delete operations.

Type:

object({
    create = optional(string)
    read   = optional(string)
    update = optional(string)
    delete = optional(string)
  })

Default: null

Description: (Optional) The visibility of the Maintenance Configuration. The only allowable value is Custom. Defaults to Custom.

Type: string

Default: "Custom"

Description: (Optional) The maintenance window of the Maintenance Configuration.

  • duration - (Optional) The duration of the maintenance window in HH:mm format. Defaults to 01:30
  • expiration_date_time - (Optional) Effective expiration date of the maintenance window in YYYY-MM-DD hh:mm format.
  • recur_every - (Required) The rate at which a maintenance window is expected to recur. The rate can be expressed as daily, weekly, or monthly schedules.
  • start_date_time - (Required) Effective start date of the maintenance window in YYYY-MM-DD hh:mm format.
  • time_zone - (Required) The time zone for the maintenance window. A list of timezones can be obtained by executing [System.TimeZoneInfo]::GetSystemTimeZones() in PowerShell.

Type:

object({
    duration             = optional(string, "01:30")
    expiration_date_time = optional(string)
    recur_every          = string
    start_date_time      = string
    time_zone            = string
  })

Default: null

Outputs

The following outputs are exported:

Description: The name of the Maintenance Configuration resource.

Description: The ID of the Maintenance Configuration resource.

Modules

The following Modules are called:

Source: Azure/avm-utl-interfaces/azure

Version: 0.9.0

Data Collection

The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.

About

Terraform Azure Verified Resource Module for Maintenance Configuration

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages