This file provides guidance to AI agents (Claude Code, Cursor, Copilot, etc.) when working with code in this repository.
# Build and install
make build # Compile provider to $GOPATH/bin
make install # Build and install to local Terraform plugins directory
make uninstall # Remove from local plugins directory
# Code quality
make fmt # Format code with gofumpt
make fmtcheck # Verify code formatting
make lint # Run golangci-lint (1h timeout)
# Testing
make test # Run unit tests (30s timeout, 4 parallel)
make testacc # Run acceptance tests (120min timeout, requires credentials)
# Run specific test suite
TEST_SUITE=tf_acc_sysdig_monitor make testacc
TEST_SUITE=tf_acc_sysdig_secure make testacc
TEST_SUITE=tf_acc_ibm_monitor make testacc
TEST_SUITE=tf_acc_ibm_secure make testacc
# Run a single test
go test ./sysdig -v -run TestAccResourceSysdigUser -tags=tf_acc_sysdig_secure -timeout 120m
# Run with debug logging
TF_ACC=1 TF_LOG=DEBUG go test ./sysdig -v -tags=tf_acc_sysdig_secure -run TestAccMacroFor acceptance tests (set in .envrc or .env):
SYSDIG_SECURE_API_TOKEN # Required for Secure tests
SYSDIG_MONITOR_API_TOKEN # Required for Monitor tests
TF_ACC=1 # Enable acceptance testing
# IBM Cloud (optional)
SYSDIG_IBM_MONITOR_API_KEY
SYSDIG_IBM_MONITOR_INSTANCE_ID
SYSDIG_IBM_SECURE_API_KEY
SYSDIG_IBM_SECURE_INSTANCE_IDTerraform provider for Sysdig Monitor and Sysdig Secure using Terraform Plugin SDK v2.
sysdig/- Main provider package (resources, data sources, provider config)sysdig/internal/client/v2/- HTTP client layer for Sysdig APIwebsite/docs/- Terraform Registry documentation
- Resources:
sysdig/resource_sysdig_<service>_<entity>.go - Data sources:
sysdig/data_source_sysdig_<service>_<entity>.go - Tests:
sysdig/<source>_test.go(same package, co-located) - Client implementations:
sysdig/internal/client/v2/<entity>.go
When implementing new resources, reference these files for patterns and utilities:
| File | Purpose |
|---|---|
sysdig/common.go |
~60 predefined Schema key constants (SchemaIDKey, SchemaTeamIDKey, etc.) |
sysdig/schema.go |
Schema composition helpers, read-only schemas, maps.Copy() pattern |
sysdig/helpers.go |
Utility functions (validateDiagFunc, parseAzureCreds) |
sysdig/sysdig_clients.go |
Client interface and selection logic |
sysdig/resource_sysdig_secure_common_policy.go |
Secure policy patterns |
sysdig/resource_sysdig_monitor_alert_v2_common.go |
Alert V2 base implementation |
sysdig/resource_sysdig_monitor_notification_channel_common.go |
Notification channel patterns |
Resources use composable schema functions for shared fields:
// Alert resources compose multiple schema layers
Schema: createScopedSegmentedAlertV2Schema(
createAlertV2Schema(
map[string]*schema.Schema{
"type_specific_field": { ... }
}
)
)
// Notification channels use shared base schema
Schema: createMonitorNotificationChannelSchema(
map[string]*schema.Schema{
"channel_specific_field": { ... }
}
)Base schema functions (in sysdig/):
createAlertV2Schema()- Alert common fieldscreateScopedSegmentedAlertV2Schema()- Scope and group_bycreateRuleSchema()- Secure rule common fieldscreateMonitorNotificationChannelSchema()- Notification channel basecreateSecureNotificationChannelSchema()- Secure notification base
func resourceSysdigXxxCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client, err := getXxxClient(meta.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}
obj := buildXxxStruct(d) // ResourceData → Go struct
created, err := client.CreateXxx(ctx, obj)
if err != nil {
return diag.FromErr(err)
}
d.SetId(strconv.Itoa(created.ID))
return updateXxxState(d, &created) // Go struct → ResourceData
}Two-function pattern for bidirectional mapping:
buildXxxStruct(d *schema.ResourceData)- Terraform state → Go structupdateXxxState(d *schema.ResourceData, obj *Type)- Go struct → Terraform state
All Update operations must include version for conflict detection:
obj.Version = d.Get("version").(int) // Critical for updatesHandle "not found" gracefully in Read operations. When a resource no longer exists in the API, remove it from Terraform state instead of erroring (allows terraform apply to recreate it):
// Pattern 1: Using typed errors
if err == v2.ErrAlertV2NotFound {
d.SetId("") // Remove from state
return nil // Don't error
}
// Pattern 2: Using HTTP status codes
if statusCode == http.StatusNotFound {
d.SetId("")
return nil
}Standard timeout configuration for CRUD operations:
timeout := 5 * time.Minute
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(timeout),
Update: schema.DefaultTimeout(timeout),
Read: schema.DefaultTimeout(timeout),
Delete: schema.DefaultTimeout(timeout),
}Use GetOk() pattern for optional fields to distinguish between unset and zero values:
if val, ok := d.GetOk("optional_field"); ok {
obj.OptionalField = val.(string) // Only set if explicitly configured
}Mark fields as deprecated with clear migration guidance:
"old_field": {
Type: schema.TypeBool,
Optional: true,
Deprecated: "Use 'new_field' in the alert resource instead. This will be removed in a future version.",
}Use helper functions from sysdig/schema.go for consistent read-only fields:
"computed_field": ReadOnlyIntSchema(), // Computed int field
"status": ReadOnlyStringSchema(), // Computed string field
"enabled": BoolSchema(), // Optional bool with default false
"active": BoolComputedSchema(), // Computed boolCommon interface (users, teams, notification channels, agent access keys)
SysdigCommon interface
├── Common
├── CustomRoleInterface, CustomRolePermissionInterface
├── GroupMappingInterface, GroupMappingConfigInterface
├── IPFiltersInterface, IPFilteringSettingsInterface
└── TeamServiceAccountInterface
MonitorCommon interface (alerts, alertsV2, dashboards, silence rules, inhibition rules)
SecureCommon interface (posture policies, posture zones, posture controls, posture accept risk, zones)
SysdigMonitor interface
├── SysdigCommon
├── MonitorCommon
└── CloudAccountMonitorInterface
SysdigSecure interface
├── SysdigCommon
├── SecureCommon
├── PolicyInterface, CompositePolicyInterface
├── RuleInterface, MacroInterface, ListInterface
├── CloudauthAccountSecureInterface, CloudauthAccountComponentSecureInterface, CloudauthAccountFeatureSecureInterface
├── OnboardingSecureInterface, OrganizationSecureInterface
└── VulnerabilityPolicyClient, VulnerabilityRuleBundleClient
Provider auto-detects client type based on configured credentials:
SysdigMonitor- Direct Sysdig Monitor APISysdigSecure- Direct Sysdig Secure APIIBMMonitor- IBM Cloud Sysdig MonitorIBMSecure- IBM Cloud Sysdig Secure
Resources access API clients through the SysdigClients interface (sysdig/sysdig_clients.go):
type SysdigClients interface {
sysdigMonitorClientV2() (v2.SysdigMonitor, error)
sysdigSecureClientV2() (v2.SysdigSecure, error)
ibmMonitorClient() (v2.IBMMonitor, error)
ibmSecureClient() (v2.IBMSecure, error)
}Each resource implements a client getter function that selects the appropriate client based on configuration:
func getAlertV2Client(c SysdigClients) (v2.AlertV2Interface, error) {
var client v2.AlertV2Interface
var err error
switch c.GetClientType() {
case IBMMonitor:
client, err = c.ibmMonitorClient()
default:
client, err = c.sysdigMonitorClientV2()
}
if err != nil {
return nil, err
}
return client, nil
}- Retryable HTTP with exponential backoff (max 5 retries)
- Retries on 5xx errors and 409 Conflict
- TLS verification can be disabled via
insecure_tls - Custom headers support via
extra_headers
Sysdig (Direct): Bearer token in Authorization header IBM Cloud: IAM token exchange with caching and auto-refresh
Tests are organized by build tags:
unit- Fast unit teststf_acc_sysdig_monitor- Monitor acceptance teststf_acc_sysdig_secure- Secure acceptance teststf_acc_ibm_monitor- IBM Monitor teststf_acc_ibm_secure- IBM Secure teststf_acc_onprem_monitor,tf_acc_onprem_secure- On-prem tests
//go:build tf_acc_sysdig_secure
func TestAccResourceXxx(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
PreCheck: preCheckAnyEnv(t, SysdigSecureApiTokenEnv),
ProviderFactories: map[string]func() (*schema.Provider, error){
"sysdig": func() (*schema.Provider, error) {
return sysdig.Provider(), nil
},
},
Steps: []resource.TestStep{
{
Config: testConfig(randomName()),
},
{
ResourceName: "sysdig_secure_xxx.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}preCheckAnyEnv(t, envs...)- Validate environment variablesrandomText(len)- Generate random strings for unique namessysdigOrIBMMonitorPreCheck(t)- Check for either credential type
Follow the Red-Green-Refactor cycle:
-
Red: Write acceptance test first (it will fail)
- Create test file with appropriate build tag
- Define test cases covering create, update, import
- Run test to confirm it fails:
go test ./sysdig -v -run TestAccNewResource -tags=tf_acc_sysdig_secure
-
Green: Write minimum code to make test pass
- Implement resource schema and CRUD operations
- Add client interface if new API endpoints needed
- Register in
provider.go - Run test until it passes
-
Refactor: Clean up if needed
- Extract common patterns to shared functions
- Ensure code follows existing conventions
- Run
make fmtandmake lint
Prefer small commits - commit after each complete Red-Green-Refactor cycle. Each commit should represent a single, atomic, working change. This makes code review easier and keeps a clean git history.
- Write acceptance test first in
sysdig/resource_sysdig_<service>_<name>_test.go - Create resource file:
sysdig/resource_sysdig_<service>_<name>.go - Implement schema using composition pattern with existing base schemas
- Implement CRUD:
CreateContext,ReadContext,UpdateContext,DeleteContext - Add client interface in
sysdig/internal/client/v2/if new API endpoints needed - Register in provider: Add to
ResourcesMapinsysdig/provider.go - Run tests until green: Iterate until all tests pass
- Add documentation in
website/docs/r/<name>.md
Documentation files in website/docs/ follow a standard structure:
---
subcategory: "Sysdig Secure" # or "Sysdig Monitor"
layout: "sysdig"
page_title: "Sysdig: sysdig_secure_<name>"
description: |-
Short description of the resource.
---
# Resource: sysdig_secure_<name>
Longer description explaining what the resource does.
## Example Usage
\`\`\`terraform
resource "sysdig_secure_<name>" "example" {
name = "example"
# ... required and common optional fields
}
\`\`\`
## Argument Reference
### Common Arguments
* `name` - (Required) The name of the resource.
### Type-Specific Arguments
* `specific_field` - (Optional) Description. Default: `value`.
### Nested Schema for `block_name`
* `nested_field` - (Required) Description.
## Attributes Reference
In addition to all arguments above, the following attributes are exported:
* `id` - The ID of the resource.
* `version` - The current version of the resource.
## Import
Resource can be imported using the ID:
\`\`\`
$ terraform import sysdig_secure_<name>.example 12345
\`\`\`Same structure but without Import section and with read-only field descriptions.
- Multi-architecture build (darwin, linux, windows, freebsd, openbsd, solaris)
- golangci-lint (30min timeout in CI, 1h timeout in local GNUmakefile)
- Unit tests
- Acceptance tests (Monitor, Secure, IBM suites in parallel)
- Provider documentation validation (tfproviderdocs)
- CodeQL security analysis
- Tag with
v*pattern triggers release - GoReleaser builds multi-platform binaries
- GPG signing of checksums
- Automatic GitHub Release creation
- Use Conventional Commit format
- Example:
feat(secure-policy): Add runbook to policy resources
- Example:
- Update CODEOWNERS for new resource areas
- Include acceptance tests
- Update documentation in
website/docs/