Skip to content
Back to skills

Sg Create Workflow

ASecurity

Guided flow for creating a StackGuardian workflow via the StackGuardian MCP create_workflow tool — first asks whether to deploy from a marketplace template or a git repository, then walks the user through template selection (or repo, branch, directory and public/private + VCS connector), workflow group, cloud connector, environment variables, custom step pipelines (CUSTOM/WfStepsConfig), template inputs (always presenting the template's defaults for review before creating), cron schedules, po...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
ai-agentsgokubernetesawsazureterraformgitdevops

Works with

  • mcp

Security analysis

A100/100

Scanned October 16, 2026

npx -y skills add StackGuardian/stackguardian-mcp --skill sg-create-workflow --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sg Create Workflow?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sg Create Workflow
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/stackguardian-sg-create-workflow/badge)](https://www.skillsdirectory.com/skills/stackguardian-sg-create-workflow)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sg-create-workflow
description: Guided flow for creating a StackGuardian workflow via the StackGuardian MCP create_workflow tool — first asks whether to deploy from a marketplace template or a git repository, then walks the user through template selection (or repo, branch, directory and public/private + VCS connector), workflow group, cloud connector, environment variables, custom step pipelines (CUSTOM/WfStepsConfig), template inputs (always presenting the template's defaults for review before creating), cron schedules, post-run notifications/chaining/webhooks, runner caching and job sizing, then creates the workflow. Covers TERRAFORM, OPENTOFU and CUSTOM workflow types and both template and git sources. Use when the user wants to create/deploy a workflow, deploy a template from the StackGuardian library, deploy IaC from their own git repo, or set up an Ansible/Helm/Kubernetes/CloudFormation step pipeline.
---

# Create Workflow — Guided Flow

Create a StackGuardian IaC workflow with the `create_workflow` tool on the
StackGuardian MCP server. Follow the steps IN ORDER. Never ask the user to
type IDs or names from memory — always fetch options with the listed tools
and present choices. In user-facing messages use plain language; never
surface internal parameter names like `wfGrp`, `source`,
`deployment.connectorId`, or `iacInputs`.

Parameters are grouped to match the tool's own shape: `source` (one object,
template XOR git — never both), `deployment`,
`terraformConfig`, `approvals`, `runnerConstraints` are each a single nested
object, not flat siblings. Build each group's object as you collect its
fields in the steps below, then pass the assembled objects in the final
`create_workflow` call.

## Step A — Pick the IaC source mode

**First question, before anything else**: does the user want to deploy from
the **StackGuardian template library** or from **their own git repository**?
Ask — don't infer it from a stray URL or template name. Build exactly one of
these two shapes for `source` — never both, and never mix fields from one
into the other:
- `{"type": "template", "templateId": ..., "iacInputs": {...}}`
- `{"type": "git", "repo": ..., "ref": ..., "workingDir": ...,
  "isPrivateRepo": ..., "gitAuthRef": ..., "includeSubModule": ...,
  "iacInputs": {...}}`

### Template mode

1. Call `search_marketplace_templates(templateType='IAC')` (add `searchQuery`
   if the user described what they want). Present the results; let the user
   pick one.
2. Note the chosen template's `ResourceId` in pinned `/owner/name:rev` form
   (e.g. `/stackguardian/simple-ec2:3`) — this becomes `source.templateId`.
   Always pin the revision; never pass an unpinned template id.
3. Immediately call `get_marketplace_template_by_id` with that id and keep the
   `InputSchemas` for Step D.
4. **Draft vs published.** Each revision's `IsPublic` says whether it is
   published (`"1"`) or still a **draft** (`"0"`). When presenting revisions,
   label each one. A draft is allowed — it is how a template author tests
   one — but tell the user plainly that it is unpublished, can still change
   under the workflow, and is not for production; let them continue if they
   want. Never block on it, and never silently pick a draft. (The tool
   repeats this as a `warnings` entry in its response.)

### Git mode

1. Collect the clone URL (`source.repo` — HTTPS or SSH form both work),
   optional branch/tag/commit (`source.ref`, repo default branch when
   omitted) and optional subdirectory holding the IaC root
   (`source.workingDir`, repo root when omitted). Ask about submodules only
   if the code vendors modules that way (`source.includeSubModule=true`).
2. **Ask whether the repository is public or private — never assume.** The
   tool has no default and refuses the call until it knows:
   - **Public**: pass `source.isPrivateRepo=false` explicitly.
   - **Private**: the platform needs credentials to clone. List the org's
     VCS connectors with
     `search_orchestrator_resources(resourceTypes=['INTEGRATION.VCS'])`,
     present them (name + provider kind), and pass the pick as
     `source.gitAuthRef="/integrations/<id>"` (use the full
     `/integrationgroups/<group>/integrations/<id>` form if the connector is
     group-managed or the tool reports an ambiguity with `candidates`).
     Setting `gitAuthRef` implies private — no need to also send
     `isPrivateRepo`. For a plain git host with no connector, a
     `/secrets/<name>` token is the alternative (GIT_OTHER hosts accept
     ONLY that form).
3. The VCS provider kind is inferred from the repo host (github.com,
   gitlab.com, bitbucket.org, dev.azure.com; anything else is GIT_OTHER) and,
   when a connector is given, taken from the connector itself. Do NOT pass
   `source.vcsProvider` unless the user explicitly needs an override such as
   GITHUB_APP_CUSTOM or AZURE_DEVOPS_SP.
4. There is no `InputSchemas` for a git source — Step D is different there
   (see below).

## Step B — Workflow group and naming

1. Offer the user exactly three ways to pick the target group — never dump
   or "recommend" arbitrary groups:
   - **Name or keyword**: resolve it via
     `search_orchestrator_resources(resourceTypes=['WORKFLOW_GROUP'],
     searchQuery=<keyword>)`; on a near-miss suggest the closest match
     ("no `production` — did you mean `prod`?"). The confirmed pick becomes
     `wfGrp` — pass EVERYTHING AFTER `/wfgrps/` IN ITS `ResourceId` (e.g.
     `mcp-test-qypm75mb` from `/wfgrps/mcp-test-qypm75mb`, and the whole
     `PROD/eu-central-1` from `/wfgrps/PROD/eu-central-1`), which is how
     the platform addresses the group; the `ResourceName` shown to the user
     can differ from it, and a clean-looking name is NO guarantee they
     match (`gitops` may be `gitops-y8pf2mb1`) — never assume, always read
     the `ResourceId`. The tool accepts either and translates a name, but
     the id is what every later call (update/run/delete) needs, so carry
     the id forward. Never invent a group name.
   - **Create a new group**: pass `createNewWorkflowGroup=true` ONLY when the
     user explicitly asks for a brand-new group.
   - **Browse in the dev portal**: share the workflow-groups page —
     `https://app.stackguardian.io/orchestrator/orgs/<org>?tab=workflowGroups`.
   This step is mandatory — never call `create_workflow` without a
   user-confirmed `wfGrp`.
   **Nested groups are accepted** — `PROD/eu-central-1` is a real group that
   holds workflows. Pass the whole path exactly as it appears; the last
   segment alone is a different group. A full resource id is not a group
   path — drop any `/wfs/...` or `/stacks/...` part.
2. Ask the user for:
   - workflow name (`resourceName`, 1–100 chars)
   - optional description (max 512 chars)
   - optional tags (max 10) and context tags (key:value pairs)
3. Optionally check for duplicates first with
   `search_workflows(searchQuery=<name>)`.

## Step C — Cloud connection and environment variables

1. Ask whether a cloud deployment connector is needed (skip if no cloud
   deployment — then omit `deployment` entirely). Build
   `deployment = {"connectorId": "/integrations/<id>"}` — only `connectorId`
   is required; its kind is resolved live server-side; do NOT pass
   `deployment.connectorKind` unless a returned error asks for it. Find
   connectors via
   `search_orchestrator_resources(resourceTypes=['INTEGRATION.CLOUD'])`.
2. Ask whether to add environment variables. Collect each as
   `{varName, textValue}` (plain text) or `{varName, secretId}` where
   `secretId` is a `/secrets/<name>` vault reference — exactly one of the
   two per variable. Repeat until done. (`environmentVariables` stays a flat
   list param, not nested under another object.)

## Step C2 — Custom steps (CUSTOM workflows only)

Skip this entirely unless the user wants a **custom step pipeline** rather
than a Terraform/OpenTofu run. Signals: they describe running an Ansible
playbook, a Helm chart, a Kubernetes manifest, a CloudFormation stack, or
their own container/script rather than Terraform.

1. Set `wfType: "CUSTOM"`.
2. Decide where the steps come from — ask if unclear:
   - **From the template** (template mode only): if the library template
     chosen in Step A is itself a CUSTOM template, it already carries its
     own steps. Omit `wfStepsConfig` entirely and the platform uses the
     template's. Supplying your own **replaces** the template's steps
     outright — there is no merge — so only do so when the user wants
     different steps.
   - **Git mode**: a git source carries no steps, so `wfStepsConfig` is
     REQUIRED for a CUSTOM workflow — the tool refuses the call without it.
   - **Explicit steps**: build `wfStepsConfig`, a list of up to 10 steps run
     in list order.
3. For each explicit step, find the step template with
   `search_marketplace_templates(templateType='WORKFLOW_STEP')` (or
   `get_subscription`) and present the choices — never invent an id. Then
   call `get_marketplace_template_by_id` on the chosen revision to read its
   `InputSchemas`. Present that step's defaults and collect values the same
   way as Step D — a step template's defaults ship unseen just as readily as
   the workflow's own.
   Each step is:
   `{"name": "<slug>", "wfStepTemplateId": "/owner/name:rev",
     "wfStepInputData": {"schemaType": "FORM_JSONSCHEMA", "data": {...}},
     "approval": false, "timeout": 5400, "environmentVariables": [...],
     "mountPoints": [...]}`
   Only `name` and `wfStepTemplateId` are required.
4. Rules worth knowing before you call:
   - `approval: true` pauses the run for manual approval BEFORE that step —
     a per-step gate, set per step. For a CUSTOM workflow this is the ONLY
     approval gate available: `approvalPreApply` lives in `terraformConfig`,
     which the platform rejects for CUSTOM outright. `approvals` still
     controls WHO may approve and how many of them.
   - `name` is letters/digits/`_`/`-` only, unique within the workflow. It is
     part of the run's status identity (`on_<index>_<name>`), not a label.
   - `mountPoints` need a **private** runner — set `runnerConstraints` to
     `{"type": "private", "names": ["<group>"]}` or drop the mounts.
   - `wfStepInputData.data` is **not** validated against the step template's
     schema by the platform; a wrong key reaches the container silently, so
     check `InputSchemas` rather than guessing.
   - `wfStepTemplateId` is **not** verified at create time either — a bad id
     creates a workflow that fails at every run. Always take it from a
     search result.
   - A `/secrets/<name>` reference inside a step's environment variables
     fails **silently** at run time if it can't be resolved (unlike
     workflow-level variables). Confirm the secret exists.

## Step D — Template parameters

For a CUSTOM workflow the per-step inputs were already collected in Step C2;
this step covers the workflow's own IaC template inputs.

**Git mode**: there is no template schema to read. Ask the user whether the
IaC needs variable values (Terraform variables without defaults); collect any
as a plain `source.iacInputs` dict of `{name: value}`, or omit it. Nothing is
validated here — a missing required variable surfaces at plan time — so
prompt for the ones the user knows are required. Then go to Step E.

**Template mode**:

**ALWAYS present the template's defaults to the user before creating — never
let one apply unseen.** Omitting an input does not leave it unset; the value
the template declares is what gets deployed. Show every input and its default
even when nothing is strictly required and the call would succeed untouched:
the user is agreeing to those values whether or not they were shown them, so
show them.

Read the defaults out of the `InputSchemas` fetched in Step A. It is a LIST;
the entry with `type: "FORM_JSONSCHEMA"` carries `encodedData`, a base64
JSON Schema. Decode it and walk `properties` recursively — each `default` key
is the value that ships if the user says nothing, and nested objects carry
nested defaults. An input with no `default` has none: it needs a value or it
falls through to whatever the template's own IaC code defaults to.

Present each input as: name, what it does, **the default that will be used**,
and whether it is required. Ask the user to confirm the set or override
individual values, and say plainly which ones you are about to accept as-is.
Collect their answers as `source.iacInputs`; inputs they left alone may be
omitted or passed explicitly — both behave the same.

If `InputSchemas` is empty or has no `FORM_JSONSCHEMA` entry, tell the user
"No template parameters needed" and skip.

## Step E — Options and create

Defaults are agent-safe; only surface these if the user asks or they matter.
Each bullet below is a top-level `create_workflow` parameter — build the
nested object only if you have something to put in it, and omit the
parameter entirely otherwise (its defaults still apply omitted):
- `wfType`: TERRAFORM (default), OPENTOFU, or CUSTOM (see Step C2 below).
  Note ANSIBLE / HELM / KUBERNETES / CLOUDFORMATION are NOT workflow types —
  they are search filters for CUSTOM workflows whose step template matches
  that keyword. To build one, use `wfType: CUSTOM` with the matching step
  template.
- `terraformConfig`: `{"approvalPreApply": true, "managedTerraformState": true,
  "driftCheck": false, "terraformVersion": ..., "driftCron": ...}` — these
  three booleans already match this tool's agent-safe defaults, so omit the
  whole object unless something needs to differ. `driftCron` (AWS cron) is
  stored independently of `driftCheck` — a schedule set while `driftCheck` is
  false sits inert until drift checking is enabled. `approvalPreApply` (default
  false) pauses after `plan` and waits before `apply` — it's the Terraform
  approval gate; a no-change plan finishes without asking.
  **Never send `terraformConfig` for a CUSTOM workflow** — the platform
  rejects the
  combination outright, so drift check, `approvalPreApply` and the Terraform
  hooks are simply unavailable there.
- `approvals`: `{"approvers": [...], "numberOfApprovalsRequired": N}` — WHO
  may approve. Two settings, and both are needed for a real gate:
  `approvalPreApply` (above) decides whether a run stops; `approvals` decides
  who releases it. A pause with no approvers can be released by ANYONE.
  The count reads backwards, so get it right: **`0` = every approver must
  approve** (the strictest setting, not "none"), **`1` = any one of them**,
  `N` = that many. Keep `N` <= the number of approvers — nothing validates it,
  and a larger N is a gate no run can satisfy. `>=1` is required when any
  approver is an SSO group. ALWAYS send both: approvers with no count leaves
  the platform's default of `0`, silently creating a UNANIMOUS gate; an empty
  list with a count mints a workflow needing approvals from nobody.
  Read the result back in plain words before creating ("anyone from X or Y can
  approve" / "both must approve").
- `runnerConstraints`: `{"type": "shared"}` default (StackGuardian-managed
  infra), or `{"type": "private", "names": ["<runner-group-name>"]}` to run on
  your own runner group. `names` is a single-item list of the group's NAME
  (not an id or path) — find it via
  `search_orchestrator_resources(resourceTypes=['RUNNER'])` and use the
  ResourceName.
- `miniSteps`: post-run actions keyed by run outcome (COMPLETED / ERRORED /
  ...) — chain other workflows or stacks, send email notifications, fire
  webhooks:
  `{"wfChaining": {"COMPLETED": [{"workflowGroupId": ..., "workflowId": ...}]},
  "notifications": {"email": {"COMPLETED": [{"recipients": [...]}]}},
  "webhooks": {"COMPLETED": [{"webhookName": ..., "webhookUrl": ...}]}}`.
  All three groups and every event key are optional. A `stackId` can stand in
  for `workflowId` when chaining to a stack. `DRIFT_DETECTED` only ever fires
  when `terraformConfig.driftCheck` is enabled — don't offer it otherwise.
- `userSchedules`: up to 10 cron schedules for automated runs —
  `[{"cron": "0 2 * * ? *", "state": "ENABLED", "desc": ..., "inputs": {...}}]`.
  `cron` is AWS EventBridge syntax (note the `?` day-of-week field) and
  `state` is ENABLED/DISABLED — a DISABLED schedule is kept but doesn't fire,
  which is how you park one during a freeze. `inputs` overrides settings for
  THAT schedule's runs only (env vars, Terraform action/config, job sizing,
  chaining) — omit it and scheduled runs use the workflow's own settings.
- `cacheConfig`: persist runner directories between runs so repeats
  warm-start — `{"enabled": true, "paths": [".terraform"], "key":
  "tf-plugins-v1", "policy": "PULL_PUSH"}`. If you pass it at all, all four of
  `enabled`/`paths`/`key`/`policy` are REQUIRED. `key` is slug format
  (letters, digits, `_`, `-`); runs sharing a key share the cache, so change
  it to force a fresh one. `policy` is PULL_PUSH (restore + save, typical),
  PULL (restore only), or PUSH (save only — a cache-warming workflow).
  Optional `fallback_keys` restores from older keys after a rotation.
- `userJobCPU` / `userJobMemory`: run-container sizing — CPU in ECS units
  (1024 = 1 vCPU; platform default 512) and memory in MB (default 1024).
  Raise memory when runs fail out-of-memory, common with large Terraform
  states; raise CPU for very large plans.
- `parallelExecution`: **CUSTOM workflows only** — true lets runs of this
  workflow execute simultaneously, false (platform default) queues them
  serially. Rejected outright for TERRAFORM/OPENTOFU.
- `isActive`: true (default) = active. false parks the workflow so it doesn't
  count toward the plan's active-workflow quota, but its run history, logs and
  outputs return 402 until it's re-activated (triggering a run re-activates it
  automatically). Rarely worth setting at create time.
- `githubComSync`: advanced GitHub PR-trigger behavior, e.g.
  `{"pull_request_opened": {"createWfRun": {"enabled": true}}}`. Free-form and
  NOT validated by the platform, so a wrong key fails silently — most
  workflows omit it entirely.

Confirm a plain-language summary of everything collected with the user, then
call `create_workflow` with all parameters.

## Error handling (teaching errors)

The tool validates live and returns actionable errors instead of opaque 400s.
On error, present the offered options and re-call — do not guess:
- `availableWorkflowGroups` → unknown group; a list of group IDS (capped
  at 50) — re-call with one verbatim; `availableWorkflowGroupNames`
  maps any id whose display name differs, show those to the user; plus
  the `devPortalWorkflowGroupsUrl` link for browsing
- `availableConnectors` → unknown `deployment.connectorId`; present list,
  re-call
- `availableVcsConnectors` → either the public/private question hasn't been
  answered (the error text asks it — put it to the user, then re-call with
  `isPrivateRepo=false` or a chosen connector as `gitAuthRef`) or the given
  `gitAuthRef` connector is unknown; present the list, re-call
- `candidates` → the bare `/integrations/<id>` exists in several integration
  groups; re-call with the full `/integrationgroups/...` id from the list
- `availableSecrets` → a referenced `/secrets/<name>` doesn't exist; present
  list or create the secret first
- HTTP 409 = workflow name taken; 403 = not permitted; 400 = payload/policy
  rejection — relay the platform's message

## Success

The response is `{success, msg, data, source}`; `data.SubResourceId` is the
new `/wfgrps/<group>/wfs/<name>` path. Tell the user the workflow was created
and where. It appears in `search_workflows` once StackGuardian ingestion
catches up (seconds) — no need to re-query, the response already carries the
resource.

Note: org is always derived server-side from the authenticated user — never
ask for or pass an organization. If the tool is missing, the server likely has
`SG_WRITE_TOOLS_ENABLED=false` (read-only mode).

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.