Introduction
Lucidity manages disk lifecycles, requiring updates to the Terraform configuration to ensure seamless integration and avoid conflicts. This guide provides detailed instructions, including Azure-specific examples, to help align Terraform with Lucidity's disk management process. While the examples focus on Azure, the Terraform integration approach is adaptable and can be extended to other cloud providers, such as GCP or AWS.
We recommend involving the Lucidity support team during the terraform integration process so we can provide any tailored recommendations to ensure a smooth, streamlined workflow.
This document is mainly split into 2 sections:
Onboard Lucidity for Existing MountPoints
Onboard Lucidity for New MountPoints
On New VMs
On Existing VMs
Note: Currently, OS disks cannot be onboarded via Terraform with Lucidity. This limitation applies to both new and existing VM setups
Onboard Lucidity To Existing MountPoints
Overview
This section of the guide outlines the steps to onboard Lucidity onto already deployed VMs and disks managed by Terraform currently
Steps to Setup Lucidity on Existing MountPoints
Update Terraform Code for Onboarding Lucidity
Adjust the Terraform (TF) code to support Lucidity’s requirements.Install Lucidity Agent on the VMs
Install the Lucidity agent directly on each VM. This can be done manually or via the Lucidity dashboard or in a scripted format based on preference.Onboard Disks to Lucidity
Register the required disks through the Lucidity dashboard (or via APIs)Verify Functionality
Confirm Lucidity’s functionality on the VMs, ensuring that new disks are active and original disks are no longer requiredRemove Original Disks from Terraform
Once onboarded, remove references to the original disks from the Terraform configuration and remove the original disks from the infrastructure.
Terraform Code Overview
Lucidity manages two key resources in the environment, both of which need to be accounted for to avoid drift:
Resource Tag: ManagedByLucidity and MarkedFor
Disks
1. Tag Management
Lucidity applies two tags to the resources it manages: ManagedByLucidity and MarkedFor. Terraform will treat these as drift and try to remove them on every apply, so they have to be excluded.
The AzureRM provider has no provider-level tag-ignore setting. Tags must be excluded per resource, using the lifecycle block described in the next section, on every resource Lucidity tags. The provider block itself needs no changes:
Sample Configuration:
provider "azurerm" {
features {}
}2. Lifecycle Management
Add the ignore_changes attribute to the lifecycle block in the VM resource. This instructs Terraform to disregard any changes to tags that are being managed by Lucidity. Add this block to the disk resource or the VM block where the disk is defined.
Sample Lifecycle Block:
lifecycle {
ignore_changes = [
tags["ManagedByLucidity"],
tags["MarkedFor"]
]
}Note: Adjustments to this code block may vary based on how the Terraform code is structured
3. Removing Original Disks
After onboarding with Lucidity, retain the original disks temporarily as a backup. Eventually, remove them from the Terraform code directly to prevent drifts in infrastructure configuration. Consider the following when updating the code:
How disks and VMs are defined (together or separately managed)
Usage of modules and variable files
Any hardcoded disk references in the Terraform code
Code walkthrough
To provide practical insight into the required Terraform code changes for Lucidity onboarding, here are a few common scenarios:
Scenario 1: Data Disks Defined With the VM
This scenario covers VMs whose data disks are declared alongside the VM itself. Which variant applies depends on which resource your Terraform uses. If your VM resource is azurerm_virtual_machine and contains storage_data_disk blocks, follow 1a. If your VM resource is azurerm_linux_virtual_machine or azurerm_windows_virtual_machine, follow 1b - those resources do not support inline data disks, so your disks are already separate resources.
1a. Legacy resource: azurerm_virtual_machine
Note: azurerm_virtual_machine is deprecated in favour of azurerm_linux_virtual_machine and azurerm_windows_virtual_machine. It is documented here because existing infrastructure still uses it. New Terraform should follow 1b.
Before Lucidity Integration
Initially, the Azure VM is configured with Managed Disks, with the basic setup ensuring disks are attached directly to the VM and tagged accordingly. The following Terraform configuration demonstrates this setup:
resource "azurerm_virtual_machine" "example" {
name = "MyExampleVM"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
vm_size = "Standard_DS1_v2"
network_interface_ids = [
azurerm_network_interface.example.id,
]
storage_os_disk {
name = "myExampleOsDisk"
create_option = "FromImage"
caching = "ReadWrite"
managed_disk_type = "Premium_LRS"
}
storage_data_disk {
name = "example-datadisk1"
create_option = "Empty"
disk_size_gb = 8
lun = 0
caching = "None"
}
storage_data_disk {
name = "example-datadisk2"
create_option = "Empty"
disk_size_gb = 16
lun = 1
caching = "None"
}
tags = {
Environment = "Production"
}
}In this configuration, any change to the VM or its disks would be directly managed by Terraform, including updates to tags and volume settings.
After Lucidity Integration
After integrating Lucidity, below modifications need to be made to the Terraform configuration to delegate management of specific attributes to Lucidity, such as tags and lifecycle changes for the managed disks. This ensures that Lucidity's automated processes can manage these aspects without Terraform attempting to revert them. Here is how the configuration after the changes:
resource "azurerm_virtual_machine" "example" {
name = "MyExampleVM"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
vm_size = "Standard_DS1_v2"
network_interface_ids = [
azurerm_network_interface.example.id,
]
storage_os_disk {
name = "myExampleOsDisk"
create_option = "FromImage"
caching = "ReadWrite"
managed_disk_type = "Premium_LRS"
}
tags = {
Environment = "Production"
}
lifecycle {
ignore_changes = [
storage_data_disk, // Lucidity manages disk lifecycle
tags["MarkedFor"],
tags["ManagedByLucidity"] // Lucidity Tags
]
}
}Key changes
Note, the key change here is the Lifecycle management change using ignore_changes attribute which tells Terraform to ignore any changes to the tags managed by Lucidity.
Note: All disks on the VM will get ignored by TF as per the example in the code. If all disks are not onboarded, this needs to be noted.
1b. Current resources: azurerm_linux_virtual_machine
On the current VM resources, data disks are always separate azurerm_managed_disk resources joined by azurerm_virtual_machine_data_disk_attachment. The same applies to azurerm_windows_virtual_machine.
Before Lucidity Integration
resource "azurerm_linux_virtual_machine" "example" {
name = "MyExampleVM"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
size = "Standard_DS1_v2"
admin_username = "adminuser"
network_interface_ids = [azurerm_network_interface.example.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
tags = {
Environment = "Production"
}
}
resource "azurerm_managed_disk" "datadisk1" {
name = "example-datadisk1"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
storage_account_type = "Premium_LRS"
create_option = "Empty"
disk_size_gb = 8
}
resource "azurerm_virtual_machine_data_disk_attachment" "datadisk1" {
managed_disk_id = azurerm_managed_disk.datadisk1.id
virtual_machine_id = azurerm_linux_virtual_machine.example.id
lun = 0
caching = "None"
}After Lucidity Integration
The onboarded disk and its attachment are removed from the configuration, and the VM gets a lifecycle block for the Lucidity tags.
resource "azurerm_linux_virtual_machine" "example" {
name = "MyExampleVM"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
size = "Standard_DS1_v2"
admin_username = "adminuser"
network_interface_ids = [azurerm_network_interface.example.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
tags = {
Environment = "Production"
}
lifecycle {
ignore_changes = [
tags["ManagedByLucidity"],
tags["MarkedFor"]
]
}
}
// azurerm_managed_disk.datadisk1 and its attachment are removed -
// that disk is now managed by Lucidity. Remove them from state too:
// terraform state rm azurerm_managed_disk.datadisk1
// terraform state rm azurerm_virtual_machine_data_disk_attachment.datadisk1Scenario 2: Externally Managed Disks
This scenario details configuring managed disks that are not directly attached during VM creation. Instead, they are managed separately, allowing flexibility for detaching or reattaching disks without affecting the VM lifecycle. After Lucidity integration, additional configuration is required to facilitate Lucidity's disk management.
Before Lucidity Integration
Initially, managed disks are defined as separate resources and attached to VMs through attachment specifications in Terraform. Here's how you might define this in your Terraform script before integrating with Lucidity:
resource "azurerm_managed_disk" "example_disk_0" {
name = "exampleDisk0"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = 50
}
resource "azurerm_virtual_machine_data_disk_attachment" "example_attach_0" {
managed_disk_id = azurerm_managed_disk.example_disk_0.id
virtual_machine_id = azurerm_linux_virtual_machine.example.id
lun = 0
caching = "ReadWrite"
}
resource "azurerm_managed_disk" "example_disk_1" {
name = "exampleDisk1"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = 100
}
resource "azurerm_virtual_machine_data_disk_attachment" "example_attach_1" {
managed_disk_id = azurerm_managed_disk.example_disk_1.id
virtual_machine_id = azurerm_linux_virtual_machine.example.id
lun = 1
caching = "ReadWrite"
}After Lucidity Integration
After Lucidity is integrated, the below modifications need to be made to ensure that changes to tags and volume_tags are ignored by Terraform. This prevents Terraform from attempting to manage or revert these properties, allowing Lucidity to handle them:
resource "azurerm_managed_disk" "example_disk_1" {
name = "exampleDisk1"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
storage_account_type = "Standard_LRS"
create_option = "Empty"
disk_size_gb = 100
lifecycle {
ignore_changes = [
tags["MarkedFor"],
tags["ManagedByLucidity"] // Lucidity Tags
]
}
}
resource "azurerm_virtual_machine_data_disk_attachment" "example_attach_1" {
managed_disk_id = azurerm_managed_disk.example_disk_1.id
virtual_machine_id = azurerm_linux_virtual_machine.example.id
lun = 1
caching = "ReadWrite"
}State Management
Here note, users need to manually remove the managed disk and disk attachment blocks from the Terraform configuration files when these resources are no longer managed by Terraform
Users must also remove the corresponding state file data using Terraform commands:
terraform state rm azurerm_managed_disk.example_disk_0
terraform state rm azurerm_virtual_machine_data_disk_attachment.example_attach_0As an alternative to manually editing the state file and Terraform configuration, users can use the following command to refresh the state based on the actual infrastructure, effectively accepting any changes made outside of Terraform after disks are removed:
terraform apply -refresh-only -auto-approveScenario 3: Modules and Variable Files for Disk Management
In this scenario, Terraform modules and variable files are used to create and manage disks independently of the VMs. When Lucidity takes over certain aspects of disk management, adjustments are needed within these modules and variables to ensure compatibility.
Before Lucidity Integration
Initially, development teams use the modules by setting the required variables in terraform.tfvars files. An example of this setup might look like:
# In terraform.tfvars file
disk_details = {
"disk0" = {
size = 8
type = "Standard_LRS"
},
"disk1" = {
size = 16
type = "Premium_LRS"
}
}# In main.tf file
module "managed_disks" {
source = "./modules/managed_disks"
disks = var.disk_details
}After Lucidity Integration
After integrating with Lucidity, it's important to ensure the module is adjusted and variable files are updated to reflect the changes. Assuming we are onboarding disk1 the code would look like:
# In terraform.tfvars file
disk_details = {
"disk0" = {
size = 8
type = "Standard_LRS"
}
}
// disk1 removed# In main.tf file
module "managed_disks" {
source = "./modules/managed_disks"
disks = var.disk_details
}
# Assuming the managed_disks module defines resources like this:
resource "azurerm_managed_disk" "example" {
for_each = var.disks
name = "ManagedDisk-${each.key}"
location = "East US"
resource_group_name = azurerm_resource_group.example.name
storage_account_type = each.value.type
create_option = "Empty"
disk_size_gb = each.value.size
lifecycle {
ignore_changes = [
tags["MarkedFor"],
tags["ManagedByLucidity"] //Tags are managed outside of Terraform
]
}
}Key Operations
Modify the Variable File:
Update terraform.tfvars to match the resources and parameters managed by Terraform.
Update the Module Configuration:
If needed, add or modify lifecycle blocks within your modules to prevent Terraform from attempting to manage aspects now handled by Lucidity.
State Reconciliation:
Use terraform apply -refresh-only -auto-approve to update the Terraform state if Lucidity has made changes to the resources.
These 3 scenarios illustrate how Terraform configurations adapt to Lucidity integration across various setups. Each scenario is aimed at maintaining seamless infrastructure management while leveraging Lucidity’s disk management capabilities.
Onboard Lucidity On New MountPoints
Overview
This section of the guide outlines the steps to onboard Lucidity onto new VMs being deployed to the infrastructure using TerraForm or new partitions being added to existing VMs using TerraForm. Lucidity handles the disk management on its end and hence, requires updates to the Terraform configuration to prevent conflicts and manage disk lifecycles.
The process involves configuring the Terraform script to include necessary parameters and making an API call to Lucidity’s dashboard backend. This results in the creation of a new partition from an existing disk pool, which Lucidity then manages
Steps to Setup Lucidity For a New MountPoint
New VM:
Create a new VM using Terraform
Create a new VM using terraform however do NOT add the disks you want to onboard to Lucidity yet.Install Lucidity Agent
Install the Lucidity agent on the VM. This can be done manually or via the Lucidity dashboard or in a scripted format based on preference.Modify Terraform Code to support Lucidity
Adjust the Terraform code to support LucidityOnboard Disks via Terraform
Add the new partition using Terraform as described by the next Code walkthrough section.Verify Functionality
As a check, verify disk has been onboarded via the dashboard and by logging into the VM.
Existing VM:
If the VM already has disks onboarded to Lucidity and you are looking to add more disks to the same VM:
Steps 1 - 3
Steps 1 to step 3 (from the previous section) should already be completed since VM has already been onboarded however we recommend verifying the same.Onboard Disks via Terraform
Adjust the Terraform code to add the new partition as described by the next section.Ensure disk configurations matches existing Lucidity disks
When defining the disk, the new disk host cache setting, disk type and tags have to be the same as existing Lucidity disks.Verify Functionality
As a check, verify disk has been onboarded via the dashboard and by logging into the VM.
Terraform Code Walkthrough
Ensure that the Provider Block has been updated to support onboarding Lucidity (as defined in previous sections).
Ensure that the Lifecycle Block has been updated to support onboarding Lucidity (as defined in previous sections).
Since no original disks need to be removed, no changes needed to the Terraform code for existing disks.
Users only need to modify their existing Terraform scripts to automate the partition creation and onboarding process for the new disk that is to be added. The script will include details like the partition name, instance ID, disk type, and Azure-specific settings such as disk cache options.
Here is a sample Terraform script tailored for Azure (Windows):
resource "null_resource" "create_new_mount_instance_windows" {
provisioner "local-exec" {
on_failure = fail
interpreter = ["PowerShell", "-Command"]
command = <<-EOT
$uri = "http://<dashboardurl>/api/v1/partition/create"
$headers = @{
"Authorization" = "secretkey"
"X-Authtype" = "auth_key"
"X-Tenant" = "<tenantId>"
"X-Account" = "<accountid>"
"accept" = "*/*"
"Content-Type" = "application/json"
"access-id" = "accesskey"
}
$body = @{
"diskType" = "Standard_LRS" # Azure specific disk type
"instance" = "<instanceid>"
"partition" = "E" # New partition to be created
"tenant" = "<tenantId>"
"diskCache" = "ReadWrite" # Azure specific cache setting
"azureCmkDiskEncryptionSetId" = "/subscriptions/<sub>/resourceGroups/<rgrp>/providers/Microsoft.Compute/diskEncryptionSets/<cmkId>"
"azureCmkPmkDiskEncryptionSetId" = "/subscriptions/<sub>/resourceGroups/<rgrp>/providers/Microsoft.Compute/diskEncryptionSets/<pmkId>"
} | ConvertTo-Json
Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body
EOT
}
triggers = {
always_run = timestamp()
}
}Here is a sample Terraform script tailored for Azure (Linux):
resource "null_resource" "create_new_mount_instance_azure" {
provisioner "local-exec" {
command = <<-EOT
#!/bin/bash
uri="http://<dashboardurl>/api/v1/partition/create"
headers=(
-H "Authorization: secretkey"
-H "X-Authtype: auth_key"
-H "X-Tenants: <tenantId>"
-H "X-Tenant: <tenantId>"
-H "X-Account: <accountid>"
-H "accept: */*"
-H "Content-Type: application/json"
-H "access-id: accesskey"
)
body=$(jq -n \
--arg diskType "<diskType>" \
--arg instance "<instanceid>" \
--arg partition "J" \
--arg tenant "<tenantId>" \
--arg azureCmkDiskEncryptionSetId "/subscriptions/<sub>/resourceGroups/<rgrp>/providers/Microsoft.Compute/diskEncryptionSets/<cmkId>" \
--arg azureCmkPmkDiskEncryptionSetId "/subscriptions/<sub>/resourceGroups/<rgrp>/providers/Microsoft.Compute/diskEncryptionSets/<pmkId>" \
'{
diskType: $diskType,
instance: $instance,
partition: $partition,
tenant: $tenant,
azureCmkDiskEncryptionSetId: $azureCmkDiskEncryptionSetId,
azureCmkPmkDiskEncryptionSetId: $azureCmkPmkDiskEncryptionSetId
}')
curl -X POST "${headers[@]}" -d "$body" "$uri"
EOT
}
triggers = {
always_run = timestamp()
}
}Parameters and Options:
diskType: Users should provide the disk type appropriate for their use case. Azure options include Premium_LRS, Standard_LRS, etc.
diskCache: For Azure, options like "None", "ReadOnly", and "ReadWrite" allow customization of how the disk interacts with cached data.
instance, tenant, and partition: Mandatory fields to specify the exact resource being managed.
azureCmkDiskEncryptionSetId and azureCmkPmkDiskEncryptionSetId: These fields refer to the customer-managed keys and platform-managed keys. These IDs link to the Azure disk encryption sets that encrypt the disk partitions. Users need to provide these if they have specific encryption requirements or default settings will be applied if left empty.
Integration Process:
Upon executing the updated Terraform script, an API call is made to Lucidity’s backend, which handles the creation and immediate integration of the new partition. This ensures that the new partition is set up for automated management actions, such as capacity adjustments based on usage.