Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ docs/
│ ├── multi-team-workflows.md — Selective extraction, CODEOWNERS
│ ├── code-first-workflow.md — IDE → git → CI/CD → APIM
│ ├── token-substitution.md — Pipeline token/placeholder substitution
│ ├── prompt-files.md — Copilot prompt files for APIOps tasks
│ └── migration-from-v1.md — Migrate from Azure/apiops toolkit
├── ci-cd/
│ ├── github-actions.md — GitHub Actions integration
Expand Down
17 changes: 9 additions & 8 deletions docs/commands/init.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,28 @@ In interactive mode (the default when running in a terminal), `apiops init` prom

| File | Purpose |
|------|---------|
| `.github/workflows/run-apim-extractor.yml` | Workflow to extract APIM artifacts |
| `.github/workflows/run-apim-publisher.yml` | Workflow to publish artifacts to APIM |
| `.github/workflows/run-apiops-extractor.yml` | Workflow to extract APIM artifacts |
| `.github/workflows/run-apiops-publisher.yml` | Workflow to publish artifacts to APIM |
| `configuration.extractor.yaml` | Sample filter configuration for extraction |
| `configuration.{env}.yaml` | Override templates per environment (e.g., `configuration.dev.yaml`, `configuration.prod.yaml`) |
| `IDENTITY-SETUP-GITHUB.md` | Step-by-step guide for configuring federated credentials |
| `.github/prompts/apiops-setup-workflow-identity.prompt.md` | Copilot prompt for GitHub Actions identity setup |
| `APIOPS-WORKFLOW-IDENTITY-SETUP.md` | Step-by-step guide for configuring GitHub Actions Azure access and workflow identity |

### Azure DevOps (`--ci azure-devops`)

| File | Purpose |
|------|---------|
| `.azdo/pipelines/run-apim-extractor.yml` | Pipeline to extract APIM artifacts |
| `.azdo/pipelines/run-apim-publisher.yml` | Pipeline to publish artifacts to APIM |
| `.azdo/pipelines/run-apiops-extractor.yml` | Pipeline to extract APIM artifacts |
| `.azdo/pipelines/run-apiops-publisher.yml` | Pipeline to publish artifacts to APIM |
| `configuration.extractor.yaml` | Sample filter configuration for extraction |
| `configuration.{env}.yaml` | Override templates per environment |
| `IDENTITY-SETUP-AZDO.md` | Step-by-step guide for configuring service connections |
| `.github/prompts/apiops-setup-pipeline-identity.prompt.md` | Copilot prompt for Azure DevOps identity setup |
| `APIOPS-PIPELINE-IDENTITY-SETUP.md` | Step-by-step guide for configuring Azure DevOps pipeline identity and repo permissions |

### Both platforms

| File | Purpose |
|------|---------|
| `.github/prompts/apiops-setup-identity.prompt.md` | Copilot prompt for identity setup |
| `.github/prompts/apiops-configure-filter.prompt.md` | Copilot prompt for creating extraction filter files |
| `.github/prompts/apiops-configure-overrides.prompt.md` | Copilot prompt for creating environment override files |
| `<artifact-dir>/` | Empty artifact directory (default: `./apim-artifacts`) |
Expand All @@ -118,7 +119,7 @@ If you pass `--cli-package <path>`, the tarball is copied into a `.apiops/` dire

## Next steps after init

1. **Set up identity** — Follow the generated `IDENTITY-SETUP-*.md` guide to configure Azure credentials for your CI/CD platform. Or use the `.github/prompts/apiops-setup-identity.prompt.md` Copilot prompt.
1. **Set up identity** — Follow the generated `APIOPS-*-IDENTITY-SETUP.md` guide or provider-specific Copilot prompt to configure Azure credentials for your CI/CD platform.
2. **Extract your first snapshot** — Run [`apiops extract`](./extract.md) to pull your current APIM configuration into the artifact directory.
3. **Configure filters** — Edit `configuration.extractor.yaml` to control which resources are extracted. Use the `.github/prompts/apiops-configure-filter.prompt.md` Copilot prompt for guided setup.
4. **Commit and push** — Check the generated files into version control.
Expand Down
83 changes: 83 additions & 0 deletions docs/guides/prompt-files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Prompt Files

APIOps provides [prompt files](https://code.visualstudio.com/docs/copilot/copilot-customization#_reusable-prompt-files-experimental) that guide GitHub Copilot through common APIOps configuration tasks. These are reusable `.prompt.md` files that give Copilot the context it needs to help you configure extraction filters, environment overrides, and CI/CD identity setup.

## What are prompt files?

Prompt files are markdown files with a `.prompt.md` extension that provide instructions and context to GitHub Copilot. When you open a prompt file in VS Code and invoke Copilot, it uses the file's content as context to guide you through a task interactively.

## Available prompt files

| File | Description |
|------|-------------|
| `apiops-configure-filter.prompt.md` | Guides Copilot through creating a `configuration.extractor.yaml` filter file to control which API Management resources are extracted. See also: [Filtering resources guide](./filtering-resources.md) |
| `apiops-configure-overrides.prompt.md` | Guides Copilot through creating environment override files (`configuration.<env>.yaml`) for promoting APIs across environments. See also: [Environment overrides guide](./environment-overrides.md) |
| `apiops-setup-workflow-identity-github-actions.prompt.md` | Walks through setting up Azure identity (app registration, federated credentials, RBAC) for GitHub Actions CI/CD pipelines. |
| `apiops-setup-workflow-identity-azure-devops.prompt.md` | Walks through setting up Azure identity (app registration, federated credentials, RBAC) for Azure DevOps CI/CD pipelines. |

## Getting prompt files

### Via `apiops init` (recommended)

The easiest way to get prompt files is to run [`apiops init`](../commands/init.md), which scaffolds your repository with prompt files already in place at `.github/prompts/`.

### Download individually

If you already have an APIOps repository and want to add prompt files without re-running init, you can download them directly from this repository.

#### Bash

```bash
# Resolve repo root so this works from any subdirectory
repo_root="$(git rev-parse --show-toplevel)"

# Create the prompts directory
mkdir -p "${repo_root}/.github/prompts"

# Available prompt files — remove any you don't need
files=(
"apiops-configure-filter.prompt.md"
"apiops-configure-overrides.prompt.md"
# Remove the identity setup prompt that doesn't apply to your CI provider:
"apiops-setup-workflow-identity-github-actions.prompt.md"
"apiops-setup-workflow-identity-azure-devops.prompt.md"
)

base_url="https://raw.githubusercontent.com/Azure/apiops-cli/main/src/templates/copilot"

for file in "${files[@]}"; do
curl -sL "${base_url}/${file}" -o "${repo_root}/.github/prompts/${file}"
echo "Downloaded ${file}"
done
```

#### PowerShell

```powershell
# Resolve repo root so this works from any subdirectory
$repoRoot = git rev-parse --show-toplevel

# Create the prompts directory
New-Item -ItemType Directory -Path "$repoRoot/.github/prompts" -Force | Out-Null

# Available prompt files — remove any you don't need
$files = @(
"apiops-configure-filter.prompt.md"
"apiops-configure-overrides.prompt.md"
# Remove the identity setup prompt that doesn't apply to your CI provider:
"apiops-setup-workflow-identity-github-actions.prompt.md"
"apiops-setup-workflow-identity-azure-devops.prompt.md"
)

$baseUrl = "https://raw.githubusercontent.com/Azure/apiops-cli/main/src/templates/copilot"

foreach ($file in $files) {
Invoke-WebRequest -Uri "$baseUrl/$file" -OutFile "$repoRoot/.github/prompts/$file"
Write-Host "Downloaded $file"
}
```

## Further reading

- [VS Code: Reusable prompt files](https://code.visualstudio.com/docs/copilot/copilot-customization#_reusable-prompt-files-experimental)
- [GitHub Copilot in the CLI](https://docs.github.com/en/copilot/github-copilot-in-the-cli/using-github-copilot-in-the-cli)
18 changes: 9 additions & 9 deletions docs/walkthrough/air-gapped-azure-devops-local-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ This generates:
| File | Purpose |
|------|---------|
| `package.json` | Declares the CLI as a dependency |
| `pipelines/run-extractor.yaml` | Extract pipeline |
| `pipelines/run-publisher.yaml` | Publish pipeline |
| `.azdo/pipelines/run-apiops-extractor.yml` | Extract pipeline |
| `.azdo/pipelines/run-apiops-publisher.yml` | Publish pipeline |
| `configuration.*.yaml` | Override templates |

Follow the remaining instructions listed in created `IDENTITY-SETUP-AZDO.md` or run `/apiops-setup-identity` prompt. This creates the necessary variable groups and and service connections.
Follow the remaining instructions listed in created `APIOPS-PIPELINE-IDENTITY-SETUP.md` or run `/apiops-setup-pipeline-identity` prompt. This creates the necessary variable groups and service connections.

### 2.2 Generate the Lock File

Expand All @@ -178,7 +178,7 @@ This creates `package-lock.json`. Commit it — the lock file is **required** fo

### 2.3 Modify Pipelines for Air-Gapped Operation

The generated pipelines (`pipelines/run-extractor.yaml` and `pipelines/run-publisher.yaml`) need the following edits:
The generated pipelines (`.azdo/pipelines/run-apiops-extractor.yml` and `.azdo/pipelines/run-apiops-publisher.yml`) need the following edits:

| Edit | What to Change |
|------|----------------|
Expand Down Expand Up @@ -207,17 +207,17 @@ Commit the files required to run the local-registry workflow on self-hosted agen
| `.npmrc` | Points npm to the local Azure Artifacts feed (`registry=...`, `always-auth=true`). |
| `package.json` | Declares the CLI dependency. |
| `package-lock.json` | Required for deterministic installs with `npm ci`. |
| `pipelines/run-extractor.yaml` | Azure DevOps extract pipeline definition. |
| `pipelines/run-publisher.yaml` | Azure DevOps publish pipeline definition. |
| `.azdo/pipelines/run-apiops-extractor.yml` | Azure DevOps extract pipeline definition. |
| `.azdo/pipelines/run-apiops-publisher.yml` | Azure DevOps publish pipeline definition. |
| `configuration.*.yaml` | Generated environment override templates. |

```bash
git add \
.npmrc \
package.json \
package-lock.json \
pipelines/run-extractor.yaml \
pipelines/run-publisher.yaml \
.azdo/pipelines/run-apiops-extractor.yml \
.azdo/pipelines/run-apiops-publisher.yml \
configuration.*.yaml
git commit -m "chore: commit local-registry apiops bootstrap files"
git push
Expand Down Expand Up @@ -245,7 +245,7 @@ Verify the following:

## 4 - Finish `apiops init` for pipeline

If not already done, while on the air-gapped network, follow the remaining instructions listed in created `IDENTITY-SETUP-AZDO.md`. This creates the necessary variable groups and and service connections.
If not already done, while on the air-gapped network, follow the remaining instructions listed in created `APIOPS-PIPELINE-IDENTITY-SETUP.md`. This creates the necessary variable groups and service connections.

---

Expand Down
18 changes: 9 additions & 9 deletions docs/walkthrough/air-gapped-azure-devops-offline-tarball.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ This command generates:
| File | Purpose |
|------|---------|
| `package.json` | Declares the CLI as a `file:` dependency pointing at the tarball |
| `pipelines/run-extractor.yaml` | Extract pipeline |
| `pipelines/run-publisher.yaml` | Publish pipeline |
| `.azdo/pipelines/run-apiops-extractor.yml` | Extract pipeline |
| `.azdo/pipelines/run-apiops-publisher.yml` | Publish pipeline |
| `configuration.*.yaml` | Override templates |

Follow the remaining instructions listed in created `IDENTITY-SETUP-AZDO.md` or run `/apiops-setup-identity` prompt. This creates the necessary variable groups and and service connections.
Follow the remaining instructions listed in created `APIOPS-PIPELINE-IDENTITY-SETUP.md` or run `/apiops-setup-pipeline-identity` prompt. This creates the necessary variable groups and service connections.

### 2.2 Generate the Lock File and Pre-Stage the npm Cache

Expand All @@ -97,7 +97,7 @@ npm ci # populates ~/.npm/_cacache/ with every package the lock file refe

### 2.3 Modify Pipelines for Air-Gapped Operation

The generated pipelines (`pipelines/run-extractor.yaml` and `pipelines/run-publisher.yaml`) need the following edits:
The generated pipelines (`.azdo/pipelines/run-apiops-extractor.yml` and `.azdo/pipelines/run-apiops-publisher.yml`) need the following edits:

| Edit | What to Change |
|------|----------------|
Expand All @@ -114,17 +114,17 @@ For the offline-tarball workflow, commit the files that make the pipeline fully
| `.apiops/peterhauge-apiops-cli-<version>.tgz` | CLI package consumed by the pipelines. |
| `package.json` | Contains the `file:` dependency pointing to the tarball. |
| `package-lock.json` | Required for deterministic offline installs with `npm ci --offline`. |
| `pipelines/run-extractor.yaml` | Azure DevOps extract pipeline definition. |
| `pipelines/run-publisher.yaml` | Azure DevOps publish pipeline definition. |
| `.azdo/pipelines/run-apiops-extractor.yml` | Azure DevOps extract pipeline definition. |
| `.azdo/pipelines/run-apiops-publisher.yml` | Azure DevOps publish pipeline definition. |
| `configuration.*.yaml` | Generated environment override templates. |

```bash
git add \
.apiops/peterhauge-apiops-cli-*.tgz \
package.json \
package-lock.json \
pipelines/run-extractor.yaml \
pipelines/run-publisher.yaml \
.azdo/pipelines/run-apiops-extractor.yml \
.azdo/pipelines/run-apiops-publisher.yml \
configuration.*.yaml
git commit -m "chore: commit offline-tarball apiops bootstrap files"
git push
Expand Down Expand Up @@ -164,7 +164,7 @@ tar -xzf npm-cacache.tar.gz -C ~/.npm

## 4 - Finish `apiops init` for pipeline

If not already done, while on the air-gapped network, follow the remaining instructions listed in created `IDENTITY-SETUP-AZDO.md`. This creates the necessary variable groups and and service connections.
If not already done, while on the air-gapped network, follow the remaining instructions listed in created `APIOPS-PIPELINE-IDENTITY-SETUP.md`. This creates the necessary variable groups and service connections.

## 5 — Commit and Validate

Expand Down
4 changes: 0 additions & 4 deletions scripts/embed-markdown-templates.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ const templates = [
exportName: 'copilotConfigureOverridesPromptTemplate',
path: 'src/templates/copilot/configure-overrides-prompt.md',
},
{
exportName: 'azureDevOpsIdentitySetupCoreTemplate',
path: 'src/templates/shared/identity-setup-azure-devops-core.md',
},
{
exportName: 'githubActionsIdentityGuideTemplate',
path: 'src/templates/identity/identity-guide-github-actions.md',
Expand Down
10 changes: 5 additions & 5 deletions src/cli/init-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@ export function createInitCommand(): Command {
generatedFiles.directories.forEach((dir) => logger.info(` - ${dir.startsWith('./') ? dir : './' + dir}`));

// Determine which CI provider was actually used by checking generated files
const isGitHub = allFiles.some((f) => f.includes('IDENTITY-SETUP-GITHUB.md'));
const isGitHub = allFiles.some((f) => f.includes('APIOPS-WORKFLOW-IDENTITY-SETUP.md'));

logger.info('\nNext steps:');
logger.info(' 1. Review and customize the generated configuration files');
logger.info(' 2. Commit the generated files to your repository');
logger.info(' 3. Set up CI/CD identity authentication:');
if (isGitHub) {
logger.info(' - Follow ./IDENTITY-SETUP-GITHUB.md for manual setup, OR');
logger.info(' - Open ./.github/prompts/apiops-setup-identity.prompt.md with GitHub Copilot for guided setup');
logger.info(' - Follow APIOPS-WORKFLOW-IDENTITY-SETUP.md for manual setup, OR');
logger.info(' - Use github/prompts/apiops-setup-workflow-identity.prompt.md prompt file with GitHub Copilot for guided setup');
} else {
logger.info(' - Follow ./IDENTITY-SETUP-AZDO.md for manual setup, OR');
logger.info(' - Open ./.github/prompts/apiops-setup-identity.prompt.md with GitHub Copilot for guided setup');
logger.info(' - Follow ./APIOPS-PIPELINE-IDENTITY-SETUP.md for manual setup, OR');
logger.info(' - Open ./.github/prompts/apiops-setup-pipeline-identity.prompt.md with GitHub Copilot for guided setup');
}
logger.info('');
} catch (error) {
Expand Down
64 changes: 7 additions & 57 deletions src/services/identity-guide-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,26 @@
// Licensed under the MIT license.
/**
* Identity setup guide generator
* Step-by-step instructions for service principal, RBAC, federated credentials,
* pipeline secrets/service connections. Optional az CLI automation per FR-021.
* Returns the static manual guide content for the selected CI provider.
*/

import {
azureDevOpsIdentitySetupCoreTemplate,
azureDevOpsIdentityGuideTemplate,
githubActionsIdentityGuideTemplate,
} from '../templates/generated/embedded-markdown.js';
import { renderTemplate } from '../lib/render-template.js';

export interface IdentityGuideService {
generateGitHubActionsGuide(
subscriptionId: string,
resourceGroup: string,
environments: string[]
): string;

generateAzureDevOpsGuide(
environments: string[]
): string;
generateGitHubActionsGuide(): string;
generateAzureDevOpsGuide(): string;
}

class IdentityGuideServiceImpl implements IdentityGuideService {
generateGitHubActionsGuide(
subscriptionId: string,
resourceGroup: string,
environments: string[]
): string {
const federatedCredentialsPerEnvironment = environments.map((env) => `az ad app federated-credential create \\
--id "$APP_ID" \\
--parameters '{
"name": "github-env-${env}",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:'"$GITHUB_ORG"'/'"$GITHUB_REPO"':environment:${env}",
"audiences": ["api://AzureADTokenExchange"]
}'`).join('\n\n');

const environmentSecrets = environments.map((env) => `
**For ${env} environment:**
- \`APIM_RESOURCE_GROUP_${env.toUpperCase()}\`: Resource group for ${env}
- \`APIM_SERVICE_NAME_${env.toUpperCase()}\`: APIM service name for ${env}
`).join('\n');

return renderTemplate(githubActionsIdentityGuideTemplate, {
SUBSCRIPTION_ID: subscriptionId,
RESOURCE_GROUP: resourceGroup,
FEDERATED_CREDENTIALS_PER_ENV: federatedCredentialsPerEnvironment,
ENVIRONMENT_SECRETS: environmentSecrets,
});
generateGitHubActionsGuide(): string {
return githubActionsIdentityGuideTemplate;
}

generateAzureDevOpsGuide(
environments: string[]
): string {
const environmentsArrayPowerShell = environments
.map((environment) => `"${environment}"`)
.join(', ');
const environmentsArrayBash = environments
.map((environment) => `"${environment}"`)
.join(' ');

const coreSteps = renderTemplate(azureDevOpsIdentitySetupCoreTemplate, {
ENVIRONMENTS_ARRAY_POWERSHELL: environmentsArrayPowerShell,
ENVIRONMENTS_ARRAY_BASH: environmentsArrayBash,
});

return renderTemplate(azureDevOpsIdentityGuideTemplate, {
AZURE_DEVOPS_CORE_STEPS: coreSteps,
});
generateAzureDevOpsGuide(): string {
return azureDevOpsIdentityGuideTemplate;
}
}

Expand Down
Loading