hashicorp / terraform-provider-awscc

Terraform AWS Cloud Control provider
https://registry.terraform.io/providers/hashicorp/awscc/latest/docs
Mozilla Public License 2.0
239 stars 107 forks source link

awscc_inspectorv2_cis_scan_configuration: subsequent terraform apply shows updates #1749

Open quixoticmonk opened 1 month ago

quixoticmonk commented 1 month ago

Community Note

Terraform CLI and Terraform AWS Cloud Control Provider Version

terraform -v
Terraform v1.7.4
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v5.50.0
+ provider registry.terraform.io/hashicorp/awscc v0.78.0

Affected Resource(s)

Terraform Configuration Files

resource "awscc_inspectorv2_cis_scan_configuration" "example" {
  scan_name = "example"
  schedule = {
    daily = {
      start_time = {
        time_of_day = "00:00"
        time_zone   = "UTC"
      }
    }
  }

  security_level = "LEVEL_1"
  targets = {
    account_ids = ["123456789012"]
    target_resource_tags = {
      "Modified By" = ["AWSCC"]
    }

  }

  tags = {
    "Modified By" = "AWSCC"
  }
}

Debug Output

Panic Output

Expected Behavior

Expected Terraform to display " No changes detected. .." showcasing the idempotencty of the operation.

Actual Behavior

Terraform identified changes required in two places on a subsequent terraform apply when the configuration didn't change.

  ~ update in-place

Terraform will perform the following actions:

  # awscc_inspectorv2_cis_scan_configuration.example will be updated in-place
  ~ resource "awscc_inspectorv2_cis_scan_configuration" "example" {
        id             = "arn:aws:inspector2:us-east-1:123456789012:owner/123456789012/cis-configuration/a6add744-7bbf-43fc-8aff-0e4bba35f1b9"
      ~ schedule       = {
          + monthly  = (known after apply)
          + one_time = (known after apply)
          + weekly   = (known after apply)
            # (1 unchanged attribute hidden)
        }
        tags           = {
            "Modified By" = "AWSCC"
        }
      ~ targets        = {
          ~ account_ids          = [
              - "SELF",
              + "123456789012",
            ]
            # (1 unchanged attribute hidden)
        }
        # (3 unchanged attributes hidden)
    }

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

Steps to Reproduce

  1. terraform apply
  2. terraform apply

Important Factoids

References

wellsiau-aws commented 4 days ago

CCAPI GetResource returned SELF as the value for account_ids:

{
  "SecurityLevel": "LEVEL_1",
  "Schedule": {
    "Daily": {
      "StartTime": {
        "TimeOfDay": "00:00",
        "TimeZone": "UTC"
      }
    }
  },
  "Targets": {
    "TargetResourceTags": {
      "Modified By": [
        "AWSCC"
      ]
    },
    "AccountIds": [
      "SELF"
    ]
  },
  "ScanName": "example",
  "Arn": "arn:aws:inspector2:us-east-1:204034886740:owner/204034886740/cis-configuration/0b4ffcdc-b7ec-40e1-ba3e-969fed5ca99b",
  "Tags": {
    "Modified By": "AWSCC"
  }
}

Since Terraform compare Statefile versus CCAPI GetResource, it detects it as a drift.

In this case, CCAPI mutate the original input (account id) and transform it into keyword SELF.

As noted in 1438, property transformation is currently not supported in AWSCC.

Worth nothing as well that the schema from Cfn registry does not include property transform either

{
  "SecurityLevel": "LEVEL_1",
  "Schedule": {
    "Daily": {
      "StartTime": {
        "TimeOfDay": "00:00",
        "TimeZone": "UTC"
      }
    }
  },
  "Targets": {
    "TargetResourceTags": {
      "Modified By": [
        "AWSCC"
      ]
    },
    "AccountIds": [
      "SELF"
    ]
  },
  "ScanName": "example",
  "Arn": "arn:aws:inspector2:us-east-1:204034886740:owner/204034886740/cis-configuration/0b4ffcdc-b7ec-40e1-ba3e-969fed5ca99b",
  "Tags": {
    "Modified By": "AWSCC"
  }
}
3c06301d1719:awscc_inspector wellsiau$ 
3c06301d1719:awscc_inspector wellsiau$ 
3c06301d1719:awscc_inspector wellsiau$ aws cloudformation describe-type --type RESOURCE --type-name AWS::InspectorV2::CisScanConfiguration | jq -r ".Schema" | jq "."
{
  "typeName": "AWS::InspectorV2::CisScanConfiguration",
  "description": "CIS Scan Configuration resource schema",
  "sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-inspector.git",
  "definitions": {
    "CisSecurityLevel": {
      "type": "string",
      "enum": [
        "LEVEL_1",
        "LEVEL_2"
      ]
    },
    "Schedule": {
      "description": "Choose a Schedule cadence",
      "properties": {
        "OneTime": {
          "$ref": "#/definitions/OneTimeSchedule"
        },
        "Daily": {
          "$ref": "#/definitions/DailySchedule"
        },
        "Weekly": {
          "$ref": "#/definitions/WeeklySchedule"
        },
        "Monthly": {
          "$ref": "#/definitions/MonthlySchedule"
        }
      }
    },
    "OneTimeSchedule": {
      "type": "object"
    },
    "DailySchedule": {
      "type": "object",
      "properties": {
        "StartTime": {
          "$ref": "#/definitions/Time"
        }
      },
      "required": [
        "StartTime"
      ],
      "additionalProperties": false
    },
    "WeeklySchedule": {
      "type": "object",
      "properties": {
        "StartTime": {
          "$ref": "#/definitions/Time"
        },
        "Days": {
          "$ref": "#/definitions/DaysList"
        }
      },
      "required": [
        "StartTime",
        "Days"
      ],
      "additionalProperties": false
    },
    "MonthlySchedule": {
      "type": "object",
      "properties": {
        "StartTime": {
          "$ref": "#/definitions/Time"
        },
        "Day": {
          "$ref": "#/definitions/Day"
        }
      },
      "required": [
        "StartTime",
        "Day"
      ],
      "additionalProperties": false
    },
    "Time": {
      "type": "object",
      "properties": {
        "TimeOfDay": {
          "type": "string",
          "pattern": "^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$"
        },
        "TimeZone": {
          "type": "string"
        }
      },
      "required": [
        "TimeOfDay",
        "TimeZone"
      ],
      "additionalProperties": false
    },
    "DaysList": {
      "type": "array",
      "minItems": 1,
      "maxItems": 7,
      "items": {
        "$ref": "#/definitions/Day"
      },
      "uniqueItems": true
    },
    "Day": {
      "type": "string",
      "enum": [
        "MON",
        "TUE",
        "WED",
        "THU",
        "FRI",
        "SAT",
        "SUN"
      ]
    },
    "CisTargets": {
      "properties": {
        "AccountIds": {
          "type": "array",
          "minItems": 1,
          "maxItems": 10000,
          "items": {
            "type": "string",
            "pattern": "^\\d{12}|ALL_MEMBERS|SELF$"
          },
          "uniqueItems": true
        },
        "TargetResourceTags": {
          "$ref": "#/definitions/TargetResourceTags"
        }
      },
      "additionalProperties": false,
      "required": [
        "AccountIds"
      ]
    },
    "TargetResourceTags": {
      "patternProperties": {
        "^.+$": {
          "$ref": "#/definitions/TagValueList"
        }
      },
      "maxProperties": 5,
      "additionalProperties": false
    },
    "TagValueList": {
      "type": "array",
      "minItems": 1,
      "maxItems": 5,
      "items": {
        "type": "string"
      },
      "uniqueItems": true
    },
    "CisTagMap": {
      "type": "object",
      "patternProperties": {
        "^.{2,127}$": {
          "type": "string",
          "pattern": "^.{1,255}$"
        }
      },
      "additionalProperties": false
    }
  },
  "properties": {
    "ScanName": {
      "description": "Name of the scan",
      "type": "string",
      "minLength": 1
    },
    "SecurityLevel": {
      "$ref": "#/definitions/CisSecurityLevel"
    },
    "Schedule": {
      "$ref": "#/definitions/Schedule"
    },
    "Targets": {
      "$ref": "#/definitions/CisTargets"
    },
    "Arn": {
      "type": "string",
      "description": "CIS Scan configuration unique identifier"
    },
    "Tags": {
      "$ref": "#/definitions/CisTagMap"
    }
  },
  "additionalProperties": false,
  "readOnlyProperties": [
    "/properties/Arn"
  ],
  "primaryIdentifier": [
    "/properties/Arn"
  ],
  "tagging": {
    "taggable": true,
    "tagOnCreate": true,
    "tagUpdatable": true,
    "cloudFormationSystemTags": true,
    "tagProperty": "/properties/Tags"
  },
  "handlers": {
    "create": {
      "permissions": [
        "inspector2:CreateCisScanConfiguration",
        "inspector2:ListCisScanConfigurations",
        "inspector2:TagResource"
      ]
    },
    "read": {
      "permissions": [
        "inspector2:ListCisScanConfigurations",
        "inspector2:ListTagsForResource"
      ]
    },
    "update": {
      "permissions": [
        "inspector2:ListCisScanConfigurations",
        "inspector2:UpdateCisScanConfiguration",
        "inspector2:TagResource",
        "inspector2:UntagResource",
        "inspector2:ListTagsForResource"
      ]
    },
    "delete": {
      "permissions": [
        "inspector2:ListCisScanConfigurations",
        "inspector2:DeleteCisScanConfiguration",
        "inspector2:UntagResource"
      ]
    },
    "list": {
      "permissions": [
        "inspector2:ListCisScanConfigurations",
        "inspector2:ListTagsForResource"
      ]
    }
  }
}
wellsiau-aws commented 4 days ago

I noticed that the AccountId property schema takes enum including account number, ALL_MEMBERS and SELF.

    "CisTargets": {
      "properties": {
        "AccountIds": {
          "type": "array",
          "minItems": 1,
          "maxItems": 10000,
          "items": {
            "type": "string",
            "pattern": "^\\d{12}|ALL_MEMBERS|SELF$"
          },
          "uniqueItems": true
        },

As such, modifying the configuration to use SELF removes this drift. I believe we can call out this workaround in the documentation example.

wellsiau-aws commented 4 days ago

Sample config that will not introduce drift:

resource "awscc_inspectorv2_cis_scan_configuration" "example" {
  scan_name = "example"
  schedule = {
    daily = {
      start_time = {
        time_of_day = "00:00"
        time_zone   = "UTC"
      }
    }
  }

  security_level = "LEVEL_1"
  targets = {
    account_ids = ["SELF"]
    target_resource_tags = {
      "Modified By" = ["AWSCC"]
    }

  }

  tags = {
    "Modified By" = "AWSCC"
  }
}
quixoticmonk commented 4 days ago

Updated the example to include a note around the account_ids to use SELF if looking at the current account.