- Description
- Why Managed Identity?
- Setup - Getting started with azure_vm_tags
- Usage - How to use the module
- How Authentication Works
- Local Testing on Azure VMs
- Limitations
- Development
- Reference
This Puppet module allows you to manage tags on Azure Virtual Machines using the Azure REST API and Managed Identity authentication. It provides a custom type (azure_vm_tag) with a provider (v1) that retrieves and updates tags on Azure VMs directly from within the VM itself.
This module uses Azure Managed Identity for authentication, which provides significant security and operational benefits:
- âś… No credentials to manage - No client secrets, passwords, certificates, or API keys required
- âś… Automatic token management - Azure handles token generation, rotation, and expiration
- âś… Secure by default - Tokens are obtained from Azure Instance Metadata Service (IMDS), only accessible from within the VM
- âś…Provider Testing on a Node (Local T-id>/resourceGroups//providers/Microsoft.Compute/virtualMachines/
#### 3. Verify Facter Facts
Ensure that the following fact is available via Facter:
- `az_metadata` - Must include `compute.resourceId` (the full Azure resource ID of the VM)
You can verify this by running:
```bash
facter az_metadata
# Set a tag with a simple value
azure_vm_tag { 'Environment':
ensure => present,
value => 'production',
}
# Set multiple tags
azure_vm_tag { 'Owner':
ensure => present,
value => 'DevOps Team',
}
azure_vm_tag { 'CostCenter':
ensure => present,
value => 'Engineering',
}
azure_vm_tag { 'Application':
ensure => present,
value => 'WebServer',
}# Use Facter facts dynamically
azure_vm_tag { 'FQDN':
ensure => present,
value => $facts['fqdn'],
}
azure_vm_tag { 'Hostname':
ensure => present,
value => $facts['hostname'],
}
azure_vm_tag { 'OS':
ensure => present,
value => "${facts['os']['name']} ${facts['os']['release']['major']}",
}
azure_vm_tag { 'PuppetVersion':
ensure => present,
value => $facts['puppetversion'],
}# Remove a tag
azure_vm_tag { 'Temporary':
ensure => absent,
}
azure_vm_tag { 'OldTag':
ensure => absent,
}class profile::azure_tags {
# Standard organizational tags
azure_vm_tag { 'Environment':
ensure => present,
value => lookup('azure_environment', String, 'first', 'production'),
}
azure_vm_tag { 'Role':
ensure => present,
value => lookup('azure_role', String, 'first', 'webserver'),
}
azure_vm_tag { 'Owner':
ensure => present,
value => lookup('team_owner', String, 'first', 'DevOps'),
}
# Automated tags from facts
azure_vm_tag { 'FQDN':
ensure => present,
value => $facts['fqdn'],
}
azure_vm_tag { 'ManagedBy':
ensure => present,
value => 'Puppet',
}
azure_vm_tag { 'LastPuppetRun':
ensure => present,
value => strftime('%Y-%m-%d %H:%M:%S'),
}
}Then include the profile in your node classification:
node 'myserver.example.com' {
include profile::azure_tags
}- Puppet agent starts on the Azure VM
- Provider requests token from Azure IMDS endpoint:
http://169.254.169.254/metadata/identity/oauth2/token - Azure validates that the request originated from within the VM with Managed Identity
- IMDS returns an OAuth 2.0 Bearer token scoped for Azure Resource Manager (
https://management.azure.com/) - Provider uses token to authenticate REST API calls to Azure Resource Manager
- Tags are read/updated via HTTP GET and PATCH requests to the Azure REST API
- Authentication Method: OAuth 2.0 Bearer Token
- Token Source: Azure Instance Metadata Service (IMDS)
- IMDS Endpoint:
http://169.254.169.254/metadata/identity/oauth2/token - IMDS API Version:
2018-02-01 - Resource Scope:
https://management.azure.com/ - Token Lifetime: Typically ~24 hours (automatically refreshed on each Puppet run)
- Timeout: 5 seconds for both connection and read operations
- Required IAM Role:
Tag ContributororContributoron the VM resource - Supported Identity Types: System-assigned and user-assigned managed identities
- No External Dependencies: Uses only Ruby standard library (
net/http,json) - No Token Caching: Each Puppet run requests a fresh token (IMDS response time: ~50-100ms)
Test the module directly on an Azure VM with Managed Identity for quick validation.
-
Azure VM with Managed Identity:
az vm identity assign --name <vm-name> --resource-group <resource-group>
-
IAM Permissions:
az role assignment create --assignee <managed-identity-principal-id> \ --role "Tag Contributor" --scope /subscriptions/<sub-id>/resourceGroups/<rg>
-
Puppet agent and Facter: Verify
facter az_metadatareturnscompute.resourceId
Create module structure on the VM:
sudo mkdir -p /etc/puppetlabs/code/environments/production/modules/azure_vm_tag/{lib/puppet/{type,provider/azure_vm_tag},manifests}
# Copy module files
sudo cp azure_vm_tag.rb /etc/puppetlabs/code/environments/production/modules/azure_vm_tag/lib/puppet/type/
sudo cp v1.rb /etc/puppetlabs/code/environments/production/modules/azure_vm_tag/lib/puppet/provider/azure_vm_tag/Create manifests/init.pp:
class azure_vm_tag (
Enum['present', 'absent'] $ensure = 'present',
) {
azure_vm_tag { 'FQDN':
ensure => $ensure,
value => $facts['fqdn'],
}
azure_vm_tag { 'Environment':
ensure => $ensure,
value => 'testing',
}
}| Scenario | Manifest Change | Expected Result |
|---|---|---|
| Create tags | ensure => 'present' with new values |
Notice: .../ensure: created |
| Update values | Change value parameter |
Notice: .../value: value changed 'old' to 'new' |
| Remove tags | ensure => 'absent' |
Notice: .../ensure: removed |
| Idempotency | Run twice with no changes | Second run: Applied catalog in 0.30 seconds (no changes) |
Test command:
cd /etc/puppetlabs/code/environments/production/modules
puppet apply --modulepath=. -e 'include azure_vm_tag'| Issue | Solution |
|---|---|
| "Could not determine resourceId" | Verify facter az_metadata returns compute.resourceId |
| "Timeout connecting to IMDS" | Ensure Managed Identity is enabled |
| 403 error | Verify IAM role: Tag Contributor or higher |
Performance: First run ~1-2s (API calls), subsequent idempotent runs ~0.3s.
This module has the following limitations:
- Azure Only: This module only works on Azure Virtual Machines running in Azure. It does not support:
- On-premises virtual machines
- VMs running in other cloud providers (AWS, GCP, etc.)
- Local development machines
- Managed Identity Only: By design, this module only supports Azure Managed Identity authentication. It intentionally does not support:
- Service Principal authentication (less secure, requires credential management)
- Azure Arc-enabled servers (may be added in a future version)
- Certificate-based authentication
- VM Tags Only: Currently supports managing tags on the VM resource itself. Does not support:
- Tags on other Azure resources (storage accounts, networks, etc.)
- Resource Group tags
- Subscription tags
- Agent-Side Only: This module runs on the Puppet agent (the Azure VM itself). If your Puppet server is running outside of Azure:
- The module will still work on Azure VMs with Managed Identity
- Consider using Azure Arc if you need Managed Identity for hybrid scenarios
- Alternatively, run your Puppet server in Azure
- No Bulk Operations: Each tag is managed individually (this is by design for idempotency)
- Case Sensitivity: Tag names are case-insensitive in Azure, but the prefetch method handles this automatically
- No Tag Validation: Azure tag naming restrictions are not validated client-side (the API will return errors if invalid)
Features being considered for future versions:
- Support for Azure Arc-enabled servers
- Support for managing tags on other Azure resources
- Bulk tag operations
- Tag validation before API calls
Contributions are welcome! Please follow these guidelines:
- Fork and clone the repository
- Create a feature branch:
git checkout -b feature/my-feature - Install dependencies:
bundle install - Run tests before submitting:
bundle exec rake validate # Validate syntax bundle exec rake lint # Check style bundle exec rake spec # Run RSpec tests
- Submit a Pull Request with:
- Clear description of changes
- Reference to related issues
- Passing test results
- Follow the Puppet Language Style Guide
- Write RSpec tests for new features
- Update README.md and CHANGELOG.md
Use the GitHub issue tracker. Include:
- Puppet and module versions
- OS and version
- Steps to reproduce
- Error messages/logs
Manages tags on the current Azure VM.
ensure- Whether the tag should exist. Valid values:present,absent. Default:presentname- (namevar) The name of the tag. Tag names in Azure:- Can contain alphanumeric characters, spaces, and these special characters:
+ - = . _ : / - Maximum length: 512 characters
- Are case-insensitive (but case is preserved)
- Can contain alphanumeric characters, spaces, and these special characters:
value- The value of the tag. Required whenensure => present.- Maximum length: 256 characters
- Can contain most characters
The default (and only) provider for managing Azure VM tags.
- Uses Azure Managed Identity for authentication
- Retrieves tokens from Azure Instance Metadata Service (IMDS)
- Makes REST API calls to Azure Resource Manager
- Implements idempotent tag operations
See the Usage section above for complete examples.
This module does not provide any Puppet functions.
This module relies on the az_metadata fact provided by Facter, which should include the VM's compute.resourceId.
Module Version: 0.1.0 License: Apache-2.0 Supported Puppet Versions: 6.0.0 and later