-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Terraform version, Kubernetes provider version and Kubernetes version
Terraform version: Terraform v1.5.5 on darwin_arm64
Kubernetes Provider version: v2.25.2
Kubernetes version: 1.28
Terraform configuration
locals {
applications = {
application-1 = {
chart = "application-1"
}
application-2 = {
chart = "application-2"
}
application-3 = {
chart = "application-3"
version = 3.46.0
}
}
}
resource "kubernetes_manifest" "argocd_application" {
for_each = local.applications
manifest = {
apiVersion = "argoproj.io/v1alpha1"
kind = "Application"
metadata = {
name = each.key
namespace = "kube-system"
finalizers = [
# Foreground cascading deletion.
"resources-finalizer.argocd.argoproj.io"
]
}
spec = {
project = "default"
source = {
repoURL = "https://artifactory.example.com/artifactory/helm-all"
chart = each.value["chart"]
targetRevision = try(var.charts[each.value["chart"]]["version"], null)
helm = {
valuesObject = {
replicas = 1
}
parameters = concat([
{
name = "image.name"
value = each.key
}
],
try(each.value["version"], null) != null ? [
{
name = "image.tag"
value = each.value["version"]
}
] : [],
)
}
}
destination = {
server = "https://kubernetes.default.svc"
namespace = "mynamespace"
}
syncPolicy = {
automated = {
prune = true
selfHeal = true
}
}
}
}
}
Question
Hi all. I am using the kubernetes_manifest
resource in order to install Argo CD Application
objects (spec is defined here).
Initial object creation works without issue, and subsequent runs of terraform apply
show no changes (as I would expect).
However, if the input value of manifest.spec.source.helm.valuesObject
changes (say, the value of foo
changes from bar
to bar123
), the subsequent run of terraform apply
says that the entire kubernetes_manifest
resource must be replaced, alongside a massive number of added and changed fields:
Terraform will perform the following actions:
# kubernetes_manifest.argocd_application["application-3"] must be replaced
-/+ resource "kubernetes_manifest" "argocd_application" {
~ manifest = {
~ spec = {
~ source = {
~ helm = {
~ valuesObject = {
~ foo = "bar" -> "bar123"
# (6 unchanged attributes hidden)
}
# (1 unchanged attribute hidden)
}
# (3 unchanged attributes hidden)
}
# (3 unchanged attributes hidden)
}
# (3 unchanged attributes hidden)
}
~ object = {
~ metadata = {
+ annotations = (known after apply)
+ creationTimestamp = (known after apply)
+ deletionGracePeriodSeconds = (known after apply)
+ deletionTimestamp = (known after apply)
+ generateName = (known after apply)
+ generation = (known after apply)
+ labels = (known after apply)
+ managedFields = (known after apply)
name = "application-3"
+ ownerReferences = (known after apply)
+ resourceVersion = (known after apply)
+ selfLink = (known after apply)
+ uid = (known after apply)
# (2 unchanged attributes hidden)
}
~ operation = {
+ info = (known after apply)
~ initiatedBy = {
+ automated = (known after apply)
+ username = (known after apply)
}
~ retry = {
~ backoff = {
+ duration = (known after apply)
+ factor = (known after apply)
+ maxDuration = (known after apply)
}
+ limit = (known after apply)
}
~ sync = {
+ dryRun = (known after apply)
+ manifests = (known after apply)
+ prune = (known after apply)
+ resources = (known after apply)
+ revision = (known after apply)
+ revisions = (known after apply)
~ source = {
+ chart = (known after apply)
~ directory = {
+ exclude = (known after apply)
+ include = (known after apply)
~ jsonnet = {
+ extVars = (known after apply)
+ libs = (known after apply)
+ tlas = (known after apply)
}
+ recurse = (known after apply)
}
~ helm = {
+ fileParameters = (known after apply)
+ ignoreMissingValueFiles = (known after apply)
+ parameters = (known after apply)
+ passCredentials = (known after apply)
+ releaseName = (known after apply)
+ skipCrds = (known after apply)
+ valueFiles = (known after apply)
+ values = (known after apply)
+ valuesObject = (known after apply)
+ version = (known after apply)
}
~ kustomize = {
+ commonAnnotations = (known after apply)
+ commonAnnotationsEnvsubst = (known after apply)
+ commonLabels = (known after apply)
+ forceCommonAnnotations = (known after apply)
+ forceCommonLabels = (known after apply)
+ images = (known after apply)
+ namePrefix = (known after apply)
+ nameSuffix = (known after apply)
+ namespace = (known after apply)
+ patches = (known after apply)
+ replicas = (known after apply)
+ version = (known after apply)
}
+ path = (known after apply)
~ plugin = {
+ env = (known after apply)
+ name = (known after apply)
+ parameters = (known after apply)
}
+ ref = (known after apply)
+ repoURL = (known after apply)
+ targetRevision = (known after apply)
}
+ sources = (known after apply)
+ syncOptions = (known after apply)
~ syncStrategy = {
~ apply = {
+ force = (known after apply)
}
~ hook = {
+ force = (known after apply)
}
}
}
}
~ spec = {
~ destination = {
+ name = (known after apply)
# (2 unchanged attributes hidden)
}
+ ignoreDifferences = (known after apply)
+ info = (known after apply)
+ revisionHistoryLimit = (known after apply)
~ source = {
~ directory = {
+ exclude = (known after apply)
+ include = (known after apply)
~ jsonnet = {
+ extVars = (known after apply)
+ libs = (known after apply)
+ tlas = (known after apply)
}
+ recurse = (known after apply)
}
~ helm = {
+ fileParameters = (known after apply)
+ ignoreMissingValueFiles = (known after apply)
~ parameters = [
~ {
+ forceString = (known after apply)
name = "image.name"
# (1 unchanged attribute hidden)
},
~ {
+ forceString = (known after apply)
name = "image.tag"
# (1 unchanged attribute hidden)
},
]
+ passCredentials = (known after apply)
+ releaseName = (known after apply)
+ skipCrds = (known after apply)
+ valueFiles = (known after apply)
+ values = (known after apply)
~ valuesObject = {
+ foo = "bar"
# (7 unchanged attributes hidden)
}
+ version = (known after apply)
}
~ kustomize = {
+ commonAnnotations = (known after apply)
+ commonAnnotationsEnvsubst = (known after apply)
+ commonLabels = (known after apply)
+ forceCommonAnnotations = (known after apply)
+ forceCommonLabels = (known after apply)
+ images = (known after apply)
+ namePrefix = (known after apply)
+ nameSuffix = (known after apply)
+ namespace = (known after apply)
+ patches = (known after apply)
+ replicas = (known after apply)
+ version = (known after apply)
}
+ path = (known after apply)
~ plugin = {
+ env = (known after apply)
+ name = (known after apply)
+ parameters = (known after apply)
}
+ ref = (known after apply)
# (3 unchanged attributes hidden)
}
+ sources = (known after apply)
~ syncPolicy = {
~ automated = {
+ allowEmpty = (known after apply)
# (2 unchanged attributes hidden)
}
~ managedNamespaceMetadata = {
+ annotations = (known after apply)
+ labels = (known after apply)
}
~ retry = {
~ backoff = {
+ duration = (known after apply)
+ factor = (known after apply)
+ maxDuration = (known after apply)
}
+ limit = (known after apply)
}
+ syncOptions = (known after apply)
}
# (1 unchanged attribute hidden)
}
# (2 unchanged attributes hidden)
}
}
I understand that objects created by Terraform can change outside of its control, and it appears that Argo CD's controllers are doing that in this case, by creating default values for optional fields. As such, I added a few lifecycle.ignore_changes
entries to instruct Terraform to disregard these:
resource "kubernetes_manifest" "argocd_application" {
lifecycle {
ignore_changes = [
object.metadata.annotations,
object.metadata.finalizers,
object.metadata.labels,
object.operation.initiatedBy.automated
]
}
[... rest of resource...]
}
However, this appears to have made no difference, as a terraform apply
still gives me the same "must be replaced" output with all the same fields.
I feel like I may be missing something here, so I'm asking for assistance at this stage. Thanks! 🙂