Why automate SCM
Manual configuration in the SCM UI does not scale when managing:
- Multiple environments (dev, staging, production)
- Mixed deployments (on-prem NGFWs, VM-Series in Azure/AWS/GCP, Cloud NGFW)
- Frequent or repetitive changes
- Requirements for consistency, auditability, and speed
Automation treats configuration as code: versioned, reviewable, and repeatable.
Automation options
| Option | Best for | Notes |
|---|---|---|
| Optionscm-go | Best forCustom Go scripts, internal tools, CI jobs | NotesPalo Alto Networks’ Go SDK. Auto-generated and community supported. Full control over API calls. [1], [2] |
OptionTerraform provider (scm) | Best forDeclarative infrastructure-as-code | NotesBuilt on scm-go. Provides state, plans, and team workflows. [4], [5] |
OptionPython: scm-python | Best forPython environments, lightweight scripts | NotesPalo Alto Networks’ auto-generated Python SDK. Currently a beta pre-release that its README says is not ready for production use. [6] |
OptionPython: pan-scm-sdk | Best forExisting scripts that already use it | NotesCommunity SDK with Pydantic models. Its repository now asks users to use the official SDK from Palo Alto instead. [7] |
| OptionAnsible modules | Best forExisting Ansible estates | NotesAvailable; check module coverage for the resources you need before choosing it. |
| OptionDirect REST API | Best forCases not covered by higher-level tools | NotesMaximum flexibility; more boilerplate required. [8] |
scm-go is the foundational Go client. Higher-level tools such as the Terraform provider use it underneath. [5]
Scope of automation
The SDK covers configuration managed through Strata Cloud Manager, including: [9]
- Objects: addresses, address groups, services, tags, applications, EDLs, HIP objects/profiles, schedules
- Security: security rules, decryption rules/profiles, anti-spyware, vulnerability protection, URL filtering, file blocking
- Network: interfaces, zones, NAT rules, PBF, IKE/IPsec, BGP, OSPF, SD-WAN, zone protection
- Setup: folders, snippets, devices, variables, labels
- Deployment, identity, mobile agent, and ZTNA Connector resources
Because the SDK manages configuration held in SCM, it applies to firewalls and deployments that SCM manages, such as NGFWs and VM-Series firewalls onboarded to SCM and Cloud NGFW resources that use SCM for policy management. Confirm SCM support for each firewall type and deployment before automating it.
Authentication and client setup
Credentials are obtained from a service account in SCM (client ID, client secret, TSG scope).
Configuration resolution order in scm-go: explicit struct fields → environment variables (only when CheckEnvironment is true) → scm-config.json → built-in defaults, such as the host api.strata.paloaltonetworks.com. [3]
Example scm-config.json (placeholder values):
{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
"host": "api.strata.paloaltonetworks.com",
"protocol": "https",
"scope": "tsg_id:<your-tsg-id>",
"logging": "quiet",
"skip_verify_certificate": false
}
Reusable authentication and client initialization:
package main
import (
"context"
scm "github.com/paloaltonetworks/scm-go"
"github.com/paloaltonetworks/scm-go/generated/objects"
)
// configPath points to your scm-config.json, for example /path/to/scm-config.json.
func NewObjectsClient(ctx context.Context, configPath string) (*objects.APIClient, error) {
client := &scm.Client{
AuthFile: configPath,
CheckEnvironment: false,
}
if err := client.Setup(); err != nil {
return nil, err
}
if err := client.RefreshJwt(ctx); err != nil {
return nil, err
}
cfg := objects.NewConfiguration()
cfg.Host = client.GetHost()
cfg.Scheme = "https"
cfg.HTTPClient = client.HttpClient
cfg.DefaultHeader = map[string]string{
"Authorization": "Bearer " + client.Jwt,
"x-auth-jwt": client.Jwt,
}
return objects.NewAPIClient(cfg), nil
}
The same pattern applies to other packages (security_services, network_services, etc.). The scm-go README uses common.GetConfigPath() in its example; that helper lives in the repository’s test utilities and resolves to the repository’s own config/scm-config.json, so pass an explicit path in your own tools. [1]
Core CRUD patterns
The examples below are illustrative. Names and the 192.0.2.10 address (a reserved documentation range) are examples only. Method and field names match scm-go v1.0.13; the generated API can change between releases, so check the package you are using.
Helper:
func strPtr(s string) *string { return &s }
Create address object
req := api.AddressesAPI.CreateAddresses(ctx)
body := objects.Addresses{
Name: "web-server-01",
Folder: strPtr("Shared"),
IpNetmask: strPtr("192.0.2.10/32"), // documentation-range example (RFC 5737)
Description: strPtr("web tier server"),
}
created, resp, err := req.Addresses(body).Execute()
if err != nil {
// Inspect resp.StatusCode and response body for details
return err
}
// created.Id contains the new object ID
List addresses (for idempotency checks)
list, _, err := api.AddressesAPI.ListAddresses(ctx).Folder("Shared").Execute()
if err != nil {
return err
}
for _, addr := range list.Data {
// addr.Name, addr.Id, addr.IpNetmask, etc.
}
Update address
existing, _, err := api.AddressesAPI.GetAddressesByID(ctx, id).Execute()
if err != nil {
return err
}
existing.Description = strPtr("updated description")
_, _, err = api.AddressesAPI.UpdateAddressesByID(ctx, id).Addresses(*existing).Execute()
Delete address
_, err = api.AddressesAPI.DeleteAddressesByID(ctx, id).Execute()
Create tag
tagReq := api.TagsAPI.CreateTags(ctx)
tagBody := objects.Tags{
Name: "env-prod",
Folder: strPtr("Shared"),
Color: strPtr("Red"),
}
_, _, err = tagReq.Tags(tagBody).Execute()
Create security rule
import sec "github.com/paloaltonetworks/scm-go/generated/security_services"
// Initialize secApi the same way as the objects client (inject JWT)
ruleReq := secApi.SecurityRulesAPI.CreateSecurityRules(ctx).Position("pre") // position is required
ruleBody := sec.SecurityRules{
Name: strPtr("allow-web-to-app"),
Folder: strPtr("Shared"),
From: []string{"trust"},
To: []string{"untrust"},
Source: []string{"any"},
Destination: []string{"web-server-01"},
Application: []string{"ssl", "web-browsing"},
Service: []string{"application-default"},
Action: strPtr("allow"),
Description: strPtr("allow web traffic to application servers"),
}
createdRule, _, err := ruleReq.SecurityRules(ruleBody).Execute()
Create NAT rule (network_services package)
import netsec "github.com/paloaltonetworks/scm-go/generated/network_services"
// Initialize netApi similarly
natReq := netApi.NATRulesAPI.CreateNatRules(ctx).Position("pre") // position is required
natBody := netsec.NatRules{
Name: "web-inbound-nat",
Folder: strPtr("Shared"),
From: []string{"untrust"},
To: []string{"untrust"},
Source: []string{"any"},
Destination: []string{"any"},
Service: "service-http",
// Destination translation fields depend on the exact model version
}
_, _, err = natReq.NatRules(natBody).Execute()
JWT token caching
The SCM authentication API has rate limits on token requests (approximately 10 concurrent requests per tenant, according to the scm-go README). [1] For parallel or frequent operations, cache the JWT in the configuration file. scm-go reads these fields from scm-config.json starting with version 1.0.8: [1], [3]
{
"client_id": "<your-client-id>",
"client_secret": "<your-client-secret>",
"scope": "tsg_id:<your-tsg-id>",
"jwt": "<cached-jwt>",
"jwt_expires_at": "2026-10-28T02:00:00Z",
"jwt_lifetime": 900
}
A background process can refresh the token every 10–12 minutes and write it atomically. Client instances then read the cached token and avoid additional authentication calls. [1] Note that RefreshJwt always requests a new token, so a client that loaded a cached token should skip RefreshJwt while that token is still valid. [3]
Only share tokens among clients that use the same client ID and secret.
Common use cases
| Use case | Implementation approach |
|---|---|
| Use caseApplication onboarding | Implementation approachCreate address objects, tags, and security rules in sequence |
| Use caseEmergency block | Implementation approachCreate a high-priority deny rule; delete or disable it after the incident |
| Use caseEnvironment promotion | Implementation approachList objects/rules from a source folder and recreate them in a target folder |
| Use caseBulk standardization | Implementation approachList resources, filter by criteria, and issue update calls |
| Use caseMulti-cloud consistency | Implementation approachApply the same object and rule definitions across folders associated with different device types |
| Use caseCI/CD integration | Implementation approachTrigger Go or Terraform runs on merge or approval |
| Use caseDrift detection | Implementation approachPeriodically list configuration and compare against a source of truth |
Implementation notes
- Configuration objects are scoped to a location:
Folder,Snippet, orDevice. - Prefer list-then-create/update patterns for idempotency.
- Generated clients return the HTTP response; check status codes and parse error bodies on failure.
- The SDK is auto-generated and community-supported. Validate behavior in a non-production tenant. [1], [2]
- For large-scale declarative management, the Terraform provider is often a better fit than imperative scripts.
- ZTNA Connector calls require the region set in the
x_panw_regionconfiguration field, which scm-go sends as thex-panw-regionheader (americas,europe, orapac). [1]
Generated package overview
| Package | Primary resources |
|---|---|
Packageobjects | Primary resourcesAddresses, groups, services, tags, applications, EDLs, HIP, schedules |
Packagesecurity_services | Primary resourcesSecurity rules, profiles (anti-spyware, URL, file blocking, decryption, etc.) |
Packagenetwork_services | Primary resourcesInterfaces, zones, NAT, PBF, IKE/IPsec, BGP, SD-WAN |
Packageconfig_setup | Primary resourcesFolders, snippets, devices, variables, labels |
Packagedeployment_services | Primary resourcesRemote networks, bandwidth, service connections |
Packageztna_connector_all | Primary resourcesZTNA Connector configuration |
scm-go v1.0.13 also includes config_operations, device_settings, identity_services, and mobile_agent packages. [9]
References
- Palo Alto Networks, scm-go repository and README. https://github.com/PaloAltoNetworks/scm-go
- Palo Alto Networks, scm-go support policy (community supported). https://github.com/PaloAltoNetworks/scm-go/blob/main/SUPPORT.md
- Palo Alto Networks, scm-go
client.go(configuration loading and JWT handling). https://github.com/PaloAltoNetworks/scm-go/blob/main/client.go - Terraform Registry, PaloAltoNetworks/scm provider. https://registry.terraform.io/providers/PaloAltoNetworks/scm/latest
- Palo Alto Networks, terraform-provider-scm
go.mod(depends on scm-go). https://github.com/PaloAltoNetworks/terraform-provider-scm/blob/main/go.mod - Palo Alto Networks, scm-python repository and README. https://github.com/PaloAltoNetworks/scm-python
- pan-scm-sdk repository. https://github.com/cdot65/pan-scm-sdk
- Palo Alto Networks, Strata Cloud Manager API documentation. https://pan.dev/scm/docs/home/
- Palo Alto Networks, scm-go generated packages. https://github.com/PaloAltoNetworks/scm-go/tree/main/generated