From ed74c6105024c7046919401e242f33f2bdc7520a Mon Sep 17 00:00:00 2001 From: Praveen Matanam Date: Wed, 1 Oct 2025 21:25:25 +0530 Subject: [PATCH 1/7] Create copilot-instructions.md --- .github/copilot-instructions.md | 276 ++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..a83899b --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,276 @@ +# GitHub Copilot Instructions for IOS XR NAC Terraform Module + +## Project Overview + +This is a Terraform module for **Network as Code (NAC)** configuration of **Cisco IOS XR devices**. The module uses YAML-based configuration files to deploy network settings to IOS XR routers via the CiscoDevNet IOS XR Terraform provider. + +## Core Architecture Patterns + +### 1. Module Structure +- Each IOS XR feature has its own `.tf` file (e.g., `iosxr_router_ospf.tf`, `iosxr_bgp.tf`) +- Follow the naming pattern: `iosxr_.tf` +- Each module file contains both `locals` blocks and `resource` blocks + +### 2. Data Flow Pattern +``` +YAML Configuration → locals processing → Terraform resource → IOS XR device +``` + +### 3. Key Generation Pattern +**ALWAYS** use this pattern for resource keys: +```terraform +key = "${device.name}-${feature_specific_identifier}" +``` + +Examples: +- OSPF: `"${device.name}-${ospf_process.process_name}"` +- BGP: `"${device.name}-${bgp_process.as_number}"` +- Interface: `"${device.name}-${interface.name}"` + +## YAML Configuration Patterns + +### Device Structure +```yaml +iosxr: + devices: + - name: router-1 # Device identifier + host: 10.1.1.1:2627 # Device connection details + configuration: + feature_name: # IOS XR feature block + - item_config # List of feature instances +``` + +### Feature Configuration Types +1. **List-based features** (multiple instances per device): + - OSPF processes, BGP neighbors, Interfaces + - Use list structure: `feature_name: [...]` + +2. **Single-instance features** (one per device): + - Hostname, Domain settings, Global settings + - Use object structure: `feature_name: {...}` + +## Terraform Code Patterns + +### 1. List-Based Features (Multiple Instances) +For features like OSPF, BGP, interfaces that can have multiple instances per device: + +```terraform +locals { + device_ = flatten([ + for device in local.devices : [ + for in try(local.device_config[device.name]., []) : { + device_name = device.name + key = "${device.name}-${.}" + + # Map all attributes with defaults fallback + attribute_name = try(.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) + + # Handle nested lists/objects + nested_items = [ + for nested in try(.nested_items, local.defaults.iosxr.configuration..nested_items, []) : { + nested_attr = try(nested.nested_attr, local.defaults.iosxr.configuration..nested_attr, null) + } + ] + } + ] + if try(local.device_config[device.name]., null) != null + ]) +} +``` + +### 2. Single-Instance Features (Global Settings) +For features like hostname, domain, LACP that have one configuration per device: + +```terraform +resource "iosxr_" "" { + for_each = { + for device in local.devices : device.name => device + if try(local.device_config[device.name]., null) != null || + try(local.defaults.iosxr.configuration., null) != null + } + device = each.value.name + + # Direct attribute mapping without locals processing + attribute_name = try(local.device_config[each.value.name]..attribute_name, local.defaults.iosxr.configuration..attribute_name, null) +} +``` + +### 3. Resource Block Structure for List-Based Features +```terraform +resource "iosxr_" "" { + for_each = { for item in local.device_ : item.key => item } + + device = each.value.device_name + + # Map all attributes from locals + attribute_name = each.value.attribute_name + nested_items = each.value.nested_items +} +``` + +## Defaults Handling + +### Attribute-Level Defaults +Use nested object structure for defaults: +```terraform +# CORRECT +local.defaults.iosxr.configuration..attribute_name + +# INCORRECT +local.defaults.iosxr.configuration._attribute_name +``` + +### List-Level Defaults +**Do NOT support defaults for list items** - only support defaults at the attribute level within each list item. + +### Try Pattern +Always use this pattern for attribute mapping: +```terraform +attribute_name = try(item.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) +``` + +## Naming Conventions + +### Files and Resources +- File: `iosxr_.tf` +- Locals: `device_` +- Resource: `iosxr_.` + +### Variables and Attributes +- Use snake_case for all Terraform variables +- Match IOS XR provider attribute names exactly +- Use descriptive names that match IOS XR CLI commands + +### YAML Keys +- Use snake_case for consistency with Terraform +- Match IOS XR provider attribute names when possible +- Use clear, self-documenting names + +## Error Handling Patterns + +### Required Attributes +Use `try()` with appropriate fallbacks: +```terraform +# With default fallback +attribute = try(item.attribute, local.defaults.iosxr.configuration.feature.attribute, null) + +# Without default (optional attribute) +attribute = try(item.attribute, null) +``` + +### Conditional Resources +Always include existence check: +```terraform +if try(local.device_config[device.name]., null) != null +``` + +## Testing Patterns + +### Example Configurations +- Provide comprehensive examples in `examples/` directory +- Use realistic network scenarios +- Include both basic and advanced configurations + +### Validation +- Always test with `terraform plan` first +- Verify resource key generation is correct +- Test both apply and destroy operations +- Validate on actual IOS XR devices when possible + +## Code Quality Standards + +### Formatting +- Use `terraform fmt` for consistent formatting +- Align attribute assignments for readability +- Use consistent indentation (2 spaces) + +### Comments +- Document complex logic in locals blocks +- Explain non-obvious attribute mappings +- Add comments for IOS XR-specific behavior + +### Modularity +- Keep each feature in its own file +- Avoid cross-feature dependencies +- Make each feature independently configurable + +## Provider Patterns + +### Device Configuration +```terraform +provider "iosxr" { + alias = "device_name" +} +``` + +### Authentication +Use environment variables: +- `IOSXR_USERNAME` +- `IOSXR_PASSWORD` +- `IOSXR_HOST` +- `IOSXR_TLS` + +## Common IOS XR Feature Patterns + +### Process-Based Features (OSPF, BGP, ISIS) +- Always include `process_name` or equivalent identifier +- Support multiple processes per device +- Use process identifier in resource key + +### Interface-Based Features +- Use interface name as identifier +- Support interface hierarchies (physical, sub-interfaces) +- Handle interface-specific attributes + +### Global Features (Hostname, Domain) +- Single instance per device +- Use device name as identifier +- Simpler locals structure (no list iteration) + +## Performance Considerations + +### Resource Creation +- Use `for_each` instead of `count` for better resource tracking +- Generate stable resource keys for consistent state management +- Minimize resource churn during updates + +### State Management +- Ensure resource keys remain stable across configuration changes +- Use meaningful identifiers that won't change frequently +- Avoid complex computed keys when possible + +## Security Best Practices + +### Credentials +- Never hardcode credentials in configuration files +- Use environment variables or secure credential stores +- Support multiple authentication methods + +### Network Access +- Support TLS encryption when available +- Validate device certificates when possible +- Use secure protocols for device communication + +--- + +## Quick Reference + +### New Feature Checklist +- [ ] Create `iosxr_.tf` file +- [ ] Implement locals block with proper key generation +- [ ] Add resource block with for_each pattern +- [ ] Support defaults with nested object structure +- [ ] Add example configuration in `examples/` +- [ ] Test on actual IOS XR device +- [ ] Verify terraform fmt compliance +- [ ] Document any feature-specific patterns + +### Key Generation Formula +``` +"${device.name}-${unique_identifier_from_config}" +``` + +### Defaults Pattern +``` +try(item.attr, local.defaults.iosxr.configuration.feature.attr, null) +``` \ No newline at end of file From e9946cd9d05f45ca49e49efc11ae59b068f1f371 Mon Sep 17 00:00:00 2001 From: Praveen Matanam Date: Wed, 1 Oct 2025 21:28:07 +0530 Subject: [PATCH 2/7] Update copilot-instructions.md --- .github/copilot-instructions.md | 80 ++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index a83899b..554a9ea 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -42,17 +42,22 @@ iosxr: ### Feature Configuration Types 1. **List-based features** (multiple instances per device): - - OSPF processes, BGP neighbors, Interfaces + - Simple lists: OSPF processes, Banner configurations + - Complex nested lists: Key chains (with nested keys), Interfaces (with sub-interfaces) - Use list structure: `feature_name: [...]` 2. **Single-instance features** (one per device): - - Hostname, Domain settings, Global settings + - Global settings: Hostname, LACP, Logging, Domain - Use object structure: `feature_name: {...}` +3. **Hybrid features**: + - NTP: Can have multiple servers but single global config + - Uses mixed approach with both list and object patterns + ## Terraform Code Patterns ### 1. List-Based Features (Multiple Instances) -For features like OSPF, BGP, interfaces that can have multiple instances per device: +For features like OSPF, BGP, banner, key_chain that can have multiple instances per device: ```terraform locals { @@ -65,10 +70,10 @@ locals { # Map all attributes with defaults fallback attribute_name = try(.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) - # Handle nested lists/objects + # Handle nested lists/objects (e.g., key_chain keys) nested_items = [ - for nested in try(.nested_items, local.defaults.iosxr.configuration..nested_items, []) : { - nested_attr = try(nested.nested_attr, local.defaults.iosxr.configuration..nested_attr, null) + for nested in try(.nested_items, []) : { + nested_attr = try(nested.nested_attr, local.defaults.iosxr.configuration..nested_items.nested_attr, null) } ] } @@ -78,8 +83,13 @@ locals { } ``` +**Examples from actual codebase:** +- **Banner**: `"${device.name}-${banner.banner_type}"` +- **Key Chain**: `"${device.name}-keychain-${key_chain.name}"` +- **OSPF**: `"${device.name}-${ospf_process.process_name}"` + ### 2. Single-Instance Features (Global Settings) -For features like hostname, domain, LACP that have one configuration per device: +For features like hostname, domain, LACP, logging that have one configuration per device: ```terraform resource "iosxr_" "" { @@ -95,6 +105,11 @@ resource "iosxr_" "" { } ``` +**Examples from actual codebase:** +- **Hostname**: Single string value per device +- **Logging**: Multiple attributes but single instance per device +- **LACP**: System-wide settings, one configuration per device + ### 3. Resource Block Structure for List-Based Features ```terraform resource "iosxr_" "" { @@ -129,6 +144,30 @@ Always use this pattern for attribute mapping: attribute_name = try(item.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) ``` +### Inconsistent Patterns Found in Existing Code +Some older modules use inconsistent patterns that should be avoided: + +**❌ AVOID: Flat defaults structure (found in interface.tf)** +```terraform +# OLD PATTERN - Don't use +local.defaults.iosxr.configuration.interface_mtu +``` + +**❌ AVOID: Mixed defaults patterns (found in banner.tf)** +```terraform +# INCONSISTENT PATTERN - Don't replicate +try(banner.banner_type, local.defaults.iosxr.configuration.banner_type, null) +try(local.device_config[device.name].banner, local.defaults.iosxr.configuration.banner, []) +``` + +**✅ USE: Nested defaults structure** +```terraform +# NEW PATTERN - Use this +local.defaults.iosxr.configuration.interface.mtu +local.defaults.iosxr.configuration.banner.banner_type +local.defaults.iosxr.configuration.key_chain.keys.key_name +``` + ## Naming Conventions ### Files and Resources @@ -177,6 +216,33 @@ if try(local.device_config[device.name]., null) != null - Test both apply and destroy operations - Validate on actual IOS XR devices when possible +## Advanced Configuration Patterns + +### Interface Groups and Complex Merging +Some modules support complex configuration merging with YAML references: + +```terraform +# Example from iosxr_interface.tf +interface_groups = try(local.device_config[device.name].interface_groups, {}) +merged_config = try(local.device_config[device.name].interfaces[each.key], yamldecode(yaml_merge( + yamlencode(local.interface_groups[each.value.interface_group]), + yamlencode(local.device_config[device.name].interfaces[each.key]) +)), {}) +``` + +This pattern allows: +- Interface group templates with shared configurations +- Per-interface overrides that merge with group settings +- Complex YAML structure handling with `yamldecode` and `yaml_merge` + +### Pre-commit Hook Integration +The project uses automated code quality checks: +- `terraform fmt`: Automatic formatting +- `tflint`: Linting and validation +- `terraform-docs`: Documentation generation + +Ensure all modules comply with these standards. + ## Code Quality Standards ### Formatting From 48a5ac228bd96a19ebb8ae3c775adb403be07bb3 Mon Sep 17 00:00:00 2001 From: Praveen Matanam Date: Wed, 1 Oct 2025 21:46:41 +0530 Subject: [PATCH 3/7] Update copilot-instructions.md --- .github/copilot-instructions.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 554a9ea..c8630a8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -317,6 +317,23 @@ Use environment variables: - Validate device certificates when possible - Use secure protocols for device communication +## Continuous Learning Protocol + +### Documentation Evolution +- **Always update this file immediately** when discovering new patterns, issues, or solutions +- Document workarounds for provider limitations or IOS XR quirks +- Record debugging techniques that prove effective +- Note performance optimizations discovered during testing +- Capture configuration patterns that work well in production + +### Learning Categories to Track +1. **Provider Behavior**: Undocumented IOS XR provider behaviors or limitations +2. **Device Quirks**: IOS XR device-specific configuration requirements +3. **Performance Patterns**: Terraform resource management optimizations +4. **Debugging Techniques**: Effective troubleshooting approaches +5. **Configuration Patterns**: YAML structure patterns that work well +6. **Testing Insights**: Validation approaches for complex configurations + --- ## Quick Reference @@ -330,6 +347,7 @@ Use environment variables: - [ ] Test on actual IOS XR device - [ ] Verify terraform fmt compliance - [ ] Document any feature-specific patterns +- [ ] **Update copilot-instructions.md with new learnings** ### Key Generation Formula ``` From f222279421122456caa0def2e87457b0f841546a Mon Sep 17 00:00:00 2001 From: Praveen Matanam Date: Mon, 6 Oct 2025 10:36:45 +0530 Subject: [PATCH 4/7] Delete copilot-instructions.md --- .github/copilot-instructions.md | 360 -------------------------------- 1 file changed, 360 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index c8630a8..0000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,360 +0,0 @@ -# GitHub Copilot Instructions for IOS XR NAC Terraform Module - -## Project Overview - -This is a Terraform module for **Network as Code (NAC)** configuration of **Cisco IOS XR devices**. The module uses YAML-based configuration files to deploy network settings to IOS XR routers via the CiscoDevNet IOS XR Terraform provider. - -## Core Architecture Patterns - -### 1. Module Structure -- Each IOS XR feature has its own `.tf` file (e.g., `iosxr_router_ospf.tf`, `iosxr_bgp.tf`) -- Follow the naming pattern: `iosxr_.tf` -- Each module file contains both `locals` blocks and `resource` blocks - -### 2. Data Flow Pattern -``` -YAML Configuration → locals processing → Terraform resource → IOS XR device -``` - -### 3. Key Generation Pattern -**ALWAYS** use this pattern for resource keys: -```terraform -key = "${device.name}-${feature_specific_identifier}" -``` - -Examples: -- OSPF: `"${device.name}-${ospf_process.process_name}"` -- BGP: `"${device.name}-${bgp_process.as_number}"` -- Interface: `"${device.name}-${interface.name}"` - -## YAML Configuration Patterns - -### Device Structure -```yaml -iosxr: - devices: - - name: router-1 # Device identifier - host: 10.1.1.1:2627 # Device connection details - configuration: - feature_name: # IOS XR feature block - - item_config # List of feature instances -``` - -### Feature Configuration Types -1. **List-based features** (multiple instances per device): - - Simple lists: OSPF processes, Banner configurations - - Complex nested lists: Key chains (with nested keys), Interfaces (with sub-interfaces) - - Use list structure: `feature_name: [...]` - -2. **Single-instance features** (one per device): - - Global settings: Hostname, LACP, Logging, Domain - - Use object structure: `feature_name: {...}` - -3. **Hybrid features**: - - NTP: Can have multiple servers but single global config - - Uses mixed approach with both list and object patterns - -## Terraform Code Patterns - -### 1. List-Based Features (Multiple Instances) -For features like OSPF, BGP, banner, key_chain that can have multiple instances per device: - -```terraform -locals { - device_ = flatten([ - for device in local.devices : [ - for in try(local.device_config[device.name]., []) : { - device_name = device.name - key = "${device.name}-${.}" - - # Map all attributes with defaults fallback - attribute_name = try(.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) - - # Handle nested lists/objects (e.g., key_chain keys) - nested_items = [ - for nested in try(.nested_items, []) : { - nested_attr = try(nested.nested_attr, local.defaults.iosxr.configuration..nested_items.nested_attr, null) - } - ] - } - ] - if try(local.device_config[device.name]., null) != null - ]) -} -``` - -**Examples from actual codebase:** -- **Banner**: `"${device.name}-${banner.banner_type}"` -- **Key Chain**: `"${device.name}-keychain-${key_chain.name}"` -- **OSPF**: `"${device.name}-${ospf_process.process_name}"` - -### 2. Single-Instance Features (Global Settings) -For features like hostname, domain, LACP, logging that have one configuration per device: - -```terraform -resource "iosxr_" "" { - for_each = { - for device in local.devices : device.name => device - if try(local.device_config[device.name]., null) != null || - try(local.defaults.iosxr.configuration., null) != null - } - device = each.value.name - - # Direct attribute mapping without locals processing - attribute_name = try(local.device_config[each.value.name]..attribute_name, local.defaults.iosxr.configuration..attribute_name, null) -} -``` - -**Examples from actual codebase:** -- **Hostname**: Single string value per device -- **Logging**: Multiple attributes but single instance per device -- **LACP**: System-wide settings, one configuration per device - -### 3. Resource Block Structure for List-Based Features -```terraform -resource "iosxr_" "" { - for_each = { for item in local.device_ : item.key => item } - - device = each.value.device_name - - # Map all attributes from locals - attribute_name = each.value.attribute_name - nested_items = each.value.nested_items -} -``` - -## Defaults Handling - -### Attribute-Level Defaults -Use nested object structure for defaults: -```terraform -# CORRECT -local.defaults.iosxr.configuration..attribute_name - -# INCORRECT -local.defaults.iosxr.configuration._attribute_name -``` - -### List-Level Defaults -**Do NOT support defaults for list items** - only support defaults at the attribute level within each list item. - -### Try Pattern -Always use this pattern for attribute mapping: -```terraform -attribute_name = try(item.attribute_name, local.defaults.iosxr.configuration..attribute_name, null) -``` - -### Inconsistent Patterns Found in Existing Code -Some older modules use inconsistent patterns that should be avoided: - -**❌ AVOID: Flat defaults structure (found in interface.tf)** -```terraform -# OLD PATTERN - Don't use -local.defaults.iosxr.configuration.interface_mtu -``` - -**❌ AVOID: Mixed defaults patterns (found in banner.tf)** -```terraform -# INCONSISTENT PATTERN - Don't replicate -try(banner.banner_type, local.defaults.iosxr.configuration.banner_type, null) -try(local.device_config[device.name].banner, local.defaults.iosxr.configuration.banner, []) -``` - -**✅ USE: Nested defaults structure** -```terraform -# NEW PATTERN - Use this -local.defaults.iosxr.configuration.interface.mtu -local.defaults.iosxr.configuration.banner.banner_type -local.defaults.iosxr.configuration.key_chain.keys.key_name -``` - -## Naming Conventions - -### Files and Resources -- File: `iosxr_.tf` -- Locals: `device_` -- Resource: `iosxr_.` - -### Variables and Attributes -- Use snake_case for all Terraform variables -- Match IOS XR provider attribute names exactly -- Use descriptive names that match IOS XR CLI commands - -### YAML Keys -- Use snake_case for consistency with Terraform -- Match IOS XR provider attribute names when possible -- Use clear, self-documenting names - -## Error Handling Patterns - -### Required Attributes -Use `try()` with appropriate fallbacks: -```terraform -# With default fallback -attribute = try(item.attribute, local.defaults.iosxr.configuration.feature.attribute, null) - -# Without default (optional attribute) -attribute = try(item.attribute, null) -``` - -### Conditional Resources -Always include existence check: -```terraform -if try(local.device_config[device.name]., null) != null -``` - -## Testing Patterns - -### Example Configurations -- Provide comprehensive examples in `examples/` directory -- Use realistic network scenarios -- Include both basic and advanced configurations - -### Validation -- Always test with `terraform plan` first -- Verify resource key generation is correct -- Test both apply and destroy operations -- Validate on actual IOS XR devices when possible - -## Advanced Configuration Patterns - -### Interface Groups and Complex Merging -Some modules support complex configuration merging with YAML references: - -```terraform -# Example from iosxr_interface.tf -interface_groups = try(local.device_config[device.name].interface_groups, {}) -merged_config = try(local.device_config[device.name].interfaces[each.key], yamldecode(yaml_merge( - yamlencode(local.interface_groups[each.value.interface_group]), - yamlencode(local.device_config[device.name].interfaces[each.key]) -)), {}) -``` - -This pattern allows: -- Interface group templates with shared configurations -- Per-interface overrides that merge with group settings -- Complex YAML structure handling with `yamldecode` and `yaml_merge` - -### Pre-commit Hook Integration -The project uses automated code quality checks: -- `terraform fmt`: Automatic formatting -- `tflint`: Linting and validation -- `terraform-docs`: Documentation generation - -Ensure all modules comply with these standards. - -## Code Quality Standards - -### Formatting -- Use `terraform fmt` for consistent formatting -- Align attribute assignments for readability -- Use consistent indentation (2 spaces) - -### Comments -- Document complex logic in locals blocks -- Explain non-obvious attribute mappings -- Add comments for IOS XR-specific behavior - -### Modularity -- Keep each feature in its own file -- Avoid cross-feature dependencies -- Make each feature independently configurable - -## Provider Patterns - -### Device Configuration -```terraform -provider "iosxr" { - alias = "device_name" -} -``` - -### Authentication -Use environment variables: -- `IOSXR_USERNAME` -- `IOSXR_PASSWORD` -- `IOSXR_HOST` -- `IOSXR_TLS` - -## Common IOS XR Feature Patterns - -### Process-Based Features (OSPF, BGP, ISIS) -- Always include `process_name` or equivalent identifier -- Support multiple processes per device -- Use process identifier in resource key - -### Interface-Based Features -- Use interface name as identifier -- Support interface hierarchies (physical, sub-interfaces) -- Handle interface-specific attributes - -### Global Features (Hostname, Domain) -- Single instance per device -- Use device name as identifier -- Simpler locals structure (no list iteration) - -## Performance Considerations - -### Resource Creation -- Use `for_each` instead of `count` for better resource tracking -- Generate stable resource keys for consistent state management -- Minimize resource churn during updates - -### State Management -- Ensure resource keys remain stable across configuration changes -- Use meaningful identifiers that won't change frequently -- Avoid complex computed keys when possible - -## Security Best Practices - -### Credentials -- Never hardcode credentials in configuration files -- Use environment variables or secure credential stores -- Support multiple authentication methods - -### Network Access -- Support TLS encryption when available -- Validate device certificates when possible -- Use secure protocols for device communication - -## Continuous Learning Protocol - -### Documentation Evolution -- **Always update this file immediately** when discovering new patterns, issues, or solutions -- Document workarounds for provider limitations or IOS XR quirks -- Record debugging techniques that prove effective -- Note performance optimizations discovered during testing -- Capture configuration patterns that work well in production - -### Learning Categories to Track -1. **Provider Behavior**: Undocumented IOS XR provider behaviors or limitations -2. **Device Quirks**: IOS XR device-specific configuration requirements -3. **Performance Patterns**: Terraform resource management optimizations -4. **Debugging Techniques**: Effective troubleshooting approaches -5. **Configuration Patterns**: YAML structure patterns that work well -6. **Testing Insights**: Validation approaches for complex configurations - ---- - -## Quick Reference - -### New Feature Checklist -- [ ] Create `iosxr_.tf` file -- [ ] Implement locals block with proper key generation -- [ ] Add resource block with for_each pattern -- [ ] Support defaults with nested object structure -- [ ] Add example configuration in `examples/` -- [ ] Test on actual IOS XR device -- [ ] Verify terraform fmt compliance -- [ ] Document any feature-specific patterns -- [ ] **Update copilot-instructions.md with new learnings** - -### Key Generation Formula -``` -"${device.name}-${unique_identifier_from_config}" -``` - -### Defaults Pattern -``` -try(item.attr, local.defaults.iosxr.configuration.feature.attr, null) -``` \ No newline at end of file From 6ca07bf2991d3c03b0d0cc339a3ab6d64dd35b47 Mon Sep 17 00:00:00 2001 From: SCHANDAN20 Date: Wed, 15 Oct 2025 17:08:29 +0530 Subject: [PATCH 5/7] added iosxr_evpn_interface features --- README.md | 1 + iosxr_evpn_interface.tf | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 iosxr_evpn_interface.tf diff --git a/README.md b/README.md index eac7180..ef09302 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ module "iosxr" { | [iosxr_domain.domain](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/domain) | resource | | [iosxr_domain_vrf.domain_vrf](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/domain_vrf) | resource | | [iosxr_evpn.evpn](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/evpn) | resource | +| [iosxr_evpn_interface.evpn_interface](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/evpn_interface) | resource | | [iosxr_hostname.hostname](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/hostname) | resource | | [iosxr_interface.interface](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/interface) | resource | | [iosxr_ipv4_prefix_list.ipv4_prefix_list](https://registry.terraform.io/providers/CiscoDevNet/iosxr/latest/docs/resources/ipv4_prefix_list) | resource | diff --git a/iosxr_evpn_interface.tf b/iosxr_evpn_interface.tf new file mode 100644 index 0000000..b3d10e8 --- /dev/null +++ b/iosxr_evpn_interface.tf @@ -0,0 +1,42 @@ +locals { + device_evpn_interfaces = flatten([ + for device in local.devices : [ + for evpn_interface in try(local.device_config[device.name].evpn_interface, []) : { + device_name = device.name + interface_name = evpn_interface.interface_name + key = "${device.name}-${evpn_interface.interface_name}" + + core_isolation_group = try(evpn_interface.core_isolation_group, local.defaults.iosxr.configuration.evpn_interface_core_isolation_group, null) + ethernet_segment_identifier_type_zero_bytes_1 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_1, null) + ethernet_segment_identifier_type_zero_bytes_23 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_23, null) + ethernet_segment_identifier_type_zero_bytes_45 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_45, null) + ethernet_segment_identifier_type_zero_bytes_67 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_67, null) + ethernet_segment_identifier_type_zero_bytes_89 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_89, null) + ethernet_segment_identifier_type_zero_esi = try(evpn_interface.ethernet_segment_identifier_type_zero_esi, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_esi, null) + ethernet_segment_load_balancing_mode_all_active = try(evpn_interface.ethernet_segment_load_balancing_mode_all_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_all_active, null) + ethernet_segment_load_balancing_mode_port_active = try(evpn_interface.ethernet_segment_load_balancing_mode_port_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_port_active, null) + ethernet_segment_load_balancing_mode_single_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_single_active, null) + ethernet_segment_load_balancing_mode_single_flow_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_flow_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_single_flow_active, null) + } + ] + ]) +} + +resource "iosxr_evpn_interface" "evpn_interface" { + for_each = { for evpn_interface in local.device_evpn_interfaces : evpn_interface.key => evpn_interface } + + device = each.value.device_name + interface_name = each.value.interface_name + + core_isolation_group = each.value.core_isolation_group + ethernet_segment_identifier_type_zero_bytes_1 = each.value.ethernet_segment_identifier_type_zero_bytes_1 + ethernet_segment_identifier_type_zero_bytes_23 = each.value.ethernet_segment_identifier_type_zero_bytes_23 + ethernet_segment_identifier_type_zero_bytes_45 = each.value.ethernet_segment_identifier_type_zero_bytes_45 + ethernet_segment_identifier_type_zero_bytes_67 = each.value.ethernet_segment_identifier_type_zero_bytes_67 + ethernet_segment_identifier_type_zero_bytes_89 = each.value.ethernet_segment_identifier_type_zero_bytes_89 + ethernet_segment_identifier_type_zero_esi = each.value.ethernet_segment_identifier_type_zero_esi + ethernet_segment_load_balancing_mode_all_active = each.value.ethernet_segment_load_balancing_mode_all_active + ethernet_segment_load_balancing_mode_port_active = each.value.ethernet_segment_load_balancing_mode_port_active + ethernet_segment_load_balancing_mode_single_active = each.value.ethernet_segment_load_balancing_mode_single_active + ethernet_segment_load_balancing_mode_single_flow_active = each.value.ethernet_segment_load_balancing_mode_single_flow_active +} From fe6363d01ff2e13b9b104aaeed795279aadcdc2f Mon Sep 17 00:00:00 2001 From: SCHANDAN20 Date: Thu, 16 Oct 2025 19:42:02 +0530 Subject: [PATCH 6/7] corrected the Key Generation Pattern rectified the pattern for resource keys: ```terraform key = "${device.name}-${feature_specific_identifier}" ``` --- iosxr_evpn_interface.tf | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/iosxr_evpn_interface.tf b/iosxr_evpn_interface.tf index b3d10e8..f79d156 100644 --- a/iosxr_evpn_interface.tf +++ b/iosxr_evpn_interface.tf @@ -6,17 +6,17 @@ locals { interface_name = evpn_interface.interface_name key = "${device.name}-${evpn_interface.interface_name}" - core_isolation_group = try(evpn_interface.core_isolation_group, local.defaults.iosxr.configuration.evpn_interface_core_isolation_group, null) - ethernet_segment_identifier_type_zero_bytes_1 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_1, null) - ethernet_segment_identifier_type_zero_bytes_23 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_23, null) - ethernet_segment_identifier_type_zero_bytes_45 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_45, null) - ethernet_segment_identifier_type_zero_bytes_67 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_67, null) - ethernet_segment_identifier_type_zero_bytes_89 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_bytes_89, null) - ethernet_segment_identifier_type_zero_esi = try(evpn_interface.ethernet_segment_identifier_type_zero_esi, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_identifier_type_zero_esi, null) - ethernet_segment_load_balancing_mode_all_active = try(evpn_interface.ethernet_segment_load_balancing_mode_all_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_all_active, null) - ethernet_segment_load_balancing_mode_port_active = try(evpn_interface.ethernet_segment_load_balancing_mode_port_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_port_active, null) - ethernet_segment_load_balancing_mode_single_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_single_active, null) - ethernet_segment_load_balancing_mode_single_flow_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_flow_active, local.defaults.iosxr.configuration.evpn_interface_ethernet_segment_load_balancing_mode_single_flow_active, null) + core_isolation_group = try(evpn_interface.core_isolation_group, local.defaults.iosxr.configuration.evpn_interface.core_isolation_group, null) + ethernet_segment_identifier_type_zero_bytes_1 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, null) + ethernet_segment_identifier_type_zero_bytes_23 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, null) + ethernet_segment_identifier_type_zero_bytes_45 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, null) + ethernet_segment_identifier_type_zero_bytes_67 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, null) + ethernet_segment_identifier_type_zero_bytes_89 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, null) + ethernet_segment_identifier_type_zero_esi = try(evpn_interface.ethernet_segment_identifier_type_zero_esi, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_esi, null) + ethernet_segment_load_balancing_mode_all_active = try(evpn_interface.ethernet_segment_load_balancing_mode_all_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_all_active, null) + ethernet_segment_load_balancing_mode_port_active = try(evpn_interface.ethernet_segment_load_balancing_mode_port_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_port_active, null) + ethernet_segment_load_balancing_mode_single_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_single_active, null) + ethernet_segment_load_balancing_mode_single_flow_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_flow_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_single_flow_active, null) } ] ]) From be7761ee3880fa0e00f8811988a64d90689f3783 Mon Sep 17 00:00:00 2001 From: SCHANDAN20 Date: Tue, 21 Oct 2025 16:25:06 +0530 Subject: [PATCH 7/7] =?UTF-8?q?applied=20=E2=80=9Cethernet=5Fsegment=5Fide?= =?UTF-8?q?ntifier=5Ftype=5Fzero=5Fesi=E2=80=9D=20to=20the=20evpn=5Finterf?= =?UTF-8?q?ace=20feature=20with=20the=20updated=20provider=20v0.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iosxr_evpn_interface.tf | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/iosxr_evpn_interface.tf b/iosxr_evpn_interface.tf index f79d156..12dc9ed 100644 --- a/iosxr_evpn_interface.tf +++ b/iosxr_evpn_interface.tf @@ -6,13 +6,11 @@ locals { interface_name = evpn_interface.interface_name key = "${device.name}-${evpn_interface.interface_name}" + # Required attribute (no default fallback needed for required fields) + ethernet_segment_identifier_type_zero_esi = evpn_interface.ethernet_segment_identifier_type_zero_esi + + # Optional attributes with defaults support core_isolation_group = try(evpn_interface.core_isolation_group, local.defaults.iosxr.configuration.evpn_interface.core_isolation_group, null) - ethernet_segment_identifier_type_zero_bytes_1 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_1, null) - ethernet_segment_identifier_type_zero_bytes_23 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_23, null) - ethernet_segment_identifier_type_zero_bytes_45 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_45, null) - ethernet_segment_identifier_type_zero_bytes_67 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_67, null) - ethernet_segment_identifier_type_zero_bytes_89 = try(evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_bytes_89, null) - ethernet_segment_identifier_type_zero_esi = try(evpn_interface.ethernet_segment_identifier_type_zero_esi, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_identifier_type_zero_esi, null) ethernet_segment_load_balancing_mode_all_active = try(evpn_interface.ethernet_segment_load_balancing_mode_all_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_all_active, null) ethernet_segment_load_balancing_mode_port_active = try(evpn_interface.ethernet_segment_load_balancing_mode_port_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_port_active, null) ethernet_segment_load_balancing_mode_single_active = try(evpn_interface.ethernet_segment_load_balancing_mode_single_active, local.defaults.iosxr.configuration.evpn_interface.ethernet_segment_load_balancing_mode_single_active, null) @@ -25,16 +23,11 @@ locals { resource "iosxr_evpn_interface" "evpn_interface" { for_each = { for evpn_interface in local.device_evpn_interfaces : evpn_interface.key => evpn_interface } - device = each.value.device_name - interface_name = each.value.interface_name + device = each.value.device_name + interface_name = each.value.interface_name + ethernet_segment_identifier_type_zero_esi = each.value.ethernet_segment_identifier_type_zero_esi core_isolation_group = each.value.core_isolation_group - ethernet_segment_identifier_type_zero_bytes_1 = each.value.ethernet_segment_identifier_type_zero_bytes_1 - ethernet_segment_identifier_type_zero_bytes_23 = each.value.ethernet_segment_identifier_type_zero_bytes_23 - ethernet_segment_identifier_type_zero_bytes_45 = each.value.ethernet_segment_identifier_type_zero_bytes_45 - ethernet_segment_identifier_type_zero_bytes_67 = each.value.ethernet_segment_identifier_type_zero_bytes_67 - ethernet_segment_identifier_type_zero_bytes_89 = each.value.ethernet_segment_identifier_type_zero_bytes_89 - ethernet_segment_identifier_type_zero_esi = each.value.ethernet_segment_identifier_type_zero_esi ethernet_segment_load_balancing_mode_all_active = each.value.ethernet_segment_load_balancing_mode_all_active ethernet_segment_load_balancing_mode_port_active = each.value.ethernet_segment_load_balancing_mode_port_active ethernet_segment_load_balancing_mode_single_active = each.value.ethernet_segment_load_balancing_mode_single_active