diff --git a/github/resource_github_actions_organization_secret.go b/github/resource_github_actions_organization_secret.go index 86b6ac1673..8a91732e52 100644 --- a/github/resource_github_actions_organization_secret.go +++ b/github/resource_github_actions_organization_secret.go @@ -137,7 +137,6 @@ func resourceGithubActionsOrganizationSecretRead(d *schema.ResourceData, meta in } d.Set("plaintext_value", d.Get("plaintext_value")) - d.Set("updated_at", secret.UpdatedAt.String()) d.Set("created_at", secret.CreatedAt.String()) d.Set("visibility", secret.Visibility) @@ -158,6 +157,28 @@ func resourceGithubActionsOrganizationSecretRead(d *schema.ResourceData, meta in d.Set("selected_repository_ids", selectedRepositoryIDs) + // This is a drift detection mechanism based on timestamps. + // + // If we do not currently store the "updated_at" field, it means we've only + // just created the resource and the value is most likely what we want it to + // be. + // + // If the resource is changed externally in the meantime then reading back + // the last update timestamp will return a result different than the + // timestamp we've persisted in the state. In that case, we can no longer + // trust that the value (which we don't see) is equal to what we've declared + // previously. + // + // The only solution to enforce consistency between is to mark the resource + // as deleted (unset the ID) in order to fix potential drift by recreating + // the resource. + if updatedAt, ok := d.GetOk("updated_at"); ok && updatedAt != secret.UpdatedAt.String() { + log.Printf("[WARN] The secret %s has been externally updated in GitHub", d.Id()) + d.SetId("") + } else if !ok { + d.Set("updated_at", secret.UpdatedAt.String()) + } + return nil } diff --git a/github/resource_github_actions_secret.go b/github/resource_github_actions_secret.go index 46a820b362..d740101100 100644 --- a/github/resource_github_actions_secret.go +++ b/github/resource_github_actions_secret.go @@ -106,9 +106,30 @@ func resourceGithubActionsSecretRead(d *schema.ResourceData, meta interface{}) e } d.Set("plaintext_value", d.Get("plaintext_value")) - d.Set("updated_at", secret.UpdatedAt.String()) d.Set("created_at", secret.CreatedAt.String()) + // This is a drift detection mechanism based on timestamps. + // + // If we do not currently store the "updated_at" field, it means we've only + // just created the resource and the value is most likely what we want it to + // be. + // + // If the resource is changed externally in the meantime then reading back + // the last update timestamp will return a result different than the + // timestamp we've persisted in the state. In that case, we can no longer + // trust that the value (which we don't see) is equal to what we've declared + // previously. + // + // The only solution to enforce consistency between is to mark the resource + // as deleted (unset the ID) in order to fix potential drift by recreating + // the resource. + if updatedAt, ok := d.GetOk("updated_at"); ok && updatedAt != secret.UpdatedAt.String() { + log.Printf("[WARN] The secret %s has been externally updated in GitHub", d.Id()) + d.SetId("") + } else if !ok { + d.Set("updated_at", secret.UpdatedAt.String()) + } + return nil }