Overview
Argo CD allows operators to define custom actions that users can perform on specific resource types. Actions are implemented as Lua scripts and can modify resources or create new ones.
Built-in Actions Argo CD includes built-in actions like restart for DaemonSet/Deployment/StatefulSet and retry for Argo Rollouts. You can extend these capabilities with custom actions.
Action Types
Argo CD supports two types of resource actions:
Returns the modified source resource. This was the only action type until v2.8 and is still supported. -- Suspend a CronJob
obj . spec . suspend = true
return obj
Returns a list of impacted resources with operations. Supports creating new resources. -- Create a Job from CronJob and patch the CronJob
result = {}
result [ 1 ] = { operation = "create" , resource = newJob }
result [ 2 ] = { operation = "patch" , resource = obj }
return result
This is an alpha feature introduced in v2.8.
Basic Configuration
Define actions in the argocd-cm ConfigMap using the format:
resource.customizations.actions.<apiGroup>_<kind>
Simple Action Example
apiVersion : v1
kind : ConfigMap
metadata :
name : argocd-cm
namespace : argocd
data :
resource.customizations.actions.batch_CronJob : |
discovery.lua: |
actions = {}
actions["suspend"] = {["disabled"] = true}
actions["resume"] = {["disabled"] = true}
local suspend = false
if obj.spec.suspend ~= nil then
suspend = obj.spec.suspend
end
if suspend then
actions["resume"]["disabled"] = false
else
actions["suspend"]["disabled"] = false
end
return actions
definitions:
- name: suspend
action.lua: |
obj.spec.suspend = true
return obj
- name: resume
action.lua: |
if obj.spec.suspend ~= nil and obj.spec.suspend then
obj.spec.suspend = false
end
return obj
Discovery Script
The discovery.lua script determines which actions are available and whether they’re enabled:
actions = {}
-- Define available actions
actions [ "restart" ] = {}
actions [ "scale" ] = {}
actions [ "delete-pod" ] = {[ "disabled" ] = true }
-- Conditionally enable actions based on resource state
if obj . spec . replicas ~= nil and obj . spec . replicas == 0 then
actions [ "restart" ][ "disabled" ] = true
end
if obj . status ~= nil and obj . status . availableReplicas ~= nil then
actions [ "delete-pod" ][ "disabled" ] = false
end
return actions
Dynamic Actions Use the resource’s current state to enable/disable actions dynamically. This provides better UX by only showing relevant actions.
Action Definitions
Each action must have a definition with the action logic:
definitions :
- name : restart
action.lua : |
local os = require("os")
if obj.spec.template.metadata == nil then
obj.spec.template.metadata = {}
end
if obj.spec.template.metadata.annotations == nil then
obj.spec.template.metadata.annotations = {}
end
obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
return obj
Customizing Action Appearance
Add icons and display names to your actions:
local actions = {}
actions [ "create-workflow" ] = {
[ "iconClass" ] = "fa fa-fw fa-plus" ,
[ "displayName" ] = "Create Workflow"
}
actions [ "terminate" ] = {
[ "iconClass" ] = "fa fa-fw fa-stop-circle" ,
[ "displayName" ] = "Terminate Analysis"
}
return actions
Action Parameters
Define parameters that users can provide when executing actions:
actions [ "scale" ] = {
[ "displayName" ] = "Scale" ,
[ "iconClass" ] = "fa fa-fw fa-arrows-v"
}
actions [ "scale" ]. params = {
{
name = "replicas" ,
type = "number" ,
default = "1"
}
}
return actions
Access parameters in the action script:
local replicas = tonumber ( params . replicas ) or 1
obj . spec . replicas = replicas
return obj
Creating New Resources
GitOps Departure Creating resources via the UI departs from GitOps principles. Use this sparingly and only for resources not part of the desired state.
Creating Child Resources
Set ownerReference for Kubernetes garbage collection:
-- Create a Job from a CronJob
ownerRef = {}
ownerRef . apiVersion = obj . apiVersion
ownerRef . kind = obj . kind
ownerRef . name = obj . metadata . name
ownerRef . uid = obj . metadata . uid
job = {}
job . apiVersion = "batch/v1"
job . kind = "Job"
job . metadata = {}
job . metadata . name = obj . metadata . name .. "-manual-" .. os.time ()
job . metadata . namespace = obj . metadata . namespace
job . metadata . ownerReferences = { ownerRef }
job . spec = obj . spec . jobTemplate . spec
result = {}
result [ 1 ] = { operation = "create" , resource = job }
return result
Creating Independent Resources
Add Tracking Label
Copy the Argo CD tracking label so the App recognizes the resource: newObj . metadata . labels = {}
newObj . metadata . labels [ "app.kubernetes.io/instance" ] =
obj . metadata . labels [ "app.kubernetes.io/instance" ]
Prevent Auto-Prune
Set annotation to prevent deletion with auto-prune: newObj . metadata . annotations = {}
newObj . metadata . annotations [ "argocd.argoproj.io/sync-options" ] =
"Prune=false"
Keep App Synced (Optional)
Prevent out-of-sync status: newObj . metadata . annotations [ "argocd.argoproj.io/compare-options" ] =
"IgnoreExtraneous"
Complete Creation Example
resource.customizations.actions.ConfigMap : |
discovery.lua: |
actions = {}
actions["create-backup"] = {
["iconClass"] = "fa fa-fw fa-save",
["displayName"] = "Create Backup"
}
return actions
definitions:
- name: create-backup
action.lua: |
local os = require("os")
-- Create backup ConfigMap
backup = {}
backup.apiVersion = "v1"
backup.kind = "ConfigMap"
backup.metadata = {}
backup.metadata.name = obj.metadata.name .. "-backup-" .. os.time()
backup.metadata.namespace = obj.metadata.namespace
backup.metadata.labels = {}
backup.metadata.labels["app.kubernetes.io/instance"] =
obj.metadata.labels["app.kubernetes.io/instance"]
backup.metadata.labels["backup-of"] = obj.metadata.name
backup.metadata.annotations = {}
backup.metadata.annotations["argocd.argoproj.io/sync-options"] =
"Prune=false"
backup.metadata.annotations["argocd.argoproj.io/compare-options"] =
"IgnoreExtraneous"
backup.data = obj.data
-- Also update original with backup timestamp
if obj.metadata.annotations == nil then
obj.metadata.annotations = {}
end
obj.metadata.annotations["last-backup"] = os.date("!%Y-%m-%dT%XZ")
result = {}
result[1] = {operation = "create", resource = backup}
result[2] = {operation = "patch", resource = obj}
return result
Merging with Built-in Actions
By default, custom actions override built-in actions. To keep built-in actions:
resource.customizations.actions.argoproj.io_Rollout : |
mergeBuiltinActions: true
discovery.lua: |
actions = {}
actions["custom-action"] = {}
return actions
definitions:
- name: custom-action
action.lua: |
return obj
Custom actions have precedence over built-in actions when names conflict.
Contributing Actions
Contribute actions to the Argo CD repository:
argo-cd/
└── resource_customizations/
└── your.crd.group.io/
└── MyKind/
└── actions/
├── action_test.yaml
├── discovery.lua
├── testdata/
│ ├── running.yaml
│ └── terminated.yaml
└── <action-name>/
└── action.lua
discoveryTests :
- inputPath : testdata/running.yaml
result :
- name : terminate
disabled : false
- name : suspend
disabled : true
- inputPath : testdata/terminated.yaml
result :
- name : terminate
disabled : true
actionTests :
- action : terminate
inputPath : testdata/running.yaml
expectedOutputPath : testdata/running_terminated.yaml
Run tests:
go test -v ./resource_customizations/.../actions/
RBAC for Actions
Control access to actions using RBAC:
p, role:action-runner, action/*, *, */*, allow
p, role:limited-user, action/apps/Deployment/restart, *, */*, allow
p, role:limited-user, action/apps/Deployment/scale, *, */*, deny
See the RBAC documentation for details.
Best Practices
Return meaningful messages on errors: if obj . status . phase == "Failed" then
error ( "Cannot restart a failed job. Delete and recreate instead." )
end
Create comprehensive tests covering:
Normal operation
Edge cases
Error conditions
Various resource states
Choose clear, action-oriented names:
✅ restart, scale, create-backup
❌ do-thing, action1, fix
Security Considerations
Resource Permissions When creating resources:
New resources must be permitted at the AppProject level
Users need appropriate RBAC permissions
Consider the security implications of creating resources outside GitOps
Example: Scale Deployment
resource.customizations.actions.apps_Deployment : |
discovery.lua: |
actions = {}
actions["scale"] = {
["iconClass"] = "fa fa-fw fa-arrows-v",
["displayName"] = "Scale"
}
-- Add parameter definition
actions["scale"].params = {
{
name = "replicas",
type = "number",
default = tostring(obj.spec.replicas or 1)
}
}
-- Disable if already scaling
if obj.status ~= nil and obj.status.replicas ~= obj.status.updatedReplicas then
actions["scale"]["disabled"] = true
end
return actions
definitions:
- name: scale
action.lua: |
local replicas = tonumber(params.replicas)
if replicas == nil or replicas < 0 then
error("Invalid replica count: " .. params.replicas)
end
if replicas > 100 then
error("Replica count exceeds maximum allowed (100)")
end
obj.spec.replicas = replicas
return obj
Next Steps
Custom Health Checks Define custom health checks for resources
UI Extensions Extend the Argo CD web interface
Built-in Actions Browse built-in resource actions
RBAC Configure action permissions