Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to associated contract with ESG without going into the resource for creating the ESG. #1157

Closed
JockeW-DvL opened this issue Feb 15, 2024 · 18 comments · Fixed by #1223
Assignees
Labels

Comments

@JockeW-DvL
Copy link

Community Note

  • Please vote on this issue by adding a 👍 reaction to the original issue to help the community and maintainers prioritize this request
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request
  • If you are interested in working on this issue or have submitted a pull request, please leave a comment

Description

I'm in need of a resource for associating contracts and ESG's the same way we can do with EPG's, i.e. the same as aci_epg_to_contract.

The reason behind this request is that i need a way of associating contracts to ESG's without beeing forced to rebuild/change the ESG for every run of terraform apply.

New or Affected Resource(s) + ACI Class(es):

  • aci_XXXXX + fv:XXXX

APIC version and APIC Platform

  • V x.x.x and on-prem/cloud-aws/cloud-azure/all.

Potential Terraform Configuration

# Copy-paste your Terraform configurations here - for large Terraform configs,
# please use a service like Dropbox and share a link to the ZIP file. For
# security, you can also encrypt the files using our GPG public key.

References

  • #0000
@akinross
Copy link
Collaborator

Hi @JockeW-DvL , thanks for raising this issue. I have added it to the todo items.

@akinross
Copy link
Collaborator

Classes that should be configured:

@akinross
Copy link
Collaborator

@lhercot, I similarities with #1023. Would this also apply for ESG?

@akinross
Copy link
Collaborator

akinross commented Mar 4, 2024

Hi @JockeW-DvL,

The aci_epg_to_contract resource would actually work for esg also. You should be able to do the following:

resource "aci_epg_to_contract" "example" {
  application_epg_dn = <ESG_DN>
  contract_dn        = aci_contract.example.id
  contract_type      = "provider"
  annotation         = "terraform"
  match_t            = "AtleastOne"
  prio               = "unspecified"
}

Please let me know if you face any issues.

@JockeW-DvL
Copy link
Author

JockeW-DvL commented Mar 5, 2024

It works the first apply run, but if i do a second apply run without changing any config all the contracts will be removed from the ESGs. if i do a third apply run the contracts will then be reapplyd again.

The output from Terraform on the second run is:

# module.esg.aci_endpoint_security_group.terraform_esg["ESG1503"] will be updated in-place
  ~ resource "aci_endpoint_security_group" "terraform_esg" {
        id                     = "uni/tn-prod/ap-prod-isolated/esg-prod-isolated_app-101123_dataparc-usmen"
        name                   = "prod-isolated_app-101123_dataparc-usmen"
        # (8 unchanged attributes hidden)

      - relation_fv_rs_prov {
          - match_t   = "AtleastOne" -> null
          - prio      = "unspecified" -> null
          - target_dn = "uni/tn-prod/brc-Allow_All" -> null
        }
      - relation_fv_rs_prov {
          - match_t   = "AtleastOne" -> null
          - prio      = "unspecified" -> null
          - target_dn = "uni/tn-prod/brc-LB_to_Dataparc_Usmen" -> null
        }
    }

Just one example of the contracts beeing removed.

@akinross
Copy link
Collaborator

akinross commented Mar 5, 2024

Hi @JockeW-DvL, just to get a better understanding, could you provide your configuration also? I assume you do not define any contract relationships in aci_endpoint_security_group.terraform_esg configuration.

@JockeW-DvL
Copy link
Author

JockeW-DvL commented Mar 5, 2024

Of course Akini, Here are the config i am using:

for building the ESGs:

resource "aci_endpoint_security_group" "terraform_esg" {
  for_each                             = var.esgs 

  application_profile_dn               = var.apps[each.value.network].id
  name                                 = join("-", [trimprefix(var.tenants[each.value.tenant].name,"tnt-"), lower(each.value.application)])
  description                          = join(" ", ["Endpoint Security Group for", each.value.application, "in Tenant", var.tenants[each.value.tenant].name])
  pc_enf_pref                          = "unenforced"
  pref_gr_memb                         = each.value.prefgr_member == "yes" ? "include" : "exclude"
  relation_fv_rs_scope                 = var.vrfs[each.value.vrf].id
  name_alias                           = each.key
}

And the indata for the above resource:

prod_isolated_esg            = {
  ESG1500                      = {
    tenant                       = "TEN2"
    vrf                          = "VRF7"
    network                      = "NETW9"
    tag_key                      = ""
    application                  = "isolated_default"
    epg                          = "NETW9"
    prefgr_member                = "no"
    infra_service                = ""
  }
  ESG1501                       = {
    tenant                       = "TEN2"
    vrf                          = "VRF7"
    network                      = "NETW9"
    tag_key                      = "TAG2"
    application                  = "isolated_APP-XXXXXX_tims"
    epg                          = ""
    prefgr_member                = "no"
    infra_service                = ""
  }
  ESG1502                       = {
    tenant                       = "TEN2"
    vrf                          = "VRF7"
    network                      = "NETW9"
    tag_key                      = "TAG2"
    application                  = "isolated_app-100557_clscl-msoft-lms"
    epg                          = ""
    prefgr_member                = "no"
    infra_service                = ""
  }
  ESG1503                       = {
    tenant                       = "TEN2"
    vrf                          = "VRF7"
    network                      = "NETW9"
    tag_key                      = "TAG2"
    application                  = "isolated_app-101123_dataparc-usmen"
    epg                          = ""
    prefgr_member                = "no"
    infra_service                = ""
  }
}

and the configuration for doing the mapping between ESG and contract:

resource "aci_epg_to_contract" "example" {
  for_each                    = var.esg_ctr_map
  
  application_epg_dn          = var.esgs[each.value.esg].id
  contract_dn                 = aci_contract.terraform_contract[each.value.contract].id
  contract_type               = each.value.ctr_type
}

And the indata for the above resourse:

esg_ctr_map	= {
  MAP1			= {
    esg 			= "ESG1500"
    contract		= "CTR1000"
    ctr_type		= "provider" 											
  }	
  MAP2                         = {
    esg                          = "ESG1500"
    contract                     = "CTR1001"
    ctr_type                     = "consumer"                       
  } 
  MAP3                         = {
    esg                          = "ESG1500"
    contract                     = "CTR1002"
    ctr_type                     = "consumer"                       
  } 
  MAP4                         = {
    esg                          = "ESG1501"
    contract                     = "CTR1000"
    ctr_type                     = "provider"                       
  } 
  MAP5                         = {
    esg                          = "ESG1502"
    contract                     = "CTR1000"
    ctr_type                     = "provider"                       
  } 
  MAP6                         = {
    esg                          = "ESG1502"
    contract                     = "CTR1002"
    ctr_type                     = "provider"                       
  } 
  MAP7                         = {
    esg                          = "ESG1503"
    contract                     = "CTR1000"
    ctr_type                     = "provider"                       
  } 
  MAP8                         = {
    esg                          = "ESG1503"
    contract                     = "CTR1001"
    ctr_type                     = "provider"                       
  } 
}

@akinross
Copy link
Collaborator

akinross commented Mar 5, 2024

Hi @JockeW-DvL,

I had a quick look today to see if I could find a quick solution to specify it in the ESG resource. Is there a reason that you are specifying the objects outside the resource, because I think this might solve your problems until we make changes. See example below and let me know if that could help you change your TF configuration logic a bit.

resource "aci_tenant" "test" {
  name = "abr_contract"
}

resource "aci_vrf" "test" {
  name      = "abr_contract_vrf"
  tenant_dn = aci_tenant.test.id
}

resource "aci_application_profile" "test" {
  name      = "abr_contract_app"
  tenant_dn = aci_tenant.test.id
}

variable "contracts" {
  type    = list(string)
  default = ["abr_contract_contract_1", "abr_contract_contract_2", "abr_contract_contract_3"]
}

resource "aci_contract" "test" {
  for_each  = toset(var.contracts)
  name      = each.value
  tenant_dn = aci_tenant.test.id
}

variable "esg_to_contracts" {
  type = map(object({
    esg           = string
    contract      = string
    contract_type = string
  }))
  default = {
    1 = {
      esg           = "abr_contract_esg"
      contract      = "abr_contract_contract_1"
      contract_type = "provider"
    }
    2 = {
      esg           = "abr_contract_esg"
      contract      = "abr_contract_contract_1"
      contract_type = "consumer"
    }
    3 = {
      esg           = "abr_contract_esg"
      contract      = "abr_contract_contract_2"
      contract_type = "provider"
    }
    4 = {
      esg           = "abr_contract_esg_2"
      contract      = "abr_contract_contract_3"
      contract_type = "consumer"
    }
    5 = {
      esg           = "abr_contract_esg"
      contract      = "abr_contract_contract_2"
      contract_type = "consumer"
    }
  }
}

resource "aci_endpoint_security_group" "terraform_esg" {
  application_profile_dn = aci_application_profile.test.id
  name                   = "abr_contract_esg"
  relation_fv_rs_scope   = aci_vrf.test.id
  dynamic "relation_fv_rs_prov" {
    for_each = {for k, v in var.esg_to_contracts : k => v if v.esg == "abr_contract_esg" && v.contract_type == "provider"}
    content {
        match_t = "AtleastOne"
        prio = "unspecified"
        target_dn = aci_contract.test[relation_fv_rs_prov.value.contract].id
    }
  }
  dynamic "relation_fv_rs_cons" {
    for_each = {for k, v in var.esg_to_contracts : k => v if v.esg == "abr_contract_esg" && v.contract_type == "consumer"}
    content {
        prio = "unspecified"
        target_dn = aci_contract.test[relation_fv_rs_cons.value.contract].id
    }
  }
}

@JockeW-DvL
Copy link
Author

Hello Akini,

If you look at my resourse definition i'm using for_each for the whole resource, and the reason behind this is that we have around 150-200 different ESGs that need to be created.
And you can't have 2 for_each in the same resource, nested for_each as it's called.

The reason i want to have the mapping outside of the resource is 2-fold:
1: I don't want to do any changes to the ESG to add or remove a contract, just have the mapping between Contract and ESG removed
2: we will be adding multiple contracts in the near future and i want to keep the config files simple, i.e. just have a mapping between Contract and ESG change.

@akinross
Copy link
Collaborator

akinross commented Mar 6, 2024

Hi @JockeW-DvL,

Ok thanks for the additional information, this is currently indeed not possible. The resource assumes that you define all the relationships to the ESG whenever you manage an ESG.

As a workaround you could define the ESG in another terraform configuration. Thus have a configuration that has datasource for ESG and then the relations defined, this way the changes made in relationship are not impacting the a resource that is defined in the same configuration. The problem would still occur that when there is a apply of configuration where the ESG is defined, but would be limited. Another option would be to adjust the data structure of you input. Both solutions are not ideal I would say.

We are working on a change for this behaviour, but this would also mean changes to the behaviour of the ESG resource where we need to detect None is provided in the relationship attribute. This is only possible starting the new terraform-plugin-framework, thus will depend on the migration of our resources to this new way.

@JockeW-DvL
Copy link
Author

I can try and have the resources for creating the ESGs and the association of the Contract and ESG in separate workspaces and report back if this works as intended.

@akinross
Copy link
Collaborator

akinross commented Mar 6, 2024

Hi @JockeW-DvL,

I was thinking a bit more about your problem and think the best way would probably be to define a local module. This way you bypass the restriction of the nested for_each. Also this way you would not have the situation you were describing whenever there is a apply needed in the code where ESG is specified.

I have tested and created some example code that I can share with you over email but probably best if we set up a call for this to walk through it since it is multiple files. Could you send me an email so we can set something up?

@JockeW-DvL
Copy link
Author

JockeW-DvL commented Mar 6, 2024 via email

@akinross akinross added bug and removed enhancement labels Mar 6, 2024
@akinross
Copy link
Collaborator

akinross commented Mar 6, 2024

Hi @JockeW-DvL, I was running some additional checks and noticed that the behaviour is also not matching the behaviour as we have in the EPG resource. I have labeled it as a bug now and we will be looking into a fix. Please follow the issue for PR updates and leverage the module workaround for now in case you need an immediate fix. Apologies for the inconvenience.

@akinross akinross self-assigned this Mar 7, 2024
@akinross
Copy link
Collaborator

Hi @JockeW-DvL, so looking at the code and running several tests drew me to the following conclusion. We need to migrate the resource into plugin framework in order for this to be fixed. The difference between EPG and ESG modules is that the ESG module exposes a list of maps, where the EPG module exposes a list of strings. In SDKv2 (framework to develop resources) the list of strings is written to state as Null when not provided, however for the list of maps it is written to a empty list. This difference in attribute handling makes it impossible for us to properly detect that a attribute is not provided and thus should not be touched. So for now the only way around this is the module workaround mentioned above.

@akinross akinross removed their assignment Mar 11, 2024
@JockeW-DvL
Copy link
Author

JockeW-DvL commented Mar 14, 2024 via email

@akinross
Copy link
Collaborator

akinross commented Mar 14, 2024

I have some time now if you are available, can you send me an email directly so we can schedule something?

@JockeW-DvL
Copy link
Author

JockeW-DvL commented Mar 14, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants