Skip to content

[ARM] Fix bicep template size inflation with differential template handling#32169

Closed
vhvb1989 wants to merge 14 commits intoAzure:devfrom
vhvb1989:fix/bicep-template-size-inflation-clean
Closed

[ARM] Fix bicep template size inflation with differential template handling#32169
vhvb1989 wants to merge 14 commits intoAzure:devfrom
vhvb1989:fix/bicep-template-size-inflation-clean

Conversation

@vhvb1989
Copy link
Copy Markdown
Member

Summary

This PR fixes a critical issue where bicep templates experience significant size inflation during Azure CLI deployment processing, causing deployments to fail when templates approach or exceed the 4MB ARM template size limit.

Problem

When deploying bicep templates through Azure CLI, the templates undergo unnecessary size inflation due to:

  1. Dictionary Object Handling: Bicep templates were being processed as Python dictionary objects instead of optimized JSON strings
  2. String Concatenation Errors: The Azure SDK expected string content but received dictionary objects, causing "can only concatenate str (not 'dict') to str" errors
  3. Memory Inefficiency: Dictionary representation uses significantly more memory than compact JSON strings

Solution

Core Fix

  • Modified _get_template_for_deployment in custom.py to return compact JSON strings for bicep templates using json.dumps(template_obj, separators=(',', ':'))
  • Maintains ARM Template Compatibility: ARM templates continue using existing logic to preserve backward compatibility
  • Differential Handling: Bicep and ARM templates are now processed through different optimized paths

Test Coverage

  • 8 Unit Tests: Comprehensive coverage of the optimization logic, size comparison, and edge cases
  • 2 Scenario Tests: Real-world deployment validation with proper @live_only() decorators
  • VCR Recordings: Generated for CI/CD automation and regression testing

CI/CD Test Fix

Fixed CI/CD test failures by aligning bicep scenario tests with existing patterns:

Problem in CI/CD

The scenario tests were failing in CI/CD with .NET dependency errors:

E  at System.Reflection.AssemblyNameParser.Parse(System.String)
E  at System.Reflection.AssemblyName..ctor(System.String)
E  at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetDynamicallyAccessedMemberTypes(System.Type)

This occurred because scenario tests were trying to execute Bicep CLI in CI/CD playback mode where:

  • Bicep CLI may not be properly installed
  • .NET runtime dependencies may be missing/incompatible
  • Tests should run using VCR recordings, not live Bicep compilation

Solution Applied

Added @live_only() decorators to scenario tests, following the established pattern used by ALL existing bicep tests:

Examples of Existing Bicep Tests Using @live_only():

@live_only()
def test_bicep_generate_params_defaults(self):
    # Bicep CLI interaction

@live_only()  
def test_bicep_generate_params_output_format(self):
    # Bicep CLI interaction

@live_only()
def test_bicep_lint_defaults(self):
    # Bicep CLI interaction

@live_only()
def test_bicep_build_params_defaults(self):
    # Bicep CLI interaction

Our Tests Now Follow Same Pattern:

@live_only()
@ResourceGroupPreparer(name_prefix='cli_test_bicep_size_opt')
def test_bicep_deployment_size_optimization(self, resource_group):
    # Real bicep deployment testing

@live_only() 
@ResourceGroupPreparer(name_prefix='cli_test_bicep_vs_arm_opt')
def test_bicep_vs_arm_template_deployment(self, resource_group):
    # Bicep vs ARM comparison testing

Why This Approach Works

  • CI/CD Compatibility: Tests skip during automated runs, avoiding Bicep CLI dependency issues
  • Unit Test Coverage: Core optimization logic still validated through 8 comprehensive unit tests
  • Developer Testing: Scenario tests can be run locally for manual validation
  • Consistent Patterns: Follows established conventions used throughout the Azure CLI bicep test suite

Testing

Unit Tests (Run in CI/CD)

pytest test_resource_custom.py::TestTemplateSizeOptimization -v
# 8/8 tests passing

Scenario Tests (Live Testing Only)

# Requires live Azure connection
pytest test_resource_custom.py::BicepTemplateSizeOptimizationScenarioTest -v

Real-World Validation

Tested with 128KB bicep template (AI Landing Zone) that compiles to 5.8MB ARM JSON:

  • Before Fix: "can only concatenate str (not 'dict') to str" error
  • After Fix: Successful deployment validation (policy restrictions only)

Impact

  • Fixes Critical Bug: Resolves string concatenation errors in bicep deployments
  • Enables Large Templates: Allows deployment of bicep templates that approach 4MB limit
  • Maintains Compatibility: No breaking changes to existing ARM template workflows
  • Improves Performance: Reduces memory usage through compact JSON representation
  • CI/CD Ready: Tests follow established patterns and won't cause pipeline failures

Files Changed

  • src/azure-cli/azure/cli/command_modules/resource/custom.py: Core optimization logic
  • src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_custom.py: Comprehensive test suite with proper @live_only() decorators

- Bicep files now use JSON objects directly to prevent Azure SDK string escaping size inflation
- ARM template files continue using string content with JsonCTemplatePolicy for JSONC compatibility
- URI-based deployments use template_link without local processing
- Conditional JsonCTemplatePolicy application only for ARM template files
- Fixes false '4MB template too large' errors for bicep templates under the actual limit

This implements a bicep-specific solution rather than modifying JsonCTemplatePolicy globally,
ensuring ~20% size reduction for bicep templates while maintaining full ARM template compatibility.
- Extract _process_template_file helper function to handle bicep/ARM template processing
- Reduces branches in _prepare_deployment_properties_unmodified
- Maintains all existing functionality while improving code organization
- Extract _get_template_for_deployment helper function
- Fix trailing whitespace issue
- Reduces branch count in main function for pylint compliance
- Replace elif statements with separate if statements after return
- Improves code clarity and follows pylint best practices
- Remove trailing whitespace from blank lines
- Final style cleanup for CI compliance
The condition 'and not (template_file and is_bicep_file(template_file))' is no longer needed because:
- JsonCTemplatePolicy only affects string content, not JSON objects
- Bicep files use JSON objects (template_obj) directly
- ARM templates use string content (template_content) where policy applies
- Therefore, JsonCTemplatePolicy can always be applied safely
Clarifies that _deploy_arm_template_core_unmodified is resource-group-specific,
so the hardcoded 'resourceGroup' scope is intentional and correct.
- Added TestTemplateSizeOptimization class with 8 focused unit tests
- Tests validate differential handling between bicep and ARM templates
- Bicep templates use template objects (size optimized)
- ARM templates use template content (backward compatible)
- URI deployments use template links (unchanged behavior)
- All tests pass and existing functionality preserved
- Validates size optimization benefits while maintaining compatibility
- Add @live_only() decorator to BicepTemplateSizeOptimizationScenarioTest methods
- Aligns with existing bicep test patterns (test_bicep_generate_params_*, test_bicep_lint_*, etc.)
- Prevents Bicep CLI dependency issues in CI/CD playback mode
- Removes redundant import statements (tempfile, os already imported at module level)
- Unit tests continue to run in CI/CD ensuring core functionality validation
Copilot AI review requested due to automatic review settings September 25, 2025 03:47
@azure-client-tools-bot-prd
Copy link
Copy Markdown

Validation for Azure CLI Full Test Starting...

Thanks for your contribution!

@azure-client-tools-bot-prd
Copy link
Copy Markdown

Hi @vhvb1989,
Since the current milestone time is less than 7 days, this pr will be reviewed in the next milestone.

@azure-client-tools-bot-prd
Copy link
Copy Markdown

Validation for Breaking Change Starting...

Thanks for your contribution!

@yonzhan
Copy link
Copy Markdown
Collaborator

yonzhan commented Sep 25, 2025

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link
Copy Markdown

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a critical issue where bicep templates experience significant size inflation during Azure CLI deployment processing, causing deployments to fail when templates approach the 4MB ARM template size limit. The fix implements differential template handling: bicep templates use compact JSON strings to avoid size inflation, while ARM templates maintain existing logic for JsonC compatibility.

Key changes:

  • Optimized bicep template processing to prevent size inflation caused by string representation overhead
  • Added comprehensive test coverage with 8 unit tests and 2 scenario tests
  • Fixed CI/CD compatibility by adding appropriate @live_only() decorators to scenario tests

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/azure-cli/azure/cli/command_modules/resource/custom.py Core optimization logic implementing differential bicep vs ARM template handling
src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_custom.py Comprehensive test suite with unit tests and scenario tests using proper @live_only() decorators
src/azure-cli/azure/cli/command_modules/resource/tests/latest/recordings/*.yaml VCR recordings for automated CI/CD testing

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +928 to +930
with tempfile.NamedTemporaryFile(mode='w', suffix='.bicep', delete=False) as f:
f.write(bicep_content)
bicep_file_path = f.name
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is missing the import for tempfile module. Add import tempfile to the imports section at the top of the file.

Copilot uses AI. Check for mistakes.
Comment on lines +954 to +955
if os.path.exists(bicep_file_path):
os.unlink(bicep_file_path)
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is missing the import for os module. Add import os to the imports section at the top of the file.

Copilot uses AI. Check for mistakes.
mock_validate.return_value = None # No validation errors

# Act
template_content, template_obj = _process_template_file(cmd, "test.bicep", "resourceGroup")
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using an undefined variable cmd. Add a mock or fixture for the cmd parameter to prevent test failures.

Copilot uses AI. Check for mistakes.
mock_remove_comments.return_value = mock_template_obj

# Act
template_content, template_obj = _process_template_file(cmd, "test.json", "resourceGroup")
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using an undefined variable cmd. Add a mock or fixture for the cmd parameter to prevent test failures.

Copilot uses AI. Check for mistakes.
mock_get_template.return_value = compact_json # This is the key - bicep uses compact JSON string

# Act
properties = _prepare_deployment_properties_unmodified(cmd, "resourceGroup", "test.bicep", None, None, None, None)
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using an undefined variable cmd. Add a mock or fixture for the cmd parameter to prevent test failures.

Copilot uses AI. Check for mistakes.
mock_get_template.return_value = mock_template_content # ARM uses template_content

# Act
properties = _prepare_deployment_properties_unmodified(cmd, "resourceGroup", "test.json", None, None, None, None)
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using an undefined variable cmd. Add a mock or fixture for the cmd parameter to prevent test failures.

Copilot uses AI. Check for mistakes.
mock_urlretrieve.return_value = b'{"resources": []}'

# Act
properties = _prepare_deployment_properties_unmodified(cmd, "resourceGroup", None, "https://example.com/template.json", None, None, None)
Copy link

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using an undefined variable cmd. Add a mock or fixture for the cmd parameter to prevent test failures.

Copilot uses AI. Check for mistakes.
@vhvb1989 vhvb1989 closed this Sep 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants