Skip to content

Commit

Permalink
Migrate Azure samples into repo #3085 (#3089)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Oct 12, 2024
1 parent d8a5b8f commit 1a221af
Show file tree
Hide file tree
Showing 15 changed files with 339 additions and 71 deletions.
44 changes: 0 additions & 44 deletions .github/ISSUE_TEMPLATE/bug-report.md

This file was deleted.

1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug-report.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Bug report
description: Report errors or unexpected behavior
title: '[BUG] <title>'
type: Bug
labels: ['bug', 'Needs: Triage :mag:']
body:
- type: input
Expand Down
21 changes: 0 additions & 21 deletions .github/ISSUE_TEMPLATE/feature-request.md

This file was deleted.

1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature-request.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Feature request
description: Suggest an idea
title: '[FEATURE] <title>'
type: Feature
labels: ['enhancement', 'Needs: Triage :mag:']
body:
- type: textarea
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/rule-request.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Rule request
description: Suggest the creation of a new or to update an existing rule
title: '[RULE] <title>'
type: Feature
labels: ['rule', 'Needs: Triage :mag:']
body:
- type: input
Expand Down
43 changes: 43 additions & 0 deletions .github/ISSUE_TEMPLATE/sample_proposal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Sample proposal
description: Make a proposal for the community to add a new sample
title: '[Sample]: '
type: Feature
labels: ['sample', 'Needs: Triage :mag:']
body:
- type: markdown
attributes:
value: |
Fill out this form below to suggest a new sample to be added by the community.
- type: dropdown
id: exiting-sample-check
attributes:
label: |
Have you checked this sample does not already exist in the repository?
Samples are stored in `samples/` in the root of the repository.
options:
- 'Yes'
- 'No'
validations:
required: true
- type: textarea
id: why-the-sample
attributes:
label: Why is the sample needed?
description: Explain why the sample is needed. If there is a existing sample, explain why it cannot be updated to meet your needs and why a new one must be created.
validations:
required: true
- type: input
id: sample-path
attributes:
label: Sample path
description: The path to the new sample.
placeholder: samples/rules/name
validations:
required: true
- type: textarea
id: sample-description
attributes:
label: Describe the sample
description: A clear and concise description of the sample.
validations:
required: true
4 changes: 4 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ See [upgrade notes][1] for helpful information when upgrading from previous vers

## Unreleased

- Engineering:
- Migrated Azure samples into PSRule for Azure by @BernieWhite.
[#3085](https://github.com/Azure/PSRule.Rules.Azure/issues/3085)

## v1.39.1

What's changed since v1.39.0:
Expand Down
130 changes: 130 additions & 0 deletions docs/hooks/samples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# NOTES:
# This file implements generation of samples TOC.

import logging
import os
import re

from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import Files
from mkdocs.structure.pages import Page

log = logging.getLogger(f"mkdocs")

#
# Hooks
#

def on_pre_build(config: MkDocsConfig):
'''Hook on_pre_build event.'''

return generate_samples_toc_fragment(config)

def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files: Files) -> str:
'''Hook on_page_markdown event.'''

return samples_shortcode(markdown, page, config, files)

#
# Supporting functions
#

def generate_samples_toc_fragment(config: MkDocsConfig):
'''Generate a markdown fragment that will be injected into files containing a short code.'''

# Get the samples directory which is in a parent directory.
repo_root_dir = os.path.join(config.docs_dir, "..")
samples_dir = os.path.join(repo_root_dir, "samples", "rules")

# Get the base repo URI.
base_repo_uri = config.repo_url
samples_repo_uri = f"{base_repo_uri}tree/main/samples/rules"

# Generate the TOC fragment, each sample exists as a README.md file in a subdirectory.
toc = []
for root, dirs, _ in os.walk(samples_dir):
for dir in dirs:
if dir == "common":
continue

current_sample_dir = os.path.join(root, dir)
title = ""
description = []
author = ""
block = "none"

# Read the file to get the title and lines until the first empty line.
with open(os.path.join(current_sample_dir, "README.md"), "r") as f:

# Read annotations and header.
for line in f:
if (block == "none" or block == "metadata") and line.strip() == "---":
if block == "none":
block = "metadata"
elif block == "metadata":
block = "header"

continue

if block == "metadata" and line.startswith("author:") and line.strip("author:").strip() != "":
author = line.strip("author:").strip()
author = f"@{author}"
continue

if block == "metadata":
continue

if block == "header" or line.startswith("# "):
if line.startswith("# "):
title = line.strip("# ").strip()
block = "header"
continue

if line.strip() == "":
continue

# Keep reading until the first H2 heading.
if line.startswith("## "):
break

description.append(line.strip())

# Write the TOC entry as a row in a markdown table.
toc.append(f"| [{title}]({'/'.join([samples_repo_uri, dir])}) | {' '.join(description)} | {author} |")

# Write the TOC to a markdown file in a table with title and description.
toc_file = os.path.join(repo_root_dir, "out", "samples_toc.md")
with open(toc_file, "w") as f:
f.write("| Title | Description | Author |\n")
f.write("| ----- | ----------- | ------ |\n")
for entry in toc:
f.write(f"{entry}\n")

def samples_shortcode(markdown: str, page: Page, config: MkDocsConfig, files: Files) -> str:
'''Replace samples shortcodes in markdown.'''

# Callback for regular expression replacement.
def replace(match: re.Match) -> str:
type, args = match.groups()
args = args.strip()
if type == "rules":
return _samples_rules_fragment(args, page, config, files)

raise RuntimeError(f"Unknown shortcode samples:{type}")

# Replace samples shortcodes.
return re.sub(
r"<!-- samples:(\w+)(.*?) -->",
replace, markdown, flags = re.I | re.M
)

def _samples_rules_fragment(args: str, page: Page, config: MkDocsConfig, files: Files) -> str:
'''Replace samples shortcode with rules fragment.'''

# Get the TOC fragment from the file.
toc_file = os.path.join(config.docs_dir, "..", "out", "samples_toc.md")
with open(toc_file, "r") as f:
return f.read()
13 changes: 7 additions & 6 deletions docs/samples.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
reviewed: 2023-04-23
reviewed: 2024-10-10
author: BernieWhite
discussion: false
link_users: true
---

# Samples
Expand All @@ -22,11 +23,11 @@ This repository contains the following samples for PSRule for Azure:

[1]: https://aka.ms/ps-rule-azure-quickstart

## PSRule samples
## Community samples

[:octicons-repo-24: Samples][2]
### Custom rules and conventions

A community collection of samples for PSRule.
This repository includes samples for Azure as well as other use cases.
The following sample rules and conventions for Azure are available.
You can use these samples as a starting point for developing your own custom rules.

[2]: https://github.com/microsoft/PSRule-samples
<!-- samples:rules -->
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ plugins:
setup/configuring-options.md: setup/index.md

hooks:
- docs/hooks/samples.py
- docs/hooks/shortcodes.py
- docs/hooks/metadata.py
- docs/hooks/aliases.py
Expand Down
49 changes: 49 additions & 0 deletions samples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Samples

You have reached the Azure community samples for PSRule.
If you have a question about these samples, please start a discussion on GitHub.

These samples are broken into the following categories:

- `rules/` - Sample rules not shipped with PSRule for Azure.
These samples do no align to Azure Well-Architected Framework (WAF),
and should be considered as a starting point for custom rules.

## Contributing samples

Additional samples can be contributed.
Please use the following structure for your `README.md`.
Replace the comment placeholders with details about your sample.

```markdown
---
author: <github_username>
---

# <title>

<!-- a short one line description which will be included in table of contents -->

## Summary

<!-- describe what your sample does -->

## Usage

<!-- how to use your sample -->

## References

<!-- references to docs that help explain the detail. here is a few to get started, but remove if they are not relevant. -->

- [Using custom rules](https://azure.github.io/PSRule.Rules.Azure/customization/using-custom-rules/)
- [Conventions](https://microsoft.github.io/PSRule/v2/concepts/PSRule/en-US/about_PSRule_Conventions/#including-with-options)
```

When contributing a sample:

- README.md:
- Please update `author:` with your GitHub username to be credited in the table of contents.
- Please give the sample a title.
- Store each sample in a unique folder. i.e. `samples/<category>/<your_sample>`.
- Prefix your sample rule or convention files with your folder name. i.e. `<your_sample>.Rule.ps1`.
22 changes: 22 additions & 0 deletions samples/rules/APIManagementPolicy/APIManagementPolicy.Rule.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Synopsis: Imports API Management policy XML files in for analysis.
Export-PSRuleConvention 'APIManagementPolicy.Import' -Initialize {
$policies = @(Get-ChildItem -Path 'policies/' -Include '*.xml' -Recurse -File | ForEach-Object {
$name = $_.Name
[PSCustomObject]@{
Name = $name
Content = [Xml](Get-Content -Path $_.FullName -Raw)
}
})
$PSRule.ImportWithType('Azure.APIM.PolicyContent', $policies)
}

# Synopsis: Checks that validate-jwt element exists in the policy.
Rule 'APIManagementPolicy.ValidateJwt' -Type 'Azure.APIM.PolicyContent' {
$policy = @([Xml]$TargetObject.Content.SelectNodes('//validate-jwt'))

# Check that validate-jwt is used in the policy.
$Assert.Greater($policy, '.', 1)
}
Loading

0 comments on commit 1a221af

Please sign in to comment.