---
url: /salesforce-devops-starter-kit/workflows/create-scratch-org.md
description: >-
  Reusable workflow that provisions a ready-to-use scratch org as a self-service
  task.
---

# Create Scratch Org

Reusable workflow that provisions a ready-to-use scratch org as a self-service task: it authenticates to the Dev Hub, creates a scratch org, optionally installs dependency packages, deploys the source, assigns permissions, imports sample data, runs anonymous Apex setup scripts, and generates a login password so the org can be handed over to a project member. Designed to be called from a `workflow_dispatch` caller (see examples/create-scratch-org.yml) so that even non-technical team members can request a fresh org straight from the GitHub Actions tab.

> Reusable workflow - call it from your own repository with `uses:`. [View source](https://github.com/svierk/salesforce-devops-starter-kit/blob/main/.github/workflows/create-scratch-org.yml)

## Inputs

| Input | Description | Type | Required | Default |
| --- | --- | --- | --- | --- |
| `org-name` | Human-readable name for the scratch org. | string | no | - |
| `description` | Description stored on the scratch org. | string | no | - |
| `admin-email` | Email address of the scratch org admin user. | string | no | - |
| `duration-days` | Number of days before the scratch org expires (1-30). | string | no | `7` |
| `source-dir` | Source directory to deploy to the scratch org. | string | no | `force-app` |
| `definition-file` | Path to the scratch org definition file. | string | no | `config/project-scratch-def.json` |
| `deploy-metadata` | Deploy the project source after the org is created. | boolean | no | `true` |
| `install-packages` | Install dependency packages before deploying. | boolean | no | `false` |
| `packages` | Comma-separated or multi-line list of dependency packages (aliases or IDs) to install, e.g. "04t...,04t...". | string | no | - |
| `permission-sets` | Comma-separated or multi-line list of permission sets to assign to the scratch org admin user after the deployment. Leave empty to skip. | string | no | - |
| `permission-set-groups` | Comma-separated or multi-line list of permission set groups to assign to the scratch org admin user. Leave empty to skip. | string | no | - |
| `permission-set-licenses` | Comma-separated or multi-line list of permission set licenses to assign to the scratch org admin user. Leave empty to skip. | string | no | - |
| `import-data` | Import sample data after the deployment. | boolean | no | `false` |
| `csv-file-path` | Path to a CSV file imported via the Bulk API 2.0. Leave empty to skip. | string | no | - |
| `csv-object-type` | sObject type of the records in the CSV file (required when csv-file-path is set). | string | no | - |
| `json-file-path` | Comma-separated or multi-line list of sObject Tree JSON file(s) to import, in order. Leave empty to skip. | string | no | - |
| `apex-file-path` | Comma-separated anonymous Apex file(s) or glob(s) to execute after the data import, e.g. "./scripts/apex/setup/\*.apex". Leave empty to skip. | string | no | - |

## Secrets

| Secret | Description | Required |
| --- | --- | --- |
| `SFDX_CONSUMER_KEY` | Consumer key (client ID) of the Dev Hub connected app used for the JWT flow. | yes |
| `SFDX_JWT_SECRET_KEY` | Contents of the private server key (server.key) used for the JWT flow. | yes |
| `SFDX_USERNAME` | Username of the Dev Hub integration user to authenticate as. | yes |

## Example usage

Copy this caller into your project's `.github/workflows/` directory ([source](https://github.com/svierk/salesforce-devops-starter-kit/blob/main/examples/create-scratch-org.yml)):

```yaml
# Example: self-service scratch org creation.
# Copy this file into your project's .github/workflows/ directory. Team members
# then run it on demand from the Actions tab ("Run workflow") and fill in the
# form below - no local Salesforce CLI setup required.

name: Create Scratch Org

on:
  workflow_dispatch:
    inputs:
      org-name:
        description: Scratch org name
        type: string
      description:
        description: Scratch org description
        type: string
      admin-email:
        description: Admin user email
        type: string
      duration-days:
        description: Lifespan in days (1-30)
        type: string
        default: '7'
      install-packages:
        description: Install dependency packages?
        type: boolean
        default: false
      import-data:
        description: Import sample data?
        type: boolean
        default: true

# Least-privilege token: the called workflow only reads the repository and talks
# to Salesforce. A reusable workflow can never get more permissions than the
# caller grants here.
permissions:
  contents: read

jobs:
  create-scratch-org:
    uses: svierk/salesforce-devops-starter-kit/.github/workflows/create-scratch-org.yml@v1.5.0
    with:
      org-name: ${{ inputs.org-name }}
      description: ${{ inputs.description }}
      admin-email: ${{ inputs.admin-email }}
      duration-days: ${{ inputs.duration-days }}
      source-dir: force-app
      deploy-metadata: true
      install-packages: ${{ inputs.install-packages }}
      # Dependency packages to install when "Install dependency packages?" is checked:
      # packages: '04t...,04t...'
      # Permissions granted to the admin user after the deployment, so the
      # handed-over org (and the data import below) can use the deployed metadata:
      # permission-sets: 'My_App_Admin,My_App_User'
      # permission-set-groups: 'Sales_Manager'
      import-data: ${{ inputs.import-data }}
      # Sample data imported when "Import sample data?" is checked (CSV via Bulk API 2.0,
      # JSON via sObject Tree). Point these at files that exist in your repository:
      csv-file-path: ./data/accounts.csv
      csv-object-type: Account
      json-file-path: ./data/accounts.json,./data/contacts.json
      # Anonymous Apex setup scripts executed last, in alphabetical order:
      # apex-file-path: ./scripts/apex/setup/*.apex
    secrets:
      SFDX_CONSUMER_KEY: ${{ secrets.SFDX_CONSUMER_KEY }}
      SFDX_JWT_SECRET_KEY: ${{ secrets.SFDX_JWT_SECRET_KEY }}
      SFDX_USERNAME: ${{ secrets.SFDX_USERNAME }}
```
