748 lines
18 KiB
Markdown
748 lines
18 KiB
Markdown
# Rule Parameter Mapping
|
|
|
|
## Overview
|
|
|
|
Rules in Attune can specify parameters to pass to actions when triggered. These parameters can be:
|
|
|
|
1. **Static values** - Hard-coded values defined in the rule
|
|
2. **Dynamic from event payload** - Values extracted from the event that triggered the rule
|
|
3. **Dynamic from pack config** - Values from the pack's configuration
|
|
|
|
This enables flexible parameter passing without hardcoding values or requiring custom code.
|
|
|
|
---
|
|
|
|
## Parameter Mapping Format
|
|
|
|
Rule `action_params` uses a JSON object where each value can be:
|
|
|
|
- **Static**: A literal value (string, number, boolean, object, array)
|
|
- **Dynamic**: A template string using `{{ }}` syntax to reference runtime values
|
|
|
|
### Template Syntax
|
|
|
|
```
|
|
{{ source.path.to.value }}
|
|
```
|
|
|
|
**Available Sources:**
|
|
|
|
- `event.payload.*` - Data from the event payload
|
|
- `pack.config.*` - Configuration values from the pack
|
|
- `system.*` - System-provided values (timestamp, execution context)
|
|
|
|
---
|
|
|
|
## Static Parameter Example
|
|
|
|
The simplest form - just pass fixed values to the action:
|
|
|
|
```json
|
|
{
|
|
"ref": "slack.notify_on_error",
|
|
"pack_ref": "slack",
|
|
"trigger_ref": "core.error_event",
|
|
"action_ref": "slack.post_message",
|
|
"action_params": {
|
|
"channel": "#alerts",
|
|
"message": "An error occurred in the system",
|
|
"color": "danger"
|
|
}
|
|
}
|
|
```
|
|
|
|
When this rule triggers, the action receives exactly these parameters.
|
|
|
|
---
|
|
|
|
## Dynamic Parameters from Event Payload
|
|
|
|
Extract values from the event that triggered the rule.
|
|
|
|
### Example: Alert with Event Data
|
|
|
|
**Event Payload:**
|
|
```json
|
|
{
|
|
"severity": "error",
|
|
"service": "api-gateway",
|
|
"message": "Database connection failed",
|
|
"timestamp": "2024-01-15T10:30:00Z",
|
|
"metadata": {
|
|
"host": "api-01.example.com",
|
|
"error_code": "DB_CONN_TIMEOUT"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Rule Definition:**
|
|
```json
|
|
{
|
|
"ref": "alerts.error_notification",
|
|
"pack_ref": "alerts",
|
|
"trigger_ref": "core.error_event",
|
|
"action_ref": "slack.post_message",
|
|
"action_params": {
|
|
"channel": "#incidents",
|
|
"message": "Error in {{ event.payload.service }}: {{ event.payload.message }}",
|
|
"severity": "{{ event.payload.severity }}",
|
|
"host": "{{ event.payload.metadata.host }}",
|
|
"timestamp": "{{ event.payload.timestamp }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Resulting Action Parameters:**
|
|
```json
|
|
{
|
|
"channel": "#incidents",
|
|
"message": "Error in api-gateway: Database connection failed",
|
|
"severity": "error",
|
|
"host": "api-01.example.com",
|
|
"timestamp": "2024-01-15T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Dynamic Parameters from Pack Config
|
|
|
|
Use configuration values stored at the pack level (useful for API keys, URLs, etc.).
|
|
|
|
### Example: Using Pack Configuration
|
|
|
|
**Pack Configuration:**
|
|
```json
|
|
{
|
|
"ref": "slack",
|
|
"config": {
|
|
"api_token": "xoxb-1234567890-abcdefghijk",
|
|
"default_channel": "#general",
|
|
"webhook_url": "https://hooks.slack.com/services/...",
|
|
"bot_name": "Attune Bot"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Rule Definition:**
|
|
```json
|
|
{
|
|
"ref": "slack.auto_notify",
|
|
"pack_ref": "slack",
|
|
"trigger_ref": "core.notification_event",
|
|
"action_ref": "slack.post_message",
|
|
"action_params": {
|
|
"token": "{{ pack.config.api_token }}",
|
|
"channel": "{{ pack.config.default_channel }}",
|
|
"username": "{{ pack.config.bot_name }}",
|
|
"message": "{{ event.payload.message }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Secrets stored in pack config, not in rules
|
|
- Easy to update credentials without changing rules
|
|
- Reuse configuration across multiple rules
|
|
|
|
---
|
|
|
|
## Mixed Parameters (Static + Dynamic)
|
|
|
|
Combine static and dynamic values in the same rule:
|
|
|
|
```json
|
|
{
|
|
"ref": "github.create_issue",
|
|
"pack_ref": "github",
|
|
"trigger_ref": "core.error_event",
|
|
"action_ref": "github.create_issue",
|
|
"action_params": {
|
|
"repo": "myorg/myrepo",
|
|
"token": "{{ pack.config.github_token }}",
|
|
"title": "Error: {{ event.payload.message }}",
|
|
"body": "Service {{ event.payload.service }} reported an error at {{ event.payload.timestamp }}",
|
|
"labels": ["bug", "automated"],
|
|
"assignees": ["oncall"]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Nested Object Access
|
|
|
|
Access nested properties using dot notation:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"user_id": "{{ event.payload.user.id }}",
|
|
"user_name": "{{ event.payload.user.profile.name }}",
|
|
"metadata": {
|
|
"ip_address": "{{ event.payload.request.client_ip }}",
|
|
"user_agent": "{{ event.payload.request.headers.user_agent }}"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Array Access
|
|
|
|
Access array elements by index:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"first_error": "{{ event.payload.errors.0 }}",
|
|
"primary_tag": "{{ event.payload.tags.0 }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Default Values and Fallbacks
|
|
|
|
Provide default values when the referenced field doesn't exist:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"priority": "{{ event.payload.priority | default: 'medium' }}",
|
|
"assignee": "{{ event.payload.assignee | default: 'unassigned' }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Type Preservation
|
|
|
|
Template values preserve their JSON types:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"count": "{{ event.payload.count }}", // Number: 42
|
|
"enabled": "{{ event.payload.enabled }}", // Boolean: true
|
|
"tags": "{{ event.payload.tags }}", // Array: ["a", "b"]
|
|
"metadata": "{{ event.payload.metadata }}" // Object: {"key": "value"}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## System Variables
|
|
|
|
Access system-provided values:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"execution_time": "{{ system.timestamp }}",
|
|
"rule_id": "{{ system.rule.id }}",
|
|
"rule_ref": "{{ system.rule.ref }}",
|
|
"event_id": "{{ system.event.id }}",
|
|
"enforcement_id": "{{ system.enforcement.id }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## String Interpolation
|
|
|
|
Embed multiple values in a single string:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"message": "User {{ event.payload.user_id }} performed {{ event.payload.action }} at {{ system.timestamp }}",
|
|
"subject": "[{{ event.payload.severity | upper }}] {{ event.payload.service }} Alert"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Filters (Future Enhancement)
|
|
|
|
Apply transformations to values:
|
|
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"uppercase_name": "{{ event.payload.name | upper }}",
|
|
"lowercase_email": "{{ event.payload.email | lower }}",
|
|
"formatted_date": "{{ event.payload.timestamp | date: '%Y-%m-%d' }}",
|
|
"truncated": "{{ event.payload.message | truncate: 100 }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Available Filters:**
|
|
- `upper` - Convert to uppercase
|
|
- `lower` - Convert to lowercase
|
|
- `trim` - Remove whitespace
|
|
- `default: <value>` - Use default if null/missing
|
|
- `date: <format>` - Format timestamp
|
|
- `truncate: <length>` - Truncate string
|
|
- `json` - Serialize to JSON string
|
|
- `base64` - Base64 encode
|
|
- `length` - Get length/count
|
|
|
|
---
|
|
|
|
## Real-World Examples
|
|
|
|
### 1. Webhook to Slack Alert
|
|
|
|
```json
|
|
{
|
|
"ref": "monitoring.webhook_to_slack",
|
|
"pack_ref": "monitoring",
|
|
"trigger_ref": "core.webhook",
|
|
"action_ref": "slack.post_message",
|
|
"action_params": {
|
|
"channel": "{{ pack.config.alert_channel }}",
|
|
"token": "{{ pack.config.slack_token }}",
|
|
"message": "⚠️ Alert from {{ event.payload.source }}: {{ event.payload.message }}",
|
|
"attachments": [
|
|
{
|
|
"color": "{{ event.payload.severity | default: 'warning' }}",
|
|
"fields": [
|
|
{
|
|
"title": "Service",
|
|
"value": "{{ event.payload.service }}",
|
|
"short": true
|
|
},
|
|
{
|
|
"title": "Environment",
|
|
"value": "{{ event.payload.environment | default: 'production' }}",
|
|
"short": true
|
|
}
|
|
],
|
|
"footer": "Attune Automation",
|
|
"ts": "{{ system.timestamp }}"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Error to Ticket System
|
|
|
|
```json
|
|
{
|
|
"ref": "errors.create_ticket",
|
|
"pack_ref": "errors",
|
|
"trigger_ref": "core.error_event",
|
|
"action_ref": "jira.create_issue",
|
|
"action_params": {
|
|
"project": "{{ pack.config.jira_project }}",
|
|
"auth": {
|
|
"username": "{{ pack.config.jira_username }}",
|
|
"token": "{{ pack.config.jira_token }}"
|
|
},
|
|
"issuetype": "Bug",
|
|
"summary": "[{{ event.payload.severity }}] {{ event.payload.service }}: {{ event.payload.message }}",
|
|
"description": {
|
|
"type": "doc",
|
|
"content": [
|
|
{
|
|
"type": "paragraph",
|
|
"content": [
|
|
{
|
|
"type": "text",
|
|
"text": "Error Details:\n\nService: {{ event.payload.service }}\nHost: {{ event.payload.host }}\nTimestamp: {{ event.payload.timestamp }}\n\nStack Trace:\n{{ event.payload.stack_trace }}"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"priority": "{{ event.payload.priority | default: 'Medium' }}",
|
|
"labels": ["automated", "{{ event.payload.service }}"]
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Metric Threshold to PagerDuty
|
|
|
|
```json
|
|
{
|
|
"ref": "monitoring.critical_alert",
|
|
"pack_ref": "monitoring",
|
|
"trigger_ref": "metrics.threshold_exceeded",
|
|
"action_ref": "pagerduty.trigger_incident",
|
|
"action_params": {
|
|
"routing_key": "{{ pack.config.pagerduty_routing_key }}",
|
|
"event_action": "trigger",
|
|
"payload": {
|
|
"summary": "{{ event.payload.metric_name }} exceeded threshold on {{ event.payload.host }}",
|
|
"severity": "critical",
|
|
"source": "{{ event.payload.host }}",
|
|
"custom_details": {
|
|
"metric": "{{ event.payload.metric_name }}",
|
|
"current_value": "{{ event.payload.current_value }}",
|
|
"threshold": "{{ event.payload.threshold }}",
|
|
"duration": "{{ event.payload.duration_seconds }}s"
|
|
}
|
|
},
|
|
"dedup_key": "{{ event.payload.host }}_{{ event.payload.metric_name }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Timer to HTTP Request
|
|
|
|
```json
|
|
{
|
|
"ref": "healthcheck.periodic_ping",
|
|
"pack_ref": "healthcheck",
|
|
"trigger_ref": "core.interval_timer",
|
|
"action_ref": "http.request",
|
|
"action_params": {
|
|
"method": "POST",
|
|
"url": "{{ pack.config.healthcheck_endpoint }}",
|
|
"headers": {
|
|
"Authorization": "Bearer {{ pack.config.api_token }}",
|
|
"Content-Type": "application/json"
|
|
},
|
|
"body": {
|
|
"source": "attune",
|
|
"timestamp": "{{ system.timestamp }}",
|
|
"rule": "{{ system.rule.ref }}"
|
|
},
|
|
"timeout": 30
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Details
|
|
|
|
### Template Processing Flow
|
|
|
|
1. **Rule Evaluation** - When an event matches a rule
|
|
2. **Template Extraction** - Identify `{{ }}` patterns in `action_params`
|
|
3. **Context Building** - Assemble available data:
|
|
- `event.id` - Event database ID
|
|
- `event.trigger` - Trigger ref that generated the event
|
|
- `event.created` - Event creation timestamp
|
|
- `event.payload` - Event payload data
|
|
- `pack.config` - Pack configuration
|
|
- `system.*` - System-provided values (timestamp, rule info)
|
|
4. **Value Resolution** - Extract values from context using dot notation paths
|
|
5. **Type Conversion** - Preserve JSON types (string, number, boolean, object, array)
|
|
6. **Parameter Assembly** - Build final parameter object
|
|
7. **Enforcement Creation** - Store resolved parameters in enforcement config
|
|
8. **Execution Creation** - Pass parameters to action execution
|
|
|
|
### Error Handling
|
|
|
|
**Missing Values:**
|
|
- If a referenced value doesn't exist and no default is provided, use `null`
|
|
- Log warning: `"Template variable not found: event.payload.missing_field"`
|
|
|
|
**Invalid Syntax:**
|
|
- If template syntax is invalid, log error and use the raw string
|
|
- Log error: `"Invalid template syntax: {{ incomplete"`
|
|
|
|
**Type Mismatches:**
|
|
- Preserve JSON types when possible
|
|
- Convert to string as fallback for complex interpolation
|
|
|
|
---
|
|
|
|
## Configuration in Pack
|
|
|
|
Pack configuration should be stored securely and can include:
|
|
|
|
```json
|
|
{
|
|
"ref": "mypack",
|
|
"config": {
|
|
"api_token": "secret-token-here",
|
|
"api_url": "https://api.example.com",
|
|
"default_timeout": 30,
|
|
"retry_attempts": 3,
|
|
"enable_notifications": true,
|
|
"notification_channels": ["#alerts", "#monitoring"]
|
|
}
|
|
}
|
|
```
|
|
|
|
**Security Note:** Sensitive values (API keys, tokens, passwords) should be stored in pack config, not in rule definitions, since:
|
|
- Pack configs can be encrypted
|
|
- Easier to rotate credentials
|
|
- Rules can be version controlled without exposing secrets
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. Use Pack Config for Secrets
|
|
❌ **Bad:**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"api_key": "sk_live_abc123xyz789" // Hardcoded secret
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **Good:**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"api_key": "{{ pack.config.api_key }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Provide Defaults for Optional Fields
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"priority": "{{ event.payload.priority | default: 'medium' }}",
|
|
"assignee": "{{ event.payload.assignee | default: 'unassigned' }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Use Descriptive Template Paths
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"user_email": "{{ event.payload.user.email }}",
|
|
"user_id": "{{ event.payload.user.id }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Keep Static Values Where Appropriate
|
|
If a value never changes, keep it static:
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"service_name": "my-service", // Static - never changes
|
|
"error_code": "{{ event.payload.code }}" // Dynamic - from event
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5. Test Your Templates
|
|
Create test events with sample payloads to verify your templates extract the correct values.
|
|
|
|
---
|
|
|
|
## Testing Parameter Mapping
|
|
|
|
### 1. Manual Testing via API
|
|
|
|
Create a test event with known payload:
|
|
|
|
```bash
|
|
curl -X POST http://localhost:8080/api/v1/events \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"trigger_ref": "core.test_event",
|
|
"payload": {
|
|
"message": "Test message",
|
|
"severity": "info",
|
|
"user": {
|
|
"id": 123,
|
|
"name": "Alice"
|
|
}
|
|
}
|
|
}'
|
|
```
|
|
|
|
Check the resulting enforcement and execution to verify parameters were resolved correctly:
|
|
|
|
```bash
|
|
# Check enforcement
|
|
curl -X GET http://localhost:8080/api/v1/enforcements/1 \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
|
|
# Check execution
|
|
curl -X GET http://localhost:8080/api/v1/executions/1 \
|
|
-H "Authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
### 2. Validate Parameter Resolution
|
|
|
|
Look for the resolved parameters in the execution's `config` field:
|
|
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"config": {
|
|
"message": "Test message", // Resolved from event.payload.message
|
|
"severity": "info", // Resolved from event.payload.severity
|
|
"user_id": 123, // Resolved from event.payload.user.id
|
|
"user_name": "Alice" // Resolved from event.payload.user.name
|
|
"event_id": 456, // Resolved from event.id
|
|
"trigger": "core.test_event" // Resolved from event.trigger
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Migration Guide
|
|
|
|
### From Static to Dynamic Parameters
|
|
|
|
**Before (Static):**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"message": "An error occurred"
|
|
}
|
|
}
|
|
```
|
|
|
|
**After (Dynamic):**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"message": "Error: {{ event.payload.message }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### From Hardcoded Secrets to Pack Config
|
|
|
|
**Before (Hardcoded):**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"api_key": "sk_live_abc123"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Steps:**
|
|
1. Add secret to pack config
|
|
2. Update rule to reference pack config
|
|
3. Remove hardcoded value
|
|
|
|
**After (Secure):**
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"api_key": "{{ pack.config.api_key }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Templates Not Resolving
|
|
|
|
**Problem:** Parameters contain literal `{{ ... }}` strings instead of resolved values.
|
|
|
|
**Solutions:**
|
|
1. Check template syntax is correct
|
|
2. Verify the referenced path exists in the event payload
|
|
3. Check sensor service logs for template resolution errors
|
|
4. Use default values for optional fields
|
|
|
|
### Incorrect Values
|
|
|
|
**Problem:** Parameters have wrong values.
|
|
|
|
**Solutions:**
|
|
1. Inspect event payload structure: `SELECT payload FROM attune.event WHERE id = X;`
|
|
2. Verify the dot notation path matches the payload structure
|
|
3. Check for typos in template paths
|
|
4. Use system logs to see template resolution details
|
|
|
|
### Type Conversion Issues
|
|
|
|
**Problem:** Numbers or booleans become strings.
|
|
|
|
**Solutions:**
|
|
1. Ensure the source value is the correct type in the payload
|
|
2. Check if string interpolation is converting types
|
|
3. Use direct references without string interpolation for non-string types
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
### 1. Conditional Parameters
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"channel": "{% if event.payload.severity == 'critical' %}#incidents{% else %}#monitoring{% endif %}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Advanced Filters
|
|
- Mathematical operations: `{{ event.payload.value | multiply: 100 }}`
|
|
- String manipulation: `{{ event.payload.text | replace: 'old', 'new' }}`
|
|
- Array operations: `{{ event.payload.items | join: ', ' }}`
|
|
|
|
### 3. Custom Functions
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"timestamp": "{{ now() }}",
|
|
"uuid": "{{ uuid() }}",
|
|
"hash": "{{ hash(event.payload.data) }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Multi-Source Merging
|
|
```json
|
|
{
|
|
"action_params": {
|
|
"user": "{{ event.payload.user | merge: pack.config.default_user }}"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [Rule Management API](./api-rules.md)
|
|
- [Event Management API](./api-events-enforcements.md)
|
|
- [Pack Management API](./api-packs.md)
|
|
- [Sensor Service Architecture](./sensor-service.md)
|
|
- [Security Best Practices](./security-review-2024-01-02.md)
|
|
- [Secrets Management](./secrets-management.md)
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
Rule parameter mapping provides a powerful way to:
|
|
|
|
1. **Decouple rules from data** - Rules reference data locations, not specific values
|
|
2. **Reuse pack configuration** - Share credentials and settings across rules
|
|
3. **Dynamic automation** - Respond to events with context-aware actions
|
|
4. **Secure secrets** - Store sensitive data in pack config, not rule definitions
|
|
5. **Flexible workflows** - Build complex automations without custom code
|
|
|
|
**Key Concepts:**
|
|
- Static values for constants
|
|
- `{{ event.payload.* }}` for event payload data
|
|
- `{{ event.id }}`, `{{ event.trigger }}`, `{{ event.created }}` for event metadata
|
|
- `{{ pack.config.* }}` for pack configuration
|
|
- `{{ system.* }}` for system-provided values
|
|
- Filters and defaults for robust templates
|
|
|
|
This feature enables Attune to match the flexibility of platforms like StackStorm while maintaining a clean, declarative approach to automation. |