Skip to content

Commit 2bb5669

Browse files
viniciusdcdcmcand
andauthored
Update Azure's schema changes during 2025.2.1 upgrade (#2965)
Co-authored-by: Chuck McAndrew <[email protected]>
1 parent d95bfe1 commit 2bb5669

File tree

3 files changed

+103
-2
lines changed

3 files changed

+103
-2
lines changed

src/_nebari/stages/terraform_state/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ def deploy(
219219
# terraform show command, inside check_immutable_fields
220220
with super().deploy(stage_outputs, disable_prompt, tofu_init=False):
221221
env_mapping = {}
222-
223222
with modified_environ(**env_mapping):
224223
yield
225224

@@ -262,6 +261,7 @@ def check_immutable_fields(self):
262261

263262
def get_nebari_config_state(self) -> dict:
264263
directory = str(self.output_directory / self.stage_prefix)
264+
265265
tf_state = opentofu.show(directory)
266266
nebari_config_state = None
267267

src/_nebari/upgrade.py

+55-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
get_k8s_version_prefix,
3535
get_provider_config_block_name,
3636
load_yaml,
37+
update_tfstate_file,
3738
yaml,
3839
)
3940
from _nebari.version import __version__, rounded_ver_parse
@@ -1649,8 +1650,61 @@ def _version_specific_upgrade(
16491650
https://www.nebari.dev/docs/how-tos/kubernetes-version-upgrade
16501651
"""
16511652
)
1652-
16531653
rich.print(text)
1654+
1655+
# If the Nebari provider is Azure, we must handle a major version upgrade
1656+
# of the Azure Terraform provider (from 3.x to 4.x). This involves schema changes
1657+
# that can cause validation issues. The following steps will attempt to migrate
1658+
# your state file automatically. For details, see:
1659+
# https://github.com/nebari-dev/nebari/issues/2964
1660+
1661+
if config.get("provider", "") == "azure":
1662+
rich.print("\n ⚠️ Azure Provider Upgrade Notice ⚠️")
1663+
rich.print(
1664+
textwrap.dedent(
1665+
"""
1666+
In this Nebari release, the Azure Terraform provider has been upgraded
1667+
from version 3.97.1 to 4.7.0. This major update includes internal schema
1668+
changes for certain resources, most notably the `azurerm_storage_account`.
1669+
1670+
Nebari will attempt to update your Terraform state automatically to
1671+
accommodate these changes. However, if you skip this automatic migration,
1672+
you may encounter validation errors during redeployment.
1673+
1674+
For detailed information on the Azure provider 4.x changes, please visit:
1675+
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/4.0-upgrade-guide
1676+
"""
1677+
)
1678+
)
1679+
1680+
# Prompt user for confirmation
1681+
continue_ = kwargs.get("attempt_fixes", False) or Confirm.ask(
1682+
"Nebari can automatically apply the necessary state migrations. Continue?",
1683+
default=False,
1684+
)
1685+
1686+
if not continue_:
1687+
rich.print(
1688+
"You have chosen to skip the automatic state migration. This may lead "
1689+
"to validation errors during deployment.\n\nFor instructions on manually "
1690+
"updating your Terraform state, please refer to:\n"
1691+
"https://github.com/nebari-dev/nebari/issues/2964"
1692+
)
1693+
exit
1694+
else:
1695+
# In this case the full path in the tfstate file is
1696+
# resources.instances.attributes.enable_https_traffic_only
1697+
MIGRATION_STATE = {
1698+
"enable_https_traffic_only": "https_traffic_only_enabled"
1699+
}
1700+
state_filepath = (
1701+
config_filename.parent
1702+
/ "stages/01-terraform-state/azure/terraform.tfstate"
1703+
)
1704+
1705+
# Perform the state file update
1706+
update_tfstate_file(state_filepath, MIGRATION_STATE)
1707+
16541708
rich.print("Ready to upgrade to Nebari version [green]2025.2.1[/green].")
16551709

16561710
return config

src/_nebari/utils.py

+47
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from pathlib import Path
1616
from typing import Any, Dict, List, Set
1717

18+
import rich
1819
from ruamel.yaml import YAML
1920

2021
from _nebari import constants
@@ -472,3 +473,49 @@ def modified(self):
472473

473474
def __repr__(self):
474475
return f"{self.__class__.__name__}(diff={json.dumps(self.diff)})"
476+
477+
478+
def update_tfstate_file(state_filepath: Path, migration_map: dict) -> None:
479+
"""
480+
Updates a Terraform state file by replacing deprecated attributes with their new
481+
counterparts.
482+
483+
Originally introduced in the Nebari `2025.2.1` release to accommodate major schema
484+
changes from Terraform cloud providers, this function can be extended for future
485+
migrations or patches. By centralizing the replacement logic, it ensures a clean,
486+
modular design that keeps upgrade steps concise.
487+
488+
:param state_filepath: A Path object pointing to the Terraform state file.
489+
:param migration_map: A dictionary where keys are old attribute paths and values are new attribute paths.
490+
"""
491+
if not state_filepath.exists():
492+
rich.print(
493+
f"[red]No Terraform state file found at {state_filepath}. Skipping migration.[/red]"
494+
)
495+
return
496+
497+
try:
498+
with open(state_filepath, "r") as f:
499+
state = json.load(f)
500+
except json.JSONDecodeError:
501+
rich.print(
502+
f"[red]Invalid JSON structure in {state_filepath}. Skipping migration.[/red]"
503+
)
504+
return
505+
506+
# Traverse the resources → instances → attributes hierarchy
507+
# and apply the specified attribute replacements.
508+
for resource in state.get("resources", []):
509+
for instance in resource.get("instances", []):
510+
attributes = instance.get("attributes", {})
511+
for old_attr, new_attr in migration_map.items():
512+
if old_attr in attributes:
513+
attributes[new_attr] = attributes.pop(old_attr)
514+
515+
# Save the modified state back to disk
516+
with open(state_filepath, "w") as f:
517+
json.dump(state, f, indent=2)
518+
519+
rich.print(
520+
f" ✅ [green]Successfully updated the Terraform state file: {state_filepath}[/green]"
521+
)

0 commit comments

Comments
 (0)