Documentation
¶
Index ¶
- Variables
- func ExtractStringValue(attr *hclwrite.Attribute) string
- func Process(outputDir, generatedFile string, reg *registry.Registry, opts *ProcessOptions) error
- func ReadObjectAttrString(body *hclwrite.Body, attrPath []string, attrName string) string
- func RemoveNestedAttrs(body *hclwrite.Body, attrPath []string, names ...string) bool
- func RenameLabels(generatedFile string, nameAttrFn func(string) string) error
- func RenameLabelsWithComposer(generatedFile string, compose LabelComposer) error
- func RenameLabelsWithFallback(generatedFile string, nameAttrFn func(string) string, ...) error
- func RewriteBlockForTest(body *hclwrite.Body, blockPath []string, rule ReferenceRule, ...)
- func RewriteListElementField(body *hclwrite.Body, attrName, elemAttr string, ...) bool
- func ShouldSkipProfile(payload string) (bool, string)
- func StripBlueprintDrafts(genPath string) (int, error)
- type ExtractSpec
- type FileKind
- type FixEdit
- type FixResult
- type LabelComposer
- type ProcessOptions
- type ProviderSchema
- type ReferenceRule
- type RequiredVar
- type TokenVar
Constants ¶
This section is empty.
Variables ¶
var Quiet bool
Quiet suppresses progress messages.
Functions ¶
func ExtractStringValue ¶
ExtractStringValue extracts a string or numeric literal from an HCL attribute's expression tokens. Exported for use by provider packages.
func Process ¶
func Process(outputDir, generatedFile string, reg *registry.Registry, opts *ProcessOptions) error
Process reads the generated HCL file, rewrites ID references, extracts script contents to files, and splits into per-type output files.
func ReadObjectAttrString ¶ added in v0.4.0
ReadObjectAttrString reads attrName from the nested object-expression attribute path attrPath within body, returning "" if absent. Exported for provider label composers that derive names from a nested attribute (e.g. the federated jamfplatform_pro_* types whose display name lives at general.name).
func RemoveNestedAttrs ¶ added in v0.4.0
RemoveNestedAttrs removes the named attributes from the object expression at attrPath within body, re-serializing the affected object expressions, and returns whether anything changed. Exported for provider post-processing that drops server-computed echo fields from a nested object (e.g. dropping a self_service_icon's uri/filename so only the rewritten id reference remains).
func RenameLabels ¶
RenameLabels rewrites auto-generated labels (like "all_0") in the generated HCL file to friendly names derived from resource attributes. It also updates the corresponding import blocks' "to" attributes to match.
nameAttrFn returns the attribute name used to derive a friendly label for the given resource type (e.g. "name", "email", "title").
func RenameLabelsWithComposer ¶ added in v0.4.0
func RenameLabelsWithComposer(generatedFile string, compose LabelComposer) error
RenameLabelsWithComposer is the most general label-renaming entry point: it derives each resource's friendly name via the supplied LabelComposer, which can inspect the whole resource body (e.g. to fold device_type into a jamfplatform_device_group label so computer and mobile groups that share a name do not collide).
func RenameLabelsWithFallback ¶ added in v0.2.0
func RenameLabelsWithFallback(generatedFile string, nameAttrFn func(string) string, idToName map[string]map[string]string) error
RenameLabelsWithFallback is like RenameLabels but, when the resource block's name attribute is missing or empty, falls back to looking up the name by import-block ID via idToName[resourceType][id]. This is needed for resource types whose name field is computed/read-only and so doesn't appear in the HCL produced by terraform query / -generate-config-out (e.g. Jamf Protect's jamfprotect_analytic_managed).
func RewriteBlockForTest ¶
func RewriteBlockForTest(body *hclwrite.Body, blockPath []string, rule ReferenceRule, reg *registry.Registry)
RewriteBlockForTest is an exported wrapper around rewriteBlock for use by external test packages. Not intended for production use.
func RewriteListElementField ¶ added in v0.4.0
func RewriteListElementField(body *hclwrite.Body, attrName, elemAttr string, resolve func(string) (string, bool)) bool
RewriteListElementField rewrites elemAttr on each object element of the list-of-objects attribute attrName in body. For each element it reads the current string value of elemAttr and calls resolve(value); when resolve returns (ref, true) the field is replaced with the raw ref token, otherwise the element is left untouched (no TODO marker — unmatched values are expected, e.g. built-in smart-group criteria). Returns true if any element changed.
Exported for provider-specific criteria rewriting where the resolution target depends on context the generic ReferenceRule engine cannot express from a single element (e.g. the owning resource's device_type selecting which extension-attribute name space to resolve against).
func ShouldSkipProfile ¶ added in v0.3.0
ShouldSkipProfile reports whether a configuration profile should be excluded from the Terraform output. Returns (true, reason) for:
- Jamf Protect profiles: any PayloadIdentifier starts with com.jamf.protect.*
- Jamf Security Cloud / Jamf Trust profiles: any PayloadIdentifier starts with com.jamf.trust.*
- Non-XML payloads: signed binary profiles stored as base64-encoded DER.
func StripBlueprintDrafts ¶ added in v0.6.0
StripBlueprintDrafts removes non-creatable blueprint drafts — and their import blocks — from a generated.tf before anything else reads it. Process makes the same call mid-pass for the single-env pipeline; this exists for the multi-env path, where the validation auto-fix runs against the raw generated.tf first and would otherwise reach a draft ahead of the skip.
It matters because the two treatments contradict each other. The draft's empty device_groups trips the provider's SizeAtLeast(1) validator, which the auto-fix reads as a Required empty collection and answers by turning the attribute into a variable for the operator to supply. That rewrite hides the emptiness the skip keys on, so the draft then survives into the module as a resource nobody can plan without inventing a device group for a blueprint that was never creatable in the first place.
Returns the number of drafts removed. A parse failure leaves the file alone.
Types ¶
type ExtractSpec ¶ added in v0.4.0
type ExtractSpec struct {
// ResourceType is the TF resource type this spec applies to.
ResourceType string
// BlockPath navigates nested HCL blocks to the container holding AttrName.
BlockPath []string
// AttrPath navigates nested object-expression attributes to the container.
AttrPath []string
// AttrName is the string attribute to extract (e.g. "script_contents", "payloads").
AttrName string
// OutputSubdir is the directory under support_files/ to write the file into.
OutputSubdir string
// FileKind selects the file extension.
FileKind FileKind
// Ext is the literal file extension (including leading dot) used when
// FileKind is FileKindRaw; ignored for other kinds.
Ext string
// NameAttr names the attribute used to derive the filename (default "name").
NameAttr string
// NameAttrPath is the object-attribute path to the container holding NameAttr
// (empty = resource root; e.g. ["general"] for jamfplatform profiles).
NameAttrPath []string
// SkipFn, when set, is consulted with the extracted content; a true result
// means the whole resource should be dropped from output (handled by the caller).
SkipFn func(content string) (bool, string)
}
ExtractSpec declaratively describes a string attribute whose value should be extracted to a support file and replaced with a file() reference. It is provider-agnostic: BlockPath navigates HCL nested blocks (jamfpro), AttrPath navigates plugin-framework object-expression attributes (jamfplatform), and they compose (BlockPath first, then AttrPath).
type FileKind ¶ added in v0.4.0
type FileKind int
FileKind selects the filename extension and derivation strategy for an extracted support file.
const ( // FileKindScript guesses the extension from the script shebang (.sh/.py/...). FileKindScript FileKind = iota // FileKindMobileconfig writes a .mobileconfig file. FileKindMobileconfig // FileKindXML writes a .xml file. FileKindXML // FileKindRaw writes a file with the literal extension given by ExtractSpec.Ext // (e.g. ".ppd", ".mobileprovision"). Used for document-like content that is // neither a guessable script nor a profile/XML payload. FileKindRaw )
type FixEdit ¶ added in v0.6.0
type FixEdit struct {
Resource string // resource address, e.g. "jamfplatform_pro_policy.install_chrome"
Attr string // attribute path
Action string // "removed", "set" or "replaced with a variable"
Reason string // the diagnostic summary
}
FixEdit is one auto-fix: the resource and attribute changed, and the diagnostic summary that prompted it.
type FixResult ¶
type FixResult struct {
Fixed int // total attributes fixed (removed, set, or replaced)
RequiredVars []RequiredVar // write-only variables that need user-supplied values
// Edits records what was changed and why, one entry per fix. The auto-fix
// edits generated configuration on the user's behalf, so it has to be able
// to say what it touched — a bare count leaves no way to tell a correct
// repair from a wrong one, or to notice when a provider fix upstream has
// made a repair unnecessary.
Edits []FixEdit
// StillInvalid reports that the project did not pass terraform validate by
// the time the fix loop gave up — either nothing left was auto-fixable or
// the iteration cap was reached. FixValidationErrors returns a nil error in
// both cases (an unfixable diagnostic is not a jamformer failure), so
// without this the caller has no way to tell a repaired project from one
// that still will not plan, and would print a success summary over it.
StillInvalid bool
}
FixResult holds the outcome of FixValidationErrors.
func FixValidationErrors ¶
func FixValidationErrors(outputDir string, schema *ProviderSchema) (*FixResult, error)
FixValidationErrors runs terraform validate in a loop, parsing error diagnostics to auto-fix attributes the provider says are invalid.
Supported fix strategies:
- "remove it" errors: remove the offending attribute
- "must be 'VALUE'" errors: set the attribute to the required value
- "must be one of [...]" errors: remove the attribute with the invalid value
- "conflicts with" errors: remove the attribute pointed to by the diagnostic
- "required null" errors: sensitive attrs get variable references, non-sensitive get ""
The schema parameter enables sensitive-aware handling of required nulls. If nil, all required nulls are replaced with variable references.
Returns a FixResult with the count and any required variables, or an error.
type LabelComposer ¶ added in v0.4.0
LabelComposer derives the pre-sanitization friendly name for a resource block. resourceType is the TF type; body is the resource block body; idLookup returns the import-block ID for this resource (for name fallback). Returning "" skips renaming this resource. The returned name is sanitized and de-duplicated by the caller's naming.Tracker.
type ProcessOptions ¶
type ProcessOptions struct {
// TypeToFileMap maps TF resource type → output filename. Required.
TypeToFileMap map[string]string
// Rules defines the cross-resource reference rewriting rules.
Rules []ReferenceRule
// PackageFiles maps package filename -> relative path for downloaded packages.
PackageFiles map[string]string
// PackageInfo maps package_name -> filename from discovery.
PackageInfo map[string]string
// PlatformPackageFiles maps a jamfplatform_pro_package file_name -> relative
// path for files downloaded from JCDS. When a package's file_name is present,
// post-processing switches it to upload mode: package_file_source = file(...)
// and the conflicting server-supplied hash attributes are removed.
PlatformPackageFiles map[string]string
// IconURLs maps icon Jamf ID -> CDN URL for icon_file_web_source.
IconURLs map[string]string
// EnrollmentCustomizationImageFiles maps enrollment customization Jamf ID -> relative path.
EnrollmentCustomizationImageFiles map[string]string
// ExtractSpecs declaratively drives string-attribute extraction to support
// files (scripts, profile payloads, app configs). Used by providers whose
// resources expose these as nested object attributes (e.g. jamfplatform).
// The jamfpro pipeline uses dedicated inline extraction instead.
ExtractSpecs []ExtractSpec
// SupportDirs lists extra directories (relative to support_files/) to create
// as recommended locations for user-supplied files (e.g. token directories).
SupportDirs []string
// SkipReferences disables cross-resource reference resolution, leaving raw ID values.
SkipReferences bool
// SplitByCategory splits categorised resource types into per-category output files.
SplitByCategory bool
// ProviderSchemas is the parsed provider schema from tfexec (for null stripping).
ProviderSchemas *tfjson.ProviderSchemas
// SupportFilesPrefix, when set, is inserted into support file paths:
// support_files/<prefix>/scripts/ instead of support_files/scripts/.
// Used by multi-env mode to separate files per environment.
SupportFilesPrefix string
// InjectRequiredWriteOnly wires Required WriteOnly attributes the server never
// returns to sensitive Terraform variables (and seeds their _wo_version
// companions with 1) so a generated config validates. Used by jamfplatform.
InjectRequiredWriteOnly bool
}
ProcessOptions holds configuration for post-processing.
type ProviderSchema ¶
type ProviderSchema struct {
// contains filtered or unexported fields
}
ProviderSchema holds the parsed schema information for resource types.
func LoadProviderSchema ¶
func LoadProviderSchema(schemas *tfjson.ProviderSchemas) *ProviderSchema
LoadProviderSchema builds a ProviderSchema from the typed tfjson.ProviderSchemas returned by tfexec's ProvidersSchema().
type ReferenceRule ¶
type ReferenceRule struct {
// ResourceType is the TF resource type containing the reference (e.g. "jamfpro_policy")
ResourceType string
// BlockPath is the HCL block nesting path to the attribute.
// e.g. ["payloads", "scripts"] means the "id" attr lives inside payloads { scripts { id = ... } }
BlockPath []string
// AttrPath is the nested object-attribute path to the container holding the
// target attribute, e.g. ["scope", "targets"] for scope = { targets = { computer_group_ids = ... } }.
// Walked after BlockPath. Empty for root-level attributes.
AttrPath []string
// AttrName is the attribute within the container that holds the raw ID(s)
// (e.g. "id", "computer_group_ids"). When ElementAttr is set, AttrName names
// the list-of-objects attribute and ElementAttr names the per-element field.
AttrName string
// TargetTypes are the TF resource types to look up in the registry.
// Tried in order; first match wins.
TargetTypes []string
// TargetAttr is the attribute on the target resource to reference (e.g. "id")
TargetAttr string
// Numeric indicates the source attribute is number-typed (e.g. an Int64
// profile_id) while the target attribute (TargetAttr) is a string. The
// resolved reference is wrapped in tonumber() so the assignment type-checks.
// Only honoured for single-attribute (non-list, non-element) rules.
Numeric bool
// EmbeddedIDs indicates AttrName is a free-form string that may contain one
// or more target-type ids embedded in surrounding text (e.g. device-group
// UUIDs inside a blueprint activation_conditions expression). Each resolvable
// UUID is replaced in place with a ${addr.TargetAttr} interpolation; the rest
// of the string and any unresolvable ids are left untouched.
EmbeddedIDs bool
// IsList indicates the attribute is a list of IDs rather than a single ID.
IsList bool
// ElementAttr, when set, indicates AttrName is a list-of-objects attribute
// (e.g. scripts = [ { id = "44" }, ... ]) and ElementAttr (e.g. "id") is the
// field rewritten on each element.
ElementAttr string
// DiscriminatorAttr, when set (with ElementAttr), names a sibling field on
// each list element whose value selects the target type via DiscriminatorMap.
// Used for polymorphic references where one ID field points at different
// resource types depending on a type tag (e.g. macOS onboarding items whose
// entity_id targets a policy, profile, or app per self_service_entity_type).
DiscriminatorAttr string
// DiscriminatorMap maps a DiscriminatorAttr value to the TF resource types to
// resolve the element's ID against, tried in order. An unmapped value, or one
// no listed type claims, leaves the ID untouched. More than one type is what
// a wire field written in two forms needs: a user-group "member of" criterion
// carries the target group's numeric ID on some Jamf Pro versions and its
// name on others, so both the ID- and name-keyed registry types are listed.
//
// With PrefixedIDs the keys are literal value prefixes instead of sibling
// field values, and the longest matching prefix wins.
DiscriminatorMap map[string][]string
// AllowUnresolved says a value no registry entry claims is expected here, so
// it is left exactly as it was with no TODO marker. Use it where the field
// legitimately accepts IDs from outside the export — a Jamf Security Cloud
// gateway reference takes either the tenant's own gateway or one of Jamf's
// shared gateways, and the shared ones are a Jamf-maintained catalogue with
// no resource to point at. Marking those TODO sends the reader looking for a
// broken reference that was never broken.
AllowUnresolved bool
// PrefixedIDs indicates the value is an ID carried behind a literal prefix
// that also selects the target type, e.g. UEM Connect's uem_group_id, which
// is written "computer_12" or "mobile_7" for a Jamf Pro device group.
// DiscriminatorMap maps each prefix to the resource type its tail resolves
// against; the prefix is preserved and the tail becomes a ${...} reference,
// so "computer_12" becomes "computer_${jamfplatform_device_group.x.jamf_pro_id}".
//
// Used with ElementAttr (per list element) or on its own (a single
// attribute). An unmatched prefix, or a tail that resolves to nothing,
// leaves the value untouched: unlike a bare ID reference, a prefixed value
// is still valid on its own and is not evidence of a broken export.
PrefixedIDs bool
}
ReferenceRule defines how to resolve a literal ID in a generated resource to a cross-resource Terraform reference.
Two traversal mechanisms are supported and may be combined (BlockPath is walked first, then AttrPath):
- BlockPath descends through HCL nested *blocks* (e.g. the jamfpro provider's scope { ... } / payloads { scripts { ... } }), navigating live hclwrite blocks.
- AttrPath descends through plugin-framework nested *attributes* (object expressions, e.g. the jamfplatform provider's scope = { targets = { ... } }), re-parsing object-expression bytes at each level.
type RequiredVar ¶
type RequiredVar struct {
VarName string // Terraform variable name (e.g. "smtp_server_password")
AttrPath string // original attribute path (e.g. "basic_auth_credentials.0.password")
Resource string // resource address (e.g. "jamfpro_smtp_server.settings")
Filename string // source file (e.g. "smtp_server.tf")
// VarType is the declared Terraform type. Empty means string, which is
// what a write-only secret wants. A collection attribute has to declare
// its own type or the config will not accept a list value.
VarType string
// NotSensitive suppresses `sensitive = true`. A list of approver email
// addresses is a required value but not a secret, and marking it sensitive
// only makes it harder to see what a plan is doing.
NotSensitive bool
// Cause names why the value has to be supplied here and becomes the
// variable's description. Empty falls back to the write-only wording,
// which is what an unreadable Required secret means. An attribute the
// server returned empty is a different problem, and a description that
// calls it a write-only secret sends the operator looking for a value the
// API is hiding rather than one the tenant never set.
Cause string
}
RequiredVar describes a write-only variable that was created to replace a required null attribute. The caller should flag these to the user.
type TokenVar ¶
type TokenVar struct {
VarName string // e.g. "dep_token_path_my_enrollment"
Description string // e.g. "Path to DEP token file (.p7m) for 'My Enrollment'"
}
extractTokenToFile extracts a token attribute (e.g. encoded_token, service_token) from a resource block, writes it to a file in the given directory, and replaces the attribute with a file() function call. If the token value is empty/null, a placeholder file() reference is created for the user to fill in. TokenVar describes a variable generated for a token file path.