Skip to content

Fix Test-EntraScript #1471

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 25 commits into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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 build/Create-EntraModule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ param (

. (Join-Path $psscriptroot "/common-functions.ps1")
. (Join-Path $psscriptroot "../src/EntraModuleBuilder.ps1")
. (Join-Path $psscriptroot "../src/Get-MissingCmds.ps1")

$moduleBuilder = [EntraModuleBuilder]::new()

Expand Down
110 changes: 110 additions & 0 deletions module/Entra/Microsoft.Entra/Test-EntraScript.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# ------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
# ------------------------------------------------------------------------------

function Test-EntraScript {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName', 'Name')]
[string[]]
$Path,

[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]
$Content,

[switch]
$Quiet
)

begin {
function Test-ScriptCommand {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[Alias('FullName')]
[string]
$Name,

[Parameter(Mandatory = $true)]
[string]
$Content,

[switch]
$Quiet,

[AllowEmptyCollection()]
[string[]]
$RequiredCommands,

[AllowEmptyCollection()]
[string[]]
$ForbiddenCommands
)

$ast = [System.Management.Automation.Language.Parser]::ParseInput($Content, [ref]$null, [ref]$null)
$allCommands = $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$allCommandNames = @($allCommands).ForEach{ $_.CommandElements[0].Value }

$findings = @()
foreach ($command in $allCommands) {
if ($command.CommandElements[0].Value -notin $ForbiddenCommands) { continue }
$findings += [PSCustomObject]@{
PSTypeName = 'Microsoft.Entra.CommandRequirement'
Name = $Name
Line = $command.Extent.StartLineNumber
Type = 'UnsupportedCommand'
Command = $command.CommandElements[0].Value
Code = $command.Extent.Text
}
}
foreach ($requiredCommand in $RequiredCommands) {
if ($requiredCommand -notin $allCommandNames) { continue }
$findings += [PSCustomObject]@{
PSTypeName = 'Microsoft.Entra.CommandRequirement'
Name = $Name
Line = -1
Type = 'RequiredCommandMissing'
Command = $requiredCommand
Code = ''
}
}

if (-not $Quiet) {
$findings
return
}

$findings -as [bool]
}

$testParam = @{
Quiet = $Quiet
ForbiddenCommands = $script:MISSING_CMDS
}
}
process {
if ($Path -and $Content) {
Test-ScriptCommand -Name @($Path)[0] -Content $Content
return
}
foreach ($entry in $Path) {
try { $resolvedPaths = Resolve-Path -Path $entry -ErrorAction Stop }
catch {
Write-Error $_
continue
}

foreach ($resolvedPath in $resolvedPaths) {
if (-not (Test-Path -Path $resolvedPath -PathType Leaf)) {
Write-Warning "Not a file: $resolvedPath"
continue
}

$scriptContent = (Get-Content -LiteralPath $resolvedPath) -join "`n"
Test-ScriptCommand -Name $resolvedPath -Content $scriptContent @testParam
}
}
}
}
110 changes: 110 additions & 0 deletions module/EntraBeta/Microsoft.Entra.Beta/Test-EntraScript.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# ------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
# ------------------------------------------------------------------------------

function Test-EntraScript {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName', 'Name')]
[string[]]
$Path,

[Parameter(ValueFromPipelineByPropertyName = $true)]
[string]
$Content,

[switch]
$Quiet
)

begin {
function Test-ScriptCommand {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[Alias('FullName')]
[string]
$Name,

[Parameter(Mandatory = $true)]
[string]
$Content,

[switch]
$Quiet,

[AllowEmptyCollection()]
[string[]]
$RequiredCommands,

[AllowEmptyCollection()]
[string[]]
$ForbiddenCommands
)

$ast = [System.Management.Automation.Language.Parser]::ParseInput($Content, [ref]$null, [ref]$null)
$allCommands = $ast.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$allCommandNames = @($allCommands).ForEach{ $_.CommandElements[0].Value }

$findings = @()
foreach ($command in $allCommands) {
if ($command.CommandElements[0].Value -notin $ForbiddenCommands) { continue }
$findings += [PSCustomObject]@{
PSTypeName = 'Microsoft.Entra.Beta.CommandRequirement'
Name = $Name
Line = $command.Extent.StartLineNumber
Type = 'UnsupportedCommand'
Command = $command.CommandElements[0].Value
Code = $command.Extent.Text
}
}
foreach ($requiredCommand in $RequiredCommands) {
if ($requiredCommand -notin $allCommandNames) { continue }
$findings += [PSCustomObject]@{
PSTypeName = 'Microsoft.Entra.Beta.CommandRequirement'
Name = $Name
Line = -1
Type = 'RequiredCommandMissing'
Command = $requiredCommand
Code = ''
}
}

if (-not $Quiet) {
$findings
return
}

$findings -as [bool]
}

$testParam = @{
Quiet = $Quiet
ForbiddenCommands = $script:MISSING_CMDS
}
}
process {
if ($Path -and $Content) {
Test-ScriptCommand -Name @($Path)[0] -Content $Content
return
}
foreach ($entry in $Path) {
try { $resolvedPaths = Resolve-Path -Path $entry -ErrorAction Stop }
catch {
Write-Error $_
continue
}

foreach ($resolvedPath in $resolvedPaths) {
if (-not (Test-Path -Path $resolvedPath -PathType Leaf)) {
Write-Warning "Not a file: $resolvedPath"
continue
}

$scriptContent = (Get-Content -LiteralPath $resolvedPath) -join "`n"
Test-ScriptCommand -Name $resolvedPath -Content $scriptContent @testParam
}
}
}
}
116 changes: 116 additions & 0 deletions module/docs/entra-powershell-beta/Test-EntraScript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
---
title: Test-EntraScript
description: This article provides details on the Test-EntraScript command.

ms.topic: reference
ms.date: 07/24/2024
ms.author: eunicewaweru
ms.reviewer: stevemutungi
manager: CelesteDG
author: msewaweru

external help file: Microsoft.Graph.Entra.Beta-help.xml
Module Name: Microsoft.Graph.Entra.Beta
online version: https://learn.microsoft.com/powershell/module/Microsoft.Graph.Entra.Beta/Test-EntraScript

schema: 2.0.0
---

# Test-EntraScript

## Synopsis

Check whether the provided script uses AzureAD commands not supported by Microsoft Entra PowerShell.

## Syntax

```powershell
Test-EntraScript [-Path] <String[]> [[-Content] <String>] [-Quiet] [<CommonParameters>]
```

## Description

Check whether the provided script uses AzureAD commands not supported by Microsoft Entra PowerShell.

## Examples

### Example 1

```powershell
Test-EntraScript -Path .\usercreation.ps1 -Quiet
```

Returns whether the script "usercreation.ps1" could run under Microsoft.Graph.Entra

### Example 2

```powershell
Get-ChildItem -Path \\contoso.com\it\code -Recurse -Filter *.ps1 | Test-EntraScript
```

Returns a list of all scripts that wouldn't run under the Microsoft.Graph.Entra module, listing each issue with line and code.

## Parameters

### -Path

Path to one or more script files to scan.
Or name of the content, when also specifying -Content

```yaml
Type: String[]
Parameter Sets: (All)
Aliases: FullName, Name

Required: True
Position: 1
Default value: None
Accept pipeline input: True (ByPropertyName, ByValue)
Accept wildcard characters: False
```

### -Content

Code content to scan.
Used when scanning code that has no file representation (for example,
straight from a repository).

```yaml
Type: String
Parameter Sets: (All)
Aliases:

Required: False
Position: 2
Default value: None
Accept pipeline input: True (ByPropertyName)
Accept wildcard characters: False
```

### -Quiet

Only return $true or $ false, based on whether the script could run under Microsoft.Graph.Entra ($true) or not ($ false)

```yaml
Type: SwitchParameter
Parameter Sets: (All)
Aliases:

Required: False
Position: Named
Default value: False
Accept pipeline input: False
Accept wildcard characters: False
```

### CommonParameters

This cmdlet supports the common parameters: `-Debug`, `-ErrorAction`, `-ErrorVariable`, `-InformationAction`, `-InformationVariable`, `-OutVariable`, `-OutBuffer`, `-PipelineVariable`, `-Verbose`, `-WarningAction`, and `-WarningVariable`. For more information, see [about_CommonParameters](https://go.microsoft.com/fwlink/?LinkID=113216).

## Inputs

## Outputs

## Notes

## Related Links
Loading