Deploy GCP Chronicle Security Rules

The gcp:chronicle/ruleDeployment:RuleDeployment resource, part of the Pulumi GCP provider, controls the deployment state of a Chronicle detection rule: whether it runs against incoming data, generates alerts, and how frequently it executes. This guide focuses on three capabilities: enabling and disabling rule execution, configuring alerting behavior, and setting execution frequency.

Rule deployments reference Chronicle Rule resources that define the detection logic. The examples are intentionally small. Combine them with your own Chronicle rules and instance configuration.

Deploy a rule with alerting and scheduled execution

Security teams deploy detection rules to continuously monitor events and generate alerts when suspicious patterns emerge.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const my_rule = new gcp.chronicle.Rule("my-rule", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    text: "rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n",
});
const example = new gcp.chronicle.RuleDeployment("example", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    rule: pulumi.all([std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }), std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }).then(invoke => invoke.result).length]).apply(([invoke, length]) => invoke.result[length - 1]),
    enabled: true,
    alerting: true,
    archived: false,
    runFrequency: "DAILY",
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

my_rule = gcp.chronicle.Rule("my-rule",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    text="rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n")
example = gcp.chronicle.RuleDeployment("example",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    rule=len(std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result).apply(lambda length: std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result[length - 1]),
    enabled=True,
    alerting=True,
    archived=False,
    run_frequency="DAILY")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/chronicle"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := chronicle.NewRule(ctx, "my-rule", &chronicle.RuleArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Text: pulumi.String("rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n"),
})
if err != nil {
return err
}
invokeSplit, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
invokeSplit1, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
_, err = chronicle.NewRuleDeployment(ctx, "example", &chronicle.RuleDeploymentArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Rule: pulumi.String(len(invokeSplit.Result).ApplyT(func(length int) (pulumi.Any, error) {
%!v(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)).(pulumi.AnyOutput)),
Enabled: pulumi.Bool(true),
Alerting: pulumi.Bool(true),
Archived: pulumi.Bool(false),
RunFrequency: pulumi.String("DAILY"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var my_rule = new Gcp.Chronicle.Rule("my-rule", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Text = @"rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
",
    });

    var example = new Gcp.Chronicle.RuleDeployment("example", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Rule = Output.Tuple(Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }), Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }).Apply(invoke => invoke.Result).Length).Apply(values =>
        {
            var invoke = values.Item1;
            var length = values.Item2;
            return invoke.Result[length - 1];
        }),
        Enabled = true,
        Alerting = true,
        Archived = false,
        RunFrequency = "DAILY",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.chronicle.Rule;
import com.pulumi.gcp.chronicle.RuleArgs;
import com.pulumi.gcp.chronicle.RuleDeployment;
import com.pulumi.gcp.chronicle.RuleDeploymentArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.SplitArgs;
import java.util.List;
import java.util.ArrayList;
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 my_rule = new Rule("my-rule", RuleArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .text("""
rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
            """)
            .build());

        var example = new RuleDeployment("example", RuleDeploymentArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .rule(StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result().length().applyValue(_length -> StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result()[_length - 1]))
            .enabled(true)
            .alerting(true)
            .archived(false)
            .runFrequency("DAILY")
            .build());

    }
}

When enabled is true, the rule runs continuously against incoming data. The alerting property determines whether detections become alerts; the runFrequency property controls execution schedule (LIVE for real-time, DAILY for batch). The archived property must be false for active deployments. The rule property references the Chronicle Rule resource by extracting its ID from the resource name.

Disable a rule while maintaining its configuration

During incident response or rule tuning, teams often need to temporarily stop a rule from executing without deleting its deployment.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const my_rule = new gcp.chronicle.Rule("my-rule", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    text: "rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n",
});
const example = new gcp.chronicle.RuleDeployment("example", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    rule: pulumi.all([std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }), std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }).then(invoke => invoke.result).length]).apply(([invoke, length]) => invoke.result[length - 1]),
    enabled: false,
    runFrequency: "LIVE",
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

my_rule = gcp.chronicle.Rule("my-rule",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    text="rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n")
example = gcp.chronicle.RuleDeployment("example",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    rule=len(std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result).apply(lambda length: std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result[length - 1]),
    enabled=False,
    run_frequency="LIVE")
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/chronicle"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := chronicle.NewRule(ctx, "my-rule", &chronicle.RuleArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Text: pulumi.String("rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n"),
})
if err != nil {
return err
}
invokeSplit, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
invokeSplit1, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
_, err = chronicle.NewRuleDeployment(ctx, "example", &chronicle.RuleDeploymentArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Rule: pulumi.String(len(invokeSplit.Result).ApplyT(func(length int) (pulumi.Any, error) {
%!v(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)).(pulumi.AnyOutput)),
Enabled: pulumi.Bool(false),
RunFrequency: pulumi.String("LIVE"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var my_rule = new Gcp.Chronicle.Rule("my-rule", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Text = @"rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
",
    });

    var example = new Gcp.Chronicle.RuleDeployment("example", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Rule = Output.Tuple(Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }), Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }).Apply(invoke => invoke.Result).Length).Apply(values =>
        {
            var invoke = values.Item1;
            var length = values.Item2;
            return invoke.Result[length - 1];
        }),
        Enabled = false,
        RunFrequency = "LIVE",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.chronicle.Rule;
import com.pulumi.gcp.chronicle.RuleArgs;
import com.pulumi.gcp.chronicle.RuleDeployment;
import com.pulumi.gcp.chronicle.RuleDeploymentArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.SplitArgs;
import java.util.List;
import java.util.ArrayList;
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 my_rule = new Rule("my-rule", RuleArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .text("""
rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
            """)
            .build());

        var example = new RuleDeployment("example", RuleDeploymentArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .rule(StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result().length().applyValue(_length -> StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result()[_length - 1]))
            .enabled(false)
            .runFrequency("LIVE")
            .build());

    }
}

Setting enabled to false pauses rule execution while preserving all other configuration. The runFrequency remains set to LIVE, ready to resume when you re-enable the deployment. This is the first step in the two-step archive process: disable, then archive.

Deploy without specifying execution frequency

Some deployments rely on Chronicle’s default execution behavior rather than explicitly setting a schedule.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
import * as std from "@pulumi/std";

const my_rule = new gcp.chronicle.Rule("my-rule", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    text: "rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n",
});
const example = new gcp.chronicle.RuleDeployment("example", {
    location: "us",
    instance: "00000000-0000-0000-0000-000000000000",
    rule: pulumi.all([std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }), std.split({
        separator: "/",
        text: googleChronicleRule["my-rule"].name,
    }).then(invoke => invoke.result).length]).apply(([invoke, length]) => invoke.result[length - 1]),
    enabled: true,
    alerting: true,
    archived: false,
});
import pulumi
import pulumi_gcp as gcp
import pulumi_std as std

my_rule = gcp.chronicle.Rule("my-rule",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    text="rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n")
example = gcp.chronicle.RuleDeployment("example",
    location="us",
    instance="00000000-0000-0000-0000-000000000000",
    rule=len(std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result).apply(lambda length: std.split(separator="/",
        text=google_chronicle_rule["my-rule"]["name"]).result[length - 1]),
    enabled=True,
    alerting=True,
    archived=False)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/chronicle"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := chronicle.NewRule(ctx, "my-rule", &chronicle.RuleArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Text: pulumi.String("rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }\n"),
})
if err != nil {
return err
}
invokeSplit, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
invokeSplit1, err := std.Split(ctx, &std.SplitArgs{
Separator: "/",
Text: googleChronicleRule.MyRule.Name,
}, nil)
if err != nil {
return err
}
_, err = chronicle.NewRuleDeployment(ctx, "example", &chronicle.RuleDeploymentArgs{
Location: pulumi.String("us"),
Instance: pulumi.String("00000000-0000-0000-0000-000000000000"),
Rule: pulumi.String(len(invokeSplit.Result).ApplyT(func(length int) (pulumi.Any, error) {
%!v(PANIC=Format method: runtime error: invalid memory address or nil pointer dereference)).(pulumi.AnyOutput)),
Enabled: pulumi.Bool(true),
Alerting: pulumi.Bool(true),
Archived: pulumi.Bool(false),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var my_rule = new Gcp.Chronicle.Rule("my-rule", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Text = @"rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
",
    });

    var example = new Gcp.Chronicle.RuleDeployment("example", new()
    {
        Location = "us",
        Instance = "00000000-0000-0000-0000-000000000000",
        Rule = Output.Tuple(Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }), Std.Split.Invoke(new()
        {
            Separator = "/",
            Text = googleChronicleRule.My_rule.Name,
        }).Apply(invoke => invoke.Result).Length).Apply(values =>
        {
            var invoke = values.Item1;
            var length = values.Item2;
            return invoke.Result[length - 1];
        }),
        Enabled = true,
        Alerting = true,
        Archived = false,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.chronicle.Rule;
import com.pulumi.gcp.chronicle.RuleArgs;
import com.pulumi.gcp.chronicle.RuleDeployment;
import com.pulumi.gcp.chronicle.RuleDeploymentArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.SplitArgs;
import java.util.List;
import java.util.ArrayList;
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 my_rule = new Rule("my-rule", RuleArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .text("""
rule test_rule { meta: events:  $userid = $e.principal.user.userid  match: $userid over 10m condition: $e }
            """)
            .build());

        var example = new RuleDeployment("example", RuleDeploymentArgs.builder()
            .location("us")
            .instance("00000000-0000-0000-0000-000000000000")
            .rule(StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result().length().applyValue(_length -> StdFunctions.split(SplitArgs.builder()
                .separator("/")
                .text(googleChronicleRule.my-rule().name())
                .build()).result()[_length - 1]))
            .enabled(true)
            .alerting(true)
            .archived(false)
            .build());

    }
}

When runFrequency is omitted, Chronicle uses its default execution behavior. The deployment is enabled with alerting active and not archived. This configuration is useful when you want Chronicle to determine the optimal execution schedule based on rule complexity and data volume.

Beyond these examples

These snippets focus on specific rule deployment features: rule activation and alerting controls, execution frequency scheduling, and archive state management. They’re intentionally minimal rather than full detection pipelines.

The examples reference pre-existing infrastructure such as Chronicle Rule resources and a Chronicle instance with a valid instance ID. They focus on deployment configuration rather than rule logic or instance provisioning.

To keep things focused, common deployment patterns are omitted, including:

  • Archive workflow (two-step disable then archive process)
  • Execution state monitoring (executionState output)
  • Rule chaining (producerRules and consumerRules relationships)
  • Alert status change tracking (lastAlertStatusChangeTime)

These omissions are intentional: the goal is to illustrate how each deployment feature is wired, not provide drop-in detection modules. See the Chronicle RuleDeployment resource reference for all available configuration options.

Let's deploy GCP Chronicle Security Rules

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & State Management
How do I archive a rule deployment and what are the constraints?
Archiving requires a two-step process: first set enabled to false, then set archived to true. Once archived, alerting automatically becomes false, and you cannot update enabled, alerting, or runFrequency until you unarchive the deployment.
What's the difference between enabled and alerting?
enabled controls whether the rule runs continuously against incoming data, while alerting determines whether detections from this deployment are considered alerts. You can have a rule enabled without alerting, or disable both.
What are the runFrequency options and when is it required?
Valid values are LIVE, HOURLY, or DAILY. The property appears to be optional in some configurations, as shown in the example where a deployment is created with enabled=true but no runFrequency specified.
Resource Properties & Immutability
What properties can't I change after creating a rule deployment?
The instance, location, project, and rule properties are immutable. Changing any of these requires recreating the resource.
How do I reference a Chronicle Rule's ID in the deployment?
Extract the rule ID from the rule resource’s name property using string manipulation. The examples show using std.split with separator “/” and taking the last segment.

Using a different cloud?

Explore security guides for other cloud providers: