CiscoDevNet / terraform-provider-mso

Terraform Cisco MSO provider
https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs
Mozilla Public License 2.0
10 stars 32 forks source link

Provider forcing you to provide optional VRF attributes for the mso_schema_template_anp_epg resource #111

Closed mike-guy closed 2 years ago

mike-guy commented 2 years ago

Community Note

Terraform Version

terraform version Terraform v1.1.4 on windows_amd64

provider registry.terraform.io/ciscodevnet/mso v0.4.1

MSO version

Nexus Dashboard Version 2.0(2g)

Multi-Site Orchestrator Version: 3.3(1e)

dCloud Cisco Nexus Dashboard Orchestrator for ACI Lab v1

APIC version and APIC Platform for Site Level Resources

N/A

Affected Resource(s)

Terraform Configuration Files

terraform {
  required_providers {
    mso = {
      source  = "CiscoDevNet/mso"
      version = "0.4.1"
    }
  }
}

provider "mso" {
  # Creds provided as environment variables
}

resource "mso_tenant" "tenant" {
  name         = "test_tenant"
  display_name = "test_tenant"
}

resource "mso_schema" "schema" {
  name          = "test_schema"
  tenant_id     = mso_tenant.tenant.id
  template_name = "test_template"
}

resource "mso_schema_template_vrf" "vrf" {
  schema_id    = mso_schema.schema.id
  name         = "test_vrf"
  display_name = "test_ vrf"
  template     = mso_schema.schema.template_name
}

resource "mso_schema_template_bd" "bd" {
  schema_id     = mso_schema.schema.id
  name          = "test_bd"
  display_name  = "test_ bd"
  template_name = mso_schema.schema.template_name
  vrf_name      = mso_schema_template_vrf.vrf.name
}

resource "mso_schema_template_anp" "ap" {
  schema_id    = mso_schema.schema.id
  template     = mso_schema.schema.template_name
  name         = "test_ap"
  display_name = "test_ap"
}

resource "mso_schema_template_anp_epg" "epg" {
  schema_id         = mso_schema.schema.id
  template_name     = mso_schema.schema.template_name
  anp_name          = mso_schema_template_anp.ap.name
  name              = "terraform_test_epg"
  display_name      = "terraform_test_epg"
  bd_name           = mso_schema_template_bd.bd.name
  bd_schema_id      = mso_schema.schema.id
  bd_template_name  = mso_schema.schema.template_name
  # vrf_name          = mso_schema_template_vrf.vrf.name
  # vrf_schema_id     = mso_schema.schema.id
  # vrf_template_name = mso_schema.schema.template_name
}

Expected Behavior

It is expected that vrf_name, vrf_schema_id and vrf_template_name are optional per the documentation. The documentations tates these are only required for "cloud sites"...

https://registry.terraform.io/providers/CiscoDevNet/mso/latest/docs/resources/schema_template_anp_epg

vrf_name - (Optional) Name of Vrf. It is required when using cloud sites.

Actual Behaviour

Terraform generates an error on apply. Whilst the description does not reference the VRF attributes, the error goes when these are included:

ā”‚ Error: "Bad Request: incomplete ref: /6216564c2e0000650270b5dc/test_template. If non-empty, must provide policy name"{}
ā”‚
ā”‚   with mso_schema_template_anp_epg.epg,
ā”‚   on main.tf line 49, in resource "mso_schema_template_anp_epg" "epg":
ā”‚   49: resource "mso_schema_template_anp_epg" "epg" {
ā”‚
ā•µ

Steps to Reproduce

Apply the provided config. To make it work, the comments must be removed to include the values.

Important Factoids

The documentation stating these are optional include to be backed up by the configuration that is generated when manually creating in the MSO GUI.

Shown below is JSON taken from the MSO API for an EPG created in the GUI (no reference to the VRF values) and a resource created via Terraform (which forces you to include them):

GUI created EPG

{
  "name": "gui_test_epg",
  "displayName": "gui_test_epg",
  "epgRef": "/schemas/6216564c2e0000650270b5dc/templates/test_template/anps/test_ap/epgs/gui_test_epg",
  "contractRelationships": [],
  "subnets": [],
  "uSegEpg": false,
  "uSegAttrs": [],
  "intraEpg": "unenforced",
  "prio": "unspecified",
  "proxyArp": false,
  "preferredGroup": false,
  "bdRef": "/schemas/6216564c2e0000650270b5dc/templates/test_template/bds/test_bd",
  "vrfRef": "",
  "selectors": [],
  "epgType": "application"
}

Terraform created EPG

{
  "name": "terraform_test_epg",
  "displayName": "terraform_test_epg",
  "epgRef": "/schemas/6216564c2e0000650270b5dc/templates/test_template/anps/test_ap/epgs/terraform_test_epg",
  "contractRelationships": [],
  "subnets": [],
  "uSegEpg": false,
  "uSegAttrs": [],
  "intraEpg": "unenforced",
  "prio": "unspecified",
  "proxyArp": false,
  "preferredGroup": false,
  "bdRef": "/schemas/6216564c2e0000650270b5dc/templates/test_template/bds/test_bd",
  "vrfRef": "/schemas/6216564c2e0000650270b5dc/templates/test_template/vrfs/test_vrf",
  "selectors": [],
  "epgType": "application"
 },
mike-guy commented 2 years ago

I've dug into this problem a little this evening. I suspect the issue may need a bit of work in both the Go client and the Terraform provider.

The immediate issue is caused by line 413 in resource_mso_schema_template_anp_epg.go...

    if tempVar, ok := d.GetOk("vrf_schema_id"); ok {
        vrf_schema_id = tempVar.(string)
    } else {
        vrf_schema_id = schemaId
    }

Whilst vrf_schemaid is set to optional in the schema, this essentially sets a default. Which means that if the user provides NO values for vrf attributes at all, this still gets set. As soon as this is set, the API call fails as it expects the name to be set as well. A quick dirty fix is to just remove the else statement - at this point the apply will succeed...

    if tempVar, ok := d.GetOk("vrf_schema_id"); ok {
        vrf_schema_id = tempVar.(string)
    }
resource "mso_schema_template_anp_epg" "epg" {
  schema_id        = mso_schema.schema.id
  template_name    = mso_schema.schema.template_name
  anp_name         = mso_schema_template_anp.ap.name
  name             = "terraform_test_epg"
  display_name     = "terraform_test_epg"
  bd_name          = mso_schema_template_bd.bd.name
  bd_schema_id     = mso_schema.schema.id
  bd_template_name = mso_schema.schema.template_name
  # vrf_name          = mso_schema_template_vrf.vrf.name
  # vrf_schema_id     = mso_schema.schema.id
  # vrf_template_name = mso_schema.schema.template_name
}
<other resources omitted for brevity>

  # mso_tenant.tenant will be created
  + resource "mso_tenant" "tenant" {
      + description  = (known after apply)
      + display_name = "test_tenant"
      + id           = (known after apply)
      + name         = "test_tenant"

      + site_associations {
          + aws_access_key_id         = (known after apply)
          + aws_account_id            = (known after apply)
          + aws_secret_key            = (known after apply)
          + azure_access_type         = (known after apply)
          + azure_active_directory_id = (known after apply)
          + azure_application_id      = (known after apply)
          + azure_client_secret       = (known after apply)
          + azure_shared_account_id   = (known after apply)
          + azure_subscription_id     = (known after apply)
          + is_aws_account_trusted    = (known after apply)
          + security_domains          = (known after apply)
          + site_id                   = (known after apply)
          + vendor                    = (known after apply)
        }

      + user_associations {
          + user_id = (known after apply)
        }
    }

Plan: 6 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

mso_tenant.tenant: Creating...
mso_tenant.tenant: Creation complete after 0s [id=622a4efb2c00000a0e40afd7]
mso_schema.schema: Creating...
mso_schema.schema: Creation complete after 0s [id=622a4efb2c0000180e40afd8]
mso_schema_template_anp.ap: Creating...
mso_schema_template_vrf.vrf: Creating...
mso_schema_template_anp.ap: Creation complete after 1s [id=test_ap]
mso_schema_template_vrf.vrf: Creation complete after 1s [id=test_vrf]
mso_schema_template_bd.bd: Creating...
mso_schema_template_bd.bd: Creation complete after 0s [id=test_bd]
mso_schema_template_anp_epg.epg: Creating...
mso_schema_template_anp_epg.epg: Creation complete after 0s [id=terraform_test_epg]

Apply complete! Resources: 6 added, 0 changed, 0 destroyed.

Whilst this fixes the issue in the short term, it does not address the fact that the MSO go client pushes attributes that would NOT be configured if you did so in the GUI. See below for an example:

Note that in particular the GUI configured does not configure:

I'm not an ACI expert, but based on the provider documentation, there are two scenarios:

It sounds like some logic in the Go client (or provider/both) needs to account for the two different scenarios and ensure only the relevant attributes are actually set.

Config for GUI created EPG

{
    "name": "gui_test_epg",
    "displayName": "gui_test_epg",
    "epgRef": "/schemas/622a4efb2c0000180e40afd8/templates/test_template/anps/test_ap/epgs/gui_test_epg",
    "contractRelationships": [],
    "subnets": [],
    "uSegEpg": false,
    "uSegAttrs": [],
    "intraEpg": "unenforced",
    "prio": "unspecified",
    "proxyArp": false,
    "preferredGroup": false,
    "bdRef": "/schemas/622a4efb2c0000180e40afd8/templates/test_template/bds/gui_test_bd",
    "vrfRef": "",
    "selectors": [],
    "epgType": "application"
}

Config for Terraform created EPG

{
    "name": "terraform_test_epg",
    "displayName": "terraform_test_epg",
    "epgRef": "/schemas/622a4efb2c0000180e40afd8/templates/test_template/anps/test_ap/epgs/terraform_test_epg",
    "contractRelationships": [],
    "subnets": [],
    "uSegEpg": false,
    "uSegAttrs": [],
    "intraEpg": "unenforced",
    "prio": "unspecified",
    "proxyArp": false,
    "mCastSource": false,
    "preferredGroup": false,
    "bdRef": "/schemas/622a4efb2c0000180e40afd8/templates/test_template/bds/test_bd",
    "vrfRef": "/schemas/622a4efb2c0000180e40afd8/templates/test_template/vrfs/",
    "selectors": [],
    "epgType": "application",
    "cloudServiceEpgConfig": {
        "serviceType": "AWS-Bucket",
        "deploymentType": "CloudNative",
        "accessType": "Private"
    }
}
lhercot commented 2 years ago

I think this is the same (but opposite) issue as #123 they should be addressed together.