|
| 1 | +# GitHub Copilot Instructions for PnP PowerShell |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +**PnP PowerShell** is a .NET 8 based PowerShell Module providing over 750 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Entra ID (Azure AD), and more. |
| 6 | + |
| 7 | +This is a cross-platform module (Windows, macOS, Linux) that requires PowerShell 7.4.0 or newer and is based on .NET 8.0. It is the successor of the PnP-PowerShell module which only worked on Windows PowerShell. |
| 8 | + |
| 9 | +### Key Characteristics |
| 10 | +- **Open-source community project** - No SLA or direct Microsoft support |
| 11 | +- **750+ cmdlets** for Microsoft 365 services |
| 12 | +- **Cross-platform** - Works on Windows, Linux, and macOS |
| 13 | +- **Modern stack** - .NET 8 / C# 12 / PowerShell 7.4+ |
| 14 | +- **Active development** - Nightly builds and regular releases |
| 15 | + |
| 16 | +## Tech Stack |
| 17 | + |
| 18 | +- **Language**: C# 12 |
| 19 | +- **Framework**: .NET 8.0 |
| 20 | +- **Target Platform**: PowerShell 7.4+ |
| 21 | +- **Build System**: .NET SDK 8 |
| 22 | +- **Dependencies**: |
| 23 | + - PnP Framework |
| 24 | + - PnP Core SDK |
| 25 | + - Microsoft.SharePoint.Client (CSOM) |
| 26 | + - Microsoft Graph SDK |
| 27 | + |
| 28 | +## Repository Structure |
| 29 | + |
| 30 | +``` |
| 31 | +/ |
| 32 | +├── .github/ # GitHub workflows and configurations |
| 33 | +├── build/ # Build scripts (PowerShell) |
| 34 | +├── src/ |
| 35 | +│ ├── Commands/ # Cmdlet implementations (organized by feature) |
| 36 | +│ │ ├── Admin/ # Tenant administration cmdlets |
| 37 | +│ │ ├── Apps/ # App catalog cmdlets |
| 38 | +│ │ ├── Lists/ # List management cmdlets |
| 39 | +│ │ ├── Sites/ # Site collection cmdlets |
| 40 | +│ │ ├── Graph/ # Microsoft Graph cmdlets |
| 41 | +│ │ └── ... # Many other feature areas |
| 42 | +│ ├── ALC/ # Assembly Load Context for dependency isolation |
| 43 | +│ └── Resources/ # Embedded resources |
| 44 | +├── documentation/ # Markdown documentation for each cmdlet |
| 45 | +├── pages/ # Documentation website content |
| 46 | +└── samples/ # Sample scripts and usage examples |
| 47 | +``` |
| 48 | + |
| 49 | +## Cmdlet Development Patterns |
| 50 | + |
| 51 | +### Cmdlet Class Structure |
| 52 | + |
| 53 | +All cmdlets should follow this pattern: |
| 54 | + |
| 55 | +```csharp |
| 56 | +using System.Management.Automation; |
| 57 | +using Microsoft.SharePoint.Client; |
| 58 | +using PnP.PowerShell.Commands.Base; |
| 59 | +using PnP.PowerShell.Commands.Base.PipeBinds; |
| 60 | +using PnP.PowerShell.Commands.Attributes; |
| 61 | + |
| 62 | +namespace PnP.PowerShell.Commands.FeatureArea |
| 63 | +{ |
| 64 | + [Cmdlet(VerbsCommon.Get, "PnPSomething")] |
| 65 | + [OutputType(typeof(SomeType))] |
| 66 | + [RequiredApiApplicationPermissions("sharepoint/Sites.Read.All")] |
| 67 | + [RequiredApiDelegatedPermissions("sharepoint/AllSites.Read")] |
| 68 | + public class GetSomething : PnPWebRetrievalsCmdlet<SomeType> |
| 69 | + { |
| 70 | + [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] |
| 71 | + public SomePipeBind Identity { get; set; } |
| 72 | + |
| 73 | + protected override void ExecuteCmdlet() |
| 74 | + { |
| 75 | + // Implementation |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | +``` |
| 80 | + |
| 81 | +### Key Conventions |
| 82 | + |
| 83 | +1. **Cmdlet Naming**: Always use `PnP` prefix (e.g., `Get-PnPList`, `Set-PnPSite`) |
| 84 | +2. **Verb Usage**: Follow PowerShell approved verbs (`Get`, `Set`, `Add`, `Remove`, `New`, etc.) |
| 85 | +3. **Namespace Organization**: Group cmdlets by feature area in `PnP.PowerShell.Commands.FeatureArea` |
| 86 | +4. **Base Classes**: |
| 87 | + - `PnPWebRetrievalsCmdlet<T>`: For cmdlets that return SharePoint objects with retrievals |
| 88 | + - `PnPGraphCmdlet`: For cmdlets that use Microsoft Graph |
| 89 | + - `PnPAdminCmdlet`: For tenant admin operations |
| 90 | + - `PnPSharePointCmdlet`: For general SharePoint operations |
| 91 | + |
| 92 | +5. **Attributes**: |
| 93 | + - Always include `[RequiredApiApplicationPermissions]` and `[RequiredApiDelegatedPermissions]` to document required permissions |
| 94 | + - Use `[OutputType]` to specify return type |
| 95 | + - Use `[Parameter]` attributes with appropriate settings (Mandatory, ValueFromPipeline, Position) |
| 96 | + |
| 97 | +6. **PipeBinds**: Use PipeBind classes for flexible parameter input (e.g., `ListPipeBind` accepts name, ID, or object) |
| 98 | + |
| 99 | +7. **Error Handling**: |
| 100 | + - Use `ThrowTerminatingError()` for fatal errors |
| 101 | + - Use `WriteWarning()` for non-fatal issues |
| 102 | + - Use `WriteVerbose()` for detailed logging |
| 103 | + |
| 104 | +8. **Resource Strings**: Store error messages in `Resources.resx` and reference via `Resources.MessageName` |
| 105 | + |
| 106 | +9. **Backward Compatibility**: When renaming a cmdlet or fixing a typo in a cmdlet name, always add an `[Alias()]` attribute with the old cmdlet name to maintain backward compatibility. Example: |
| 107 | + ```csharp |
| 108 | + [Cmdlet(VerbsCommon.Get, "PnPEntraIDAppSitePermission")] |
| 109 | + [Alias("Get-PnPAzureADAppSitePermission")] |
| 110 | + public class GetEntraIDAppSitePermission : PnPGraphCmdlet |
| 111 | + ``` |
| 112 | + |
| 113 | +## Coding Standards |
| 114 | + |
| 115 | +### C# Style Guide |
| 116 | + |
| 117 | +1. **Indentation**: Use tabs (not spaces) - this repository uses tabs for indentation |
| 118 | +2. **Braces**: Opening brace on same line for methods, properties; new line for classes |
| 119 | +3. **Naming**: |
| 120 | + - PascalCase for classes, methods, properties, public fields |
| 121 | + - camelCase for parameters, local variables, private fields |
| 122 | + - Prefix interfaces with `I` (e.g., `IListItem`) |
| 123 | +4. **Null Checking**: Use null-conditional operators (`?.`, `??`) where appropriate |
| 124 | +5. **LINQ**: Prefer LINQ for collection operations |
| 125 | +6. **Async/Await**: Use async patterns for asynchronous operations |
| 126 | + |
| 127 | +### Code Analysis |
| 128 | +- EnforceCodeStyleInBuild is enabled |
| 129 | +- EnableNETAnalyzers is enabled |
| 130 | +- Address all warnings before committing |
| 131 | + |
| 132 | +## Common Patterns |
| 133 | + |
| 134 | +### Retrieving SharePoint Objects with Specific Properties |
| 135 | + |
| 136 | +```csharp |
| 137 | +DefaultRetrievalExpressions = [ |
| 138 | + l => l.Id, |
| 139 | + l => l.Title, |
| 140 | + l => l.RootFolder.ServerRelativeUrl |
| 141 | +]; |
| 142 | + |
| 143 | +var list = Identity.GetList(CurrentWeb); |
| 144 | +list?.EnsureProperties(RetrievalExpressions); |
| 145 | +WriteObject(list); |
| 146 | +``` |
| 147 | + |
| 148 | +### Working with PipeBinds |
| 149 | + |
| 150 | +```csharp |
| 151 | +// Accepts ID, name, or object instance |
| 152 | +[Parameter(Mandatory = true)] |
| 153 | +public ListPipeBind Identity { get; set; } |
| 154 | + |
| 155 | +// In ExecuteCmdlet |
| 156 | +var list = Identity.GetList(CurrentWeb); |
| 157 | +``` |
| 158 | + |
| 159 | +### Using Graph API |
| 160 | + |
| 161 | +```csharp |
| 162 | +public class GetGraphSomething : PnPGraphCmdlet |
| 163 | +{ |
| 164 | + protected override void ExecuteCmdlet() |
| 165 | + { |
| 166 | + var result = GraphHelper.GetAsync<SomeType>( |
| 167 | + Connection, |
| 168 | + "/v1.0/endpoint", |
| 169 | + AccessToken |
| 170 | + ).GetAwaiter().GetResult(); |
| 171 | + |
| 172 | + WriteObject(result); |
| 173 | + } |
| 174 | +} |
| 175 | +``` |
| 176 | + |
| 177 | +## Documentation |
| 178 | + |
| 179 | +Every cmdlet must have: |
| 180 | +1. **XML Documentation Comments** in the C# source |
| 181 | +2. **Markdown Documentation** in `/documentation/{Cmdlet-Name}.md` |
| 182 | +3. **Examples** showing typical usage |
| 183 | + |
| 184 | +### Markdown Documentation Template |
| 185 | + |
| 186 | +```markdown |
| 187 | +# Get-PnPSomething |
| 188 | + |
| 189 | +## Description |
| 190 | +Brief description of what the cmdlet does. |
| 191 | + |
| 192 | +## Syntax |
| 193 | + |
| 194 | +### Parameter Set 1 |
| 195 | +```powershell |
| 196 | +Get-PnPSomething [-Identity <String>] [-Connection <PnPConnection>] |
| 197 | +``` |
| 198 | + |
| 199 | +## Examples |
| 200 | + |
| 201 | +### Example 1 |
| 202 | +```powershell |
| 203 | +Get-PnPSomething -Identity "Value" |
| 204 | +``` |
| 205 | +Description of what this example does. |
| 206 | + |
| 207 | +## Parameters |
| 208 | + |
| 209 | +### -Identity |
| 210 | +Description of the parameter. |
| 211 | + |
| 212 | +## Outputs |
| 213 | + |
| 214 | +### Type |
| 215 | +Description of output type. |
| 216 | +``` |
| 217 | +
|
| 218 | +## Do's and Don'ts |
| 219 | +
|
| 220 | +### Do's |
| 221 | +✅ Follow PowerShell naming conventions (Verb-PnPNoun) |
| 222 | +✅ Use appropriate base classes (PnPWebRetrievalsCmdlet, PnPGraphCmdlet, etc.) |
| 223 | +✅ Include all required permission attributes |
| 224 | +✅ Write comprehensive parameter documentation |
| 225 | +✅ Add examples to documentation |
| 226 | +✅ Use PipeBind classes for flexible parameter input |
| 227 | +✅ Handle errors gracefully with meaningful messages |
| 228 | +✅ Use existing helper methods and utilities from base classes |
| 229 | +✅ Follow the existing code structure and patterns |
| 230 | +✅ Use `ClientContext.ExecuteQueryRetry()` instead of `ExecuteQuery()` for resilience |
| 231 | +✅ Add `[Alias()]` attribute when renaming cmdlets to maintain backward compatibility |
| 232 | +
|
| 233 | +### Don'ts |
| 234 | +❌ Don't add cmdlets without proper documentation |
| 235 | +❌ Don't use `Console.WriteLine()` - use `WriteObject()`, `WriteWarning()`, `WriteVerbose()` |
| 236 | +❌ Don't hardcode credentials or sensitive data |
| 237 | +❌ Don't break backward compatibility without discussion |
| 238 | +❌ Don't add unnecessary dependencies |
| 239 | +❌ Don't commit commented-out code |
| 240 | +❌ Don't ignore compiler warnings |
| 241 | +❌ Don't use deprecated APIs or methods |
| 242 | +❌ Don't create cmdlets that bypass standard authentication flows |
| 243 | +❌ Don't use `ExecuteQuery()` directly - use `ExecuteQueryRetry()` for automatic retry logic |
| 244 | +
|
| 245 | +## Contributing Workflow |
| 246 | +
|
| 247 | +1. **Fork** the repository |
| 248 | +2. **Clone** your fork locally |
| 249 | +3. **Create a branch** for your feature/fix from `dev` branch |
| 250 | +4. **Make changes** following the patterns above |
| 251 | +5. **Update documentation** in `/documentation/` folder |
| 252 | +6. **Commit** with clear, descriptive messages |
| 253 | +7. **Push** to your fork |
| 254 | +8. **Create Pull Request** to the `dev` branch |
| 255 | +
|
| 256 | +## Additional Resources |
| 257 | +
|
| 258 | +- [Main Documentation](https://pnp.github.io/powershell/) |
| 259 | +- [Getting Started Contributing](https://pnp.github.io/powershell/articles/gettingstartedcontributing.html) |
| 260 | +- [Migration Guides](https://github.com/pnp/powershell/blob/dev/MIGRATE-2.0-to-3.0.md) |
| 261 | +- [Changelog](https://github.com/pnp/powershell/blob/dev/CHANGELOG.md) |
| 262 | +
|
| 263 | +## Notes for Copilot |
| 264 | +
|
| 265 | +When generating or modifying code: |
| 266 | +- Always check existing cmdlets in the same feature area for patterns |
| 267 | +- Maintain consistency with existing code style |
| 268 | +- Consider cross-platform compatibility |
| 269 | +- Remember that this module runs in PowerShell 7.4+ (not Windows PowerShell 5.1) |
| 270 | +- Use modern C# 12 features where appropriate |
| 271 | +- Prioritize readability and maintainability |
| 272 | +- Follow the principle of least surprise for PowerShell users |
0 commit comments