The management of time within Infrastructure as Code (IaC) is a critical dimension of stability, particularly when dealing with complex cloud environments or hardware-defined networking resources. In the Pulumi ecosystem, timing manifests in two distinct forms: the operational timeouts governing the lifecycle of a resource (create, update, and delete) and the logical timeouts configured as properties within a specific resource to govern network behavior. Understanding the intersection of the customTimeouts resource option and the scm.SessionTimeout resource is essential for engineers aiming to prevent deployment failures and optimize network session persistence.
The CustomTimeouts Resource Option
The customTimeouts resource option is a foundational mechanism provided by the Pulumi base resource-options type. This option allows a developer to override the default amount of time Pulumi will wait for a specific infrastructure operation to reach a terminal state.
In a standard deployment, Pulumi automatically manages the wait time for operations to complete. However, infrastructure providers may occasionally exhibit bugs, or certain resources—such as large database clusters or complex virtual appliances—may require significantly longer than the provider's default duration to provision or decommission.
Functional Application of Custom Timeouts
Custom timeouts are applied specifically to three primary lifecycle operations:
- Create: The duration Pulumi waits for a new resource to be fully provisioned and reach a "ready" state.
- Update: The duration Pulumi waits for a modification to a resource to be applied and confirmed.
- Delete: The duration Pulumi waits for a resource to be completely removed from the infrastructure provider.
The impact of implementing these timeouts is primarily felt during the "waiting" phase of a pulumi up or pulumi destroy command. Without a custom timeout, a resource that takes 40 minutes to create might trigger a default timeout error at 30 minutes, causing the entire deployment to fail and leaving the infrastructure in a partially provisioned state. By extending the create timeout to 60 minutes, the engineer ensures the deployment process is resilient to the slow provisioning speeds of the underlying provider.
Duration Specification and Syntax
Pulumi utilizes a standardized duration string format to define these intervals. This ensures consistency across different language SDKs. The supported duration units include:
- ns: Nanoseconds
- us or µs: Microseconds
- ms: Milliseconds
- s: Seconds
- m: Minutes
- h: Hours
For example, a timeout specified as 30m equates to thirty minutes, while 12h equates to twelve hours.
Language-Specific Implementations of CustomTimeouts
Depending on the SDK being used, the syntax for implementing customTimeouts varies, although the underlying logic remains identical.
TypeScript/JavaScript
In TypeScript, customTimeouts is passed as part of the third argument (the options object) to the resource constructor.
typescript
let db = new Database("db", {/*...*/},
{ customTimeouts: { create: "30m" } });
Python
Python utilizes the ResourceOptions class and the CustomTimeouts helper class to define the window.
python
db = Database('db',
opts=ResourceOptions(custom_timeouts=CustomTimeouts(create='30m')))
Go
In Go, the pulumi.Timeouts function is used to pass a CustomTimeouts struct.
go
db, err := NewDatabase(ctx, "db", &DatabaseArgs{ /*...*/ },
pulumi.Timeouts(&pulumi.CustomTimeouts{Create: "30m"}))
C# (.NET)
C# employs the CustomResourceOptions class and the TimeSpan object for precise duration control.
csharp
var db = new Database("db", new DatabaseArgs(),
new CustomResourceOptions {
CustomTimeouts = new CustomTimeouts { Create = TimeSpan.FromMinutes(30) }
});
Java
Java uses a builder pattern within the CustomResourceOptions to set the duration.
java
var db = new Database("db",
DatabaseArgs.Empty,
CustomResourceOptions.builder()
.customTimeouts(
CustomTimeouts.builder()
.create(Duration.ofMinutes(30))
.build())
.build());
YAML
For those using Pulumi's YAML configuration, the options are defined under a dedicated options key.
yaml
resources:
db:
type: Database
options:
customTimeouts:
create: "30m"
Critical Limitations of CustomTimeouts
It is imperative to recognize that customTimeouts is not a universal solution for all resource types.
- Component Resources: Passing
customTimeoutsto a component resource has no direct effect at runtime. Because component resources are logical groupings of other resources rather than physical entities in a provider, the base resource-options type allows the syntax at compile time to avoid SDK errors, but the logic is ignored. - Provider Support: Support for
customTimeoutsis dependent on the specific resource's implementation within its provider. If a provider has not implemented the logic to handle these overrides, the specified timeout will be ignored.
The Scm SessionTimeout Resource
While customTimeouts manages the deployment lifecycle, the scm.SessionTimeout resource is used to configure the actual operational behavior of network sessions within the SCM provider (typically associated with Next-Generation Firewalls or similar network security appliances). This resource defines how long various types of network traffic should be maintained in the session table before being aged out.
Core Configuration Properties
The scm.SessionTimeout resource is defined by several key attributes that determine its scope and its specific timing parameters.
Primary Resource Attributes
- Device: Specifies the target hardware or virtual device where the session timeouts are applied.
- Folder: Defines the logical grouping or folder (e.g.,
ngfw-shared) where the configuration resides. This is critical for organizing shared configurations across multiple security zones. - Snippet: An optional string used for providing custom configuration fragments or metadata.
SessionTimeoutSessionTimeoutsArgs (The Timing Matrix)
The heart of the scm.SessionTimeout resource is the sessionTimeouts block. Each parameter here represents a different protocol or session state, measured in seconds.
| Parameter | Description | Example Value |
|---|---|---|
| TimeoutDefault | The fallback timeout for any session that does not match a more specific rule. | 60 |
| TimeoutDiscardDefault | The timeout for packets that are explicitly discarded by the security policy. | 60 |
| TimeoutDiscardTcp | Specifically targets TCP packets that are dropped. | 90 |
| TimeoutDiscardUdp | Specifically targets UDP packets that are dropped. | 60 |
| TimeoutIcmp | Governs the duration of ICMP (Internet Control Message Protocol) sessions. | 6 |
| TimeoutScan | Controls the timeout for detected port scans or reconnaissance traffic. | 10 |
| TimeoutTcp | The standard timeout for established TCP connections. | 3600 |
| TimeoutTcphandshake | The window allowed for the TCP three-way handshake to complete. | 10 |
| TimeoutTcpinit | The timeout for the initial TCP connection attempt. | 5 |
| TimeoutTcpHalfClosed | Duration for sessions where one end has closed the connection (FIN). | 120 |
| TimeoutTcpTimeWait | The time a connection remains in the TIME_WAIT state. | 15 |
| TimeoutTcpUnverifiedRst | Timeout for TCP Reset packets that cannot be verified. | 30 |
| TimeoutUdp | The timeout for standard UDP datagram sessions. | 30 |
| TimeoutCaptivePortal | The timeout for sessions interacting with a captive portal authentication page. | 30 |
Multi-Language Implementation of Scm SessionTimeout
Implementing session timeouts requires a precise mapping of the SessionTimeoutSessionTimeoutsArgs to the desired network policy.
Go Implementation
The Go SDK utilizes pointers and specific argument structs to ensure type safety.
go
package main
import (
"github.com/pulumi/pulumi-scm/sdk/go/scm"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := scm.NewSessionTimeout(ctx, "st_example", &scm.SessionTimeoutArgs{
Folder: pulumi.String("ngfw-shared"),
SessionTimeouts: &scm.SessionTimeoutSessionTimeoutsArgs{
TimeoutDefault: pulumi.Int(60),
TimeoutDiscardDefault: pulumi.Int(60),
TimeoutDiscardTcp: pulumi.Int(90),
TimeoutDiscardUdp: pulumi.Int(60),
TimeoutIcmp: pulumi.Int(6),
TimeoutScan: pulumi.Int(10),
TimeoutTcp: pulumi.Int(3600),
TimeoutTcphandshake: pulumi.Int(10),
TimeoutTcpinit: pulumi.Int(5),
TimeoutTcpHalfClosed: pulumi.Int(120),
TimeoutTcpTimeWait: pulumi.Int(15),
TimeoutTcpUnverifiedRst: pulumi.Int(30),
TimeoutUdp: pulumi.Int(30),
TimeoutCaptivePortal: pulumi.Int(30),
},
})
if err != nil {
return err
}
return nil
})
}
TypeScript Implementation
TypeScript provides a clean object-literal syntax for defining these timeouts.
typescript
import * as pulumi from "@pulumi/pulumi";
import * as scm from "@pulumi/scm";
const stExample = new scm.SessionTimeout("st_example", {
folder: "ngfw-shared",
sessionTimeouts: {
timeoutDefault: 60,
timeoutDiscardDefault: 60,
timeoutDiscardTcp: 90,
timeoutDiscardUdp: 60,
timeoutIcmp: 6,
timeoutScan: 10,
timeoutTcp: 3600,
timeoutTcphandshake: 10,
timeoutTcpinit: 5,
timeoutTcpHalfClosed: 120,
timeoutTcpTimeWait: 15,
timeoutTcpUnverifiedRst: 30,
timeoutUdp: 30,
timeoutCaptivePortal: 30,
},
});
Python Implementation
Python utilizes keyword arguments, making the configuration highly readable.
python
import pulumi
import pulumi_scm as scm
st_example = scm.SessionTimeout("st_example",
folder="ngfw-shared",
session_timeouts={
"timeout_default": 60,
"timeout_discard_default": 60,
"timeout_discard_tcp": 90,
"timeout_discard_udp": 60,
"timeout_icmp": 6,
"timeout_scan": 10,
"timeout_tcp": 3600,
"timeout_tcphandshake": 10,
"timeout_tcpinit": 5,
"timeout_tcp_half_closed": 120,
"timeout_tcp_time_wait": 15,
"timeout_tcp_unverified_rst": 30,
"timeout_udp": 30,
"timeout_captive_portal": 30,
})
C# Implementation
C# uses the Scm.Inputs namespace to handle the complex argument requirements.
csharp
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Scm = Pulumi.Scm;
return await Deployment.RunAsync(() =>
{
var stExample = new Scm.SessionTimeout("st_example", new()
{
Folder = "ngfw-shared",
SessionTimeouts = new Scm.Inputs.SessionTimeoutSessionTimeoutsArgs
{
TimeoutDefault = 60,
TimeoutDiscardDefault = 60,
TimeoutDiscardTcp = 90,
TimeoutDiscardUdp = 60,
TimeoutIcmp = 6,
TimeoutScan = 10,
TimeoutTcp = 3600,
TimeoutTcphandshake = 10,
TimeoutTcpinit = 5,
TimeoutTcpHalfClosed = 120,
TimeoutTcpTimeWait = 15,
TimeoutTcpUnverifiedRst = 30,
TimeoutUdp = 30,
TimeoutCaptivePortal = 30,
},
});
});
Java Implementation
Java relies on a verbose builder pattern to construct the nested SessionTimeoutSessionTimeoutsArgs.
java
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.scm.SessionTimeout;
import com.pulumi.scm.SessionTimeoutArgs;
import com.pulumi.scm.inputs.SessionTimeoutSessionTimeoutsArgs;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var stExample = new SessionTimeout("stExample", SessionTimeoutArgs.builder()
.folder("ngfw-shared")
.sessionTimeouts(SessionTimeoutSessionTimeoutsArgs.builder()
.timeoutDefault(60)
.timeoutDiscardDefault(60)
.timeoutDiscardTcp(90)
.timeoutDiscardUdp(60)
.timeoutIcmp(6)
.timeoutScan(10)
.timeoutTcp(3600)
.timeoutTcphandshake(10)
.timeoutTcpinit(5)
.timeoutTcpHalfClosed(120)
.timeoutTcpTimeWait(15)
.timeoutTcpUnverifiedRst(30)
.timeoutUdp(30)
.timeoutCaptivePortal(30)
.build())
.build());
}
}
Terraform/HCL Implementation
Pulumi's Terraform-compatible syntax allows for a declarative definition of the session timeout resource.
hcl
resource "scm_sessiontimeout" "st_example" {
folder = "ngfw-shared"
session_timeouts = {
timeout_default = 60
timeout_discard_default = 60
timeout_discard_tcp = 90
timeout_discard_udp = 60
timeout_icmp = 6
timeout_scan = 10
timeout_tcp = 3600
timeout_tcphandshake = 10
timeout_tcpinit = 5
timeout_tcp_half_closed = 120
timeout_tcp_time_wait = 15
timeout_tcp_unverified_rst = 30
timeout_udp = 30
timeout_captive_portal = 30
}
}
Strategic Analysis of Timeout Coordination
The interplay between customTimeouts and scm.SessionTimeout represents two different layers of system engineering: the Deployment Layer and the Runtime Layer.
Deployment Layer Resilience
When applying an scm.SessionTimeout resource, the Pulumi engine communicates with the SCM provider API. If the provider is under heavy load or the security appliance requires a service restart to apply these session changes, the operation may exceed the default provider timeout. In such cases, the engineer must apply a customTimeouts option to the scm.SessionTimeout resource itself.
For instance, if applying session timeout changes requires a commit process on the firewall that takes 10 minutes, the deployment would be configured as follows in Python:
python
st_example = scm.SessionTimeout("st_example",
folder="ngfw-shared",
session_timeouts={...},
opts=ResourceOptions(custom_timeouts=CustomTimeouts(update='15m'))
)
This configuration ensures that Pulumi does not mark the update as failed prematurely, which would otherwise lead to a "drift" state where the Pulumi state file believes the update failed, but the hardware actually applied the changes.
Runtime Layer Optimization
The scm.SessionTimeout properties directly affect the memory and CPU utilization of the network appliance. Every active session consumes a slot in the session table.
- High TCP Timeouts: Setting
timeoutTcpto 3600 (1 hour) is beneficial for long-lived SSH or database connections, preventing them from being dropped during periods of inactivity. However, if too many sessions remain open, the session table may exhaust its capacity. - Aggressive ICMP/Scan Timeouts: Low values for
timeoutIcmp(6s) andtimeoutScan(10s) are security best practices. They ensure that ephemeral or malicious probes do not linger in the session table, freeing up resources for legitimate traffic. - Handshake and Init Timeouts: The tight windows for
timeoutTcphandshake(10s) andtimeoutTcpinit(5s) protect the appliance from SYN flood attacks by quickly purging half-open connections that never complete the handshake.
Conclusion
The orchestration of timing in Pulumi requires a dual-pronged approach: utilizing customTimeouts to manage the infrastructure's lifecycle and leveraging specialized resources like scm.SessionTimeout to dictate runtime network behavior. The customTimeouts option is a powerful tool for overcoming provider-side instability and slow provisioning, provided it is applied to a supported resource rather than a component resource. Conversely, the scm.SessionTimeout resource allows for granular control over the session table of a security appliance, balancing the need for connection persistence against the necessity of resource conservation and security. By aligning these two timeout mechanisms, DevOps engineers can create a deployment pipeline that is both resilient to infrastructure delays and optimized for high-performance network security.