You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: add Scope custom store property for GCP Certificate Manager
GCP Certificate Manager's `scope` field is create-only and immutable.
Previous releases hard-coded `Scope = "DEFAULT"` on every certificate
created, which made the orchestrator unusable for environments running
cross-region internal Application Load Balancers (`ALL_REGIONS`), Media
CDN (`EDGE_CACHE`), or mTLS trust configs (`CLIENT_AUTH`). Affected
customers had to pre-create empty placeholder certificate resources in
GCP via Terraform with the right scope, then point Keyfactor at the
shell - breaking the single-pane-of-glass workflow.
Adds a `Scope` custom store property honored by Management/Add. Values
are case-normalized and validated against the four-value enum GCP
accepts; an unsupported value fails the `ResolveScope` flow step before
any API call. Blank resolves to `DEFAULT` so existing stores keep
working with no operator action.
Replace (overwrite) intentionally does not propagate scope: the patch
UpdateMask is "SelfManaged", and GCP would reject a scope change
anyway. The recommended pattern is one store per (project, location,
scope) tuple.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docsource/gcpcertmgr.md
+34Lines changed: 34 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -20,6 +20,7 @@ That single value carries both the GCP project and the location (region or `glob
20
20
|**Client Machine**| Display label only. Recommended: GCP Organization ID (e.g. `1005564431893`). Not parsed. | UI grouping in Command |
21
21
|**Service Account Key File Path** (custom, *deprecated*) | v1.1 shape only. Leave blank for new stores - authentication uses Application Default Credentials. | Credential loader fallback; emits a deprecation warning when read |
22
22
|**Location** (custom, *deprecated*) | v1.1 shape only. New stores leave it blank. Used as a fallback when Store Path is empty or `n/a`. | v1.1 fallback path; emits a deprecation warning when read |
23
+
|**Certificate Scope** (custom) | GCP `scope` to apply to every new certificate created in this store. One of `DEFAULT`, `ALL_REGIONS`, `EDGE_CACHE`, `CLIENT_AUTH`. Blank → `DEFAULT`. Immutable on each cert once set in GCP - use one store per scope. See "Certificate scope" below. | Management/Add |
23
24
24
25
#### Location semantics: where the GCP region lives
25
26
@@ -65,6 +66,7 @@ Set:
65
66
-**Store Path**: `projects/{projectId}/locations/{location}` - e.g. `projects/edgecerts/locations/global`
-**Certificate Scope**: `DEFAULT` for global external Application Load Balancers (the common case). Set to `ALL_REGIONS` if this store provisions certs for cross-region internal Application Load Balancers; `EDGE_CACHE` for Media CDN; `CLIENT_AUTH` for mTLS trust configs. See "Certificate scope" below.
|**Certificate Scope** (custom) | Defaults to `DEFAULT`. Change only if this discovered store is going to back a non-default scope (`ALL_REGIONS`, `EDGE_CACHE`, `CLIENT_AUTH`) - Discovery does not know which scope your downstream load balancers need, so the operator sets it at approval time. Discovery emits one candidate per (project, location); if you need both `DEFAULT` and `ALL_REGIONS` certs in the same (project, location), reject this candidate and create two stores manually instead. See "Certificate scope" below. |
82
85
|**Create Certificate Store If Missing**|**Check this.** Tells Command to create a new certificate store record from this candidate. Without it, the candidate sits in the discover tab with no store backing it. |
83
86
|**Inventory Schedule**| Pick a cadence (e.g. Daily) for the inventory job to run after the store is created. |
84
87
@@ -148,6 +151,37 @@ GCP Certificate Manager constrains certificate resource IDs to a strict shape:
148
151
149
152
The orchestrator validates the alias against this rule **before** any API calls or PFX parsing during Management/Add. A non-conforming alias fails fast with a `[FAIL] ValidateAlias` step in the flow trace and a suggestion of a normalized alias (e.g. `Cert1` → `cert1`). Rename the certificate in Keyfactor Command to the suggested form and retry the Management/Add job.
150
153
154
+
### Certificate scope
155
+
156
+
GCP Certificate Manager attaches a `scope` to every certificate that determines which load balancer / service families can consume it. The `Scope` custom store property tells the orchestrator which value to pass to GCP on create.
157
+
158
+
| Value | What it is for |
159
+
|---|---|
160
+
|`DEFAULT`| Global external Application Load Balancers - the standard GCP load balancer for internet-facing traffic. This is the GCP default and the right answer for most stores. |
161
+
|`ALL_REGIONS`| Cross-region **internal** Application Load Balancers. Use this when the consuming load balancer is regional/internal and replicated across regions. |
162
+
|`EDGE_CACHE`| Google Cloud Media CDN edge-cache certs. |
163
+
|`CLIENT_AUTH`| Certificates used by mTLS trust configs, or server certificates that are authorized for client authentication. |
164
+
165
+
#### Immutability
166
+
167
+
The `scope` field is **create-only** in the GCP API. Once GCP creates a certificate with a given scope, that scope cannot be changed by any patch operation. The orchestrator's Management/Replace path uses `UpdateMask = "SelfManaged"`, so re-adding a certificate over an existing one preserves its original scope - even if the store's Scope property has changed. To migrate a certificate to a different scope, delete it (Management/Remove) and re-add it with the new scope.
168
+
169
+
This is why the recommended deployment pattern is **one store per (project, location, scope) tuple**. The Scope property is store-wide, not per-cert.
170
+
171
+
#### What happened before v1.2.1
172
+
173
+
Prior to v1.2.1 the orchestrator hard-coded `Scope = "DEFAULT"` on every certificate it created. Customers who needed non-default scopes (typically `ALL_REGIONS` for cross-region internal ALBs) had to pre-create empty placeholder certificate resources in GCP via Terraform with `scope = "ALL_REGIONS"`, then point Keyfactor at the existing resource as a Replace target. The new property removes that workaround: a store with Scope = `ALL_REGIONS` will create new certificate resources directly at the right scope.
174
+
175
+
#### How the orchestrator validates the value
176
+
177
+
`JobBase.ResolveScope` runs as the `ResolveScope` flow step on every Management/Add. It trims and uppercases the configured value, then validates it against the set GCP accepts. An unsupported value (typo, lowercase letters that don't normalize to a valid token, anything outside the four allowed values) fails with `[FAIL] ResolveScope` and a clear message naming the four legal values. Blank or null resolves to `DEFAULT`.
178
+
179
+
#### Quick reference
180
+
181
+
- "Where do I see what scope a certificate ended up with?" → GCP's Certificate Manager Console, or `gcloud certificate-manager certificates describe <name> --location=<loc> --project=<proj>`. The orchestrator's Inventory job does not surface scope today.
182
+
- "Can I change a certificate's scope?" → No. Delete and re-add.
183
+
- "I have one store with DEFAULT certs and I want to add an ALL_REGIONS cert" → Create a second store with the same (project, location) but Scope = `ALL_REGIONS`.
184
+
151
185
### Architecture and logging
152
186
153
187
Every job (Discovery, Inventory, Management) uses a shared `FlowLogger` to record step-by-step progress with timing. The flow summary is appended to `JobResult.FailureMessage` on **both** success and failure paths so operators reading job history can see what happened without having to pull orchestrator-side trace logs. Errors arising from the GCP SDK are unwrapped through `AggregateException` walls and reported with HTTP status + the GCP error response body, so quota errors / IAM denials / malformed certificates surface clearly in Command's UI.
Copy file name to clipboardExpand all lines: integration-manifest.json
+10Lines changed: 10 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,16 @@
58
58
"Required": false,
59
59
"IsPAMEligible": false,
60
60
"Description": "**Deprecated in v1.2.** Leave blank. Authenticate via Application Default Credentials instead (set `GOOGLE_APPLICATION_CREDENTIALS` as a machine-level environment variable on the orchestrator host pointing at the JSON key, or run on a GCE VM / GKE pod with workload identity). The Discovery job has no way to surface this custom property in Keyfactor Command's discovery-job UI, so ADC is the only mechanism that works uniformly across all four job types. v1.1 stores that have this populated continue to work via a deprecation-logged fallback; the field is scheduled for removal in v2.0."
61
+
},
62
+
{
63
+
"Name": "Scope",
64
+
"DisplayName": "Certificate Scope",
65
+
"Type": "String",
66
+
"DependsOn": "",
67
+
"DefaultValue": "DEFAULT",
68
+
"Required": false,
69
+
"IsPAMEligible": false,
70
+
"Description": "GCP Certificate Manager `scope` value applied to every new certificate created in this store. Allowed: `DEFAULT` (global external Application Load Balancers - the GCP default), `ALL_REGIONS` (cross-region internal Application Load Balancers), `EDGE_CACHE` (Media CDN), `CLIENT_AUTH` (mTLS trust configs / authorized client server certs). **Immutable in GCP** - once a certificate is created with a given scope, GCP refuses to change it. To use a different scope, delete and re-add the certificate. Pick the scope that matches the load balancer / service this store provisions certs for, and use one store per scope. Leave blank or set to `DEFAULT` for the legacy behavior."
61
71
}
62
72
],
63
73
"ClientMachineDescription": "Display label for grouping certificate stores in Keyfactor Command. Recommended value is the GCP Organization ID (e.g. `1005564431893`); the orchestrator does not parse a project ID out of this field. The actual GCP project + location are read from Store Path.",
0 commit comments