Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ function Add-EntraInheritablePermissionsToAgentIdentityBlueprint {

if ($existingEntry) {
# Overwrite the existing entry for this resourceAppId
Write-Verbose "Existing inheritable permissions found for resource '$resourceName' — overwriting..."
Write-Verbose "Existing inheritable permissions found for resource '$resourceName' - overwriting..."
$patchUrl = "$apiUrl/$($currentResourceAppId.ToString())"
Write-Debug "PATCH URL: $patchUrl"

Expand All @@ -169,8 +169,8 @@ function Add-EntraInheritablePermissionsToAgentIdentityBlueprint {
}
}
else {
# No existing entry for this resourceAppId — add it (preserves other resources' permissions)
Write-Verbose "No existing inheritable permissions for resource '$resourceName' — adding..."
# No existing entry for this resourceAppId - add it (preserves other resources' permissions)
Write-Verbose "No existing inheritable permissions for resource '$resourceName' - adding..."
Write-Debug "POST URL: $apiUrl"

while ($retryCount -lt $maxRetries -and -not $success) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Get-EntraAgentIdentityBlueprint {
return
}

# Resolve BlueprintId: explicit param → stored → prompt → error
# Resolve BlueprintId: explicit param -> stored -> prompt -> error
if (-not $BlueprintId) {
if ((Test-Path variable:script:CurrentAgentBlueprintId) -and $script:CurrentAgentBlueprintId) {
$BlueprintId = $script:CurrentAgentBlueprintId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Get-EntraAgentIdentityBlueprintPrincipal {
return
}

# Resolve ServicePrincipalId: explicit param → stored → prompt → error
# Resolve ServicePrincipalId: explicit param -> stored -> prompt -> error
if (-not $ServicePrincipalId) {
if ((Test-Path variable:script:CurrentAgentBlueprintServicePrincipalId) -and $script:CurrentAgentBlueprintServicePrincipalId) {
$ServicePrincipalId = $script:CurrentAgentBlueprintServicePrincipalId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,44 +609,44 @@ function Invoke-EntraAgentIdInteractive {
# ===================================================================

Write-Host "=== Complete Workflow Summary ===" -ForegroundColor Green
Write-Host "✓ 1. Agent Identity Blueprint created and configured" -ForegroundColor Green
Write-Host "✓ 2. Client secret added for API authentication" -ForegroundColor Green
Write-Host "[OK] 1. Agent Identity Blueprint created and configured" -ForegroundColor Green
Write-Host "[OK] 2. Client secret added for API authentication" -ForegroundColor Green

if ($hasInteractiveAgents) {
Write-Host "✓ 3. Interactive agent scopes configured with user prompts" -ForegroundColor Green
Write-Host "[OK] 3. Interactive agent scopes configured with user prompts" -ForegroundColor Green
}
else {
Write-Host "- 3. Interactive agent scopes (skipped by user choice)" -ForegroundColor Gray
}

if ($blueprintWillCreateAgentUsers) {
Write-Host "✓ 4. Blueprint configured to create Agent ID users" -ForegroundColor Green
Write-Host "[OK] 4. Blueprint configured to create Agent ID users" -ForegroundColor Green
}
else {
Write-Host "- 4. Blueprint configured to create Agent ID users (skipped by user choice)" -ForegroundColor Gray
}

if ($hasInheritablePermissions) {
Write-Host "✓ 5. Inheritable permissions configured for agent users" -ForegroundColor Green
Write-Host "[OK] 5. Inheritable permissions configured for agent users" -ForegroundColor Green
}
else {
Write-Host "- 5. Inheritable permissions (skipped by user choice)" -ForegroundColor Gray
}

if ($hasInheritablePermissions -and $useStaticPermissions) {
Write-Host "✓ 6. Static permissions configured via required resource access" -ForegroundColor Green
Write-Host "[OK] 6. Static permissions configured via required resource access" -ForegroundColor Green
}
elseif ($hasInheritablePermissions -and -not $useStaticPermissions) {
Write-Host "✓ 6. Dynamic permissions selected (resolved at runtime)" -ForegroundColor Green
Write-Host "[OK] 6. Dynamic permissions selected (resolved at runtime)" -ForegroundColor Green
}
else {
Write-Host "- 6. Permission model selection (skipped - no inheritable permissions)" -ForegroundColor Gray
}

Write-Host "✓ 7. Consent obtained for the blueprint in this tenant" -ForegroundColor Green
Write-Host "[OK] 7. Consent obtained for the blueprint in this tenant" -ForegroundColor Green

if ($allAgentIdentities.Count -gt 0) {
Write-Host "✓ 8. Agent Identity and User Creation completed" -ForegroundColor Green
Write-Host "[OK] 8. Agent Identity and User Creation completed" -ForegroundColor Green
Write-Host " - Created $($allAgentIdentities.Count) Agent $(if ($allAgentIdentities.Count -eq 1) { 'Identity' } else { 'Identities' })" -ForegroundColor Green
if ($blueprintWillCreateAgentUsers) {
Write-Host " - Created $($allAgentUsers.Count) Agent $(if ($allAgentUsers.Count -eq 1) { 'User' } else { 'Users' })" -ForegroundColor Green
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function New-EntraAgentIDForAgentIdentityBlueprint {
Write-Verbose "Could not retrieve current user details: $_"
}

# Sponsors are always required — prompt until at least one is provided
# Sponsors are always required - prompt until at least one is provided
$hasSponsors = (($SponsorUserIds -and $SponsorUserIds.Count -gt 0) -or
($SponsorGroupIds -and $SponsorGroupIds.Count -gt 0))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function New-EntraAgentIdentityBlueprint {
$customHeaders = $null
}

# Sponsors are always required — prompt until at least one is provided
# Sponsors are always required - prompt until at least one is provided
$hasSponsors = (($SponsorUserIds -and $SponsorUserIds.Count -gt 0) -or
($SponsorGroupIds -and $SponsorGroupIds.Count -gt 0))

Expand Down
2 changes: 1 addition & 1 deletion module/Entra/Microsoft.Entra/Users/New-EntraUser.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function New-EntraUser {
[Parameter(ParameterSetName = "CreateUser", HelpMessage = "The user's surname (last name). Maximum length: 64 characters.")]
[System.String] $Surname,

[Parameter(ParameterSetName = "CreateUser", HelpMessage = "A list of the user’s additional email addresses (e.g., ['bob@contoso.com', 'Robert@fabrikam.com']). Supports up to 250 entries, each up to 250 characters.")]
[Parameter(ParameterSetName = "CreateUser", HelpMessage = "A list of the user's additional email addresses (e.g., ['bob@contoso.com', 'Robert@fabrikam.com']). Supports up to 250 entries, each up to 250 characters.")]
[System.Collections.Generic.List`1[System.String]] $OtherMails,

[Parameter(ParameterSetName = "CreateUser", HelpMessage = "The user's sign-in name (UPN) in the format alias@domain. It should match the user's email and use a verified domain in the tenant.")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ function Add-EntraBetaInheritablePermissionsToAgentIdentityBlueprint {

if ($existingEntry) {
# Overwrite the existing entry for this resourceAppId
Write-Verbose "Existing inheritable permissions found for resource '$resourceName' — overwriting..."
Write-Verbose "Existing inheritable permissions found for resource '$resourceName' - overwriting..."
$patchUrl = "$apiUrl/$($currentResourceAppId.ToString())"
Write-Debug "PATCH URL: $patchUrl"

Expand All @@ -152,8 +152,8 @@ function Add-EntraBetaInheritablePermissionsToAgentIdentityBlueprint {
}
}
else {
# No existing entry for this resourceAppId — add it (preserves other resources' permissions)
Write-Verbose "No existing inheritable permissions for resource '$resourceName' — adding..."
# No existing entry for this resourceAppId - add it (preserves other resources' permissions)
Write-Verbose "No existing inheritable permissions for resource '$resourceName' - adding..."
Write-Debug "POST URL: $apiUrl"

while ($retryCount -lt $maxRetries -and -not $success) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Get-EntraBetaAgentIdentityBlueprint {
return
}

# Resolve BlueprintId: explicit param → stored → prompt → error
# Resolve BlueprintId: explicit param -> stored -> prompt -> error
if (-not $BlueprintId) {
if ((Test-Path variable:script:CurrentAgentBlueprintId) -and $script:CurrentAgentBlueprintId) {
$BlueprintId = $script:CurrentAgentBlueprintId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function Get-EntraBetaAgentIdentityBlueprintPrincipal {
return
}

# Resolve ServicePrincipalId: explicit param → stored → prompt → error
# Resolve ServicePrincipalId: explicit param -> stored -> prompt -> error
if (-not $ServicePrincipalId) {
if ((Test-Path variable:script:CurrentAgentBlueprintServicePrincipalId) -and $script:CurrentAgentBlueprintServicePrincipalId) {
$ServicePrincipalId = $script:CurrentAgentBlueprintServicePrincipalId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function Grant-EntraBetaMcpServerPermission {
$incomingScopes = $targetScopes | Where-Object { $_ } | Sort-Object -Unique

if (-not $grant) {
# No existing grant – create with provided scopes (already additive by definition)
# No existing grant - create with provided scopes (already additive by definition)
$targetString = ($incomingScopes) -join ' '
Write-Verbose "Creating new permission grant with scopes: $targetString"
$body = @{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,44 +609,44 @@ function Invoke-EntraBetaAgentIdInteractive {
# ===================================================================

Write-Host "=== Complete Workflow Summary ===" -ForegroundColor Green
Write-Host "✓ 1. Agent Identity Blueprint created and configured" -ForegroundColor Green
Write-Host "✓ 2. Client secret added for API authentication" -ForegroundColor Green
Write-Host "[OK] 1. Agent Identity Blueprint created and configured" -ForegroundColor Green
Write-Host "[OK] 2. Client secret added for API authentication" -ForegroundColor Green

if ($hasInteractiveAgents) {
Write-Host "✓ 3. Interactive agent scopes configured with user prompts" -ForegroundColor Green
Write-Host "[OK] 3. Interactive agent scopes configured with user prompts" -ForegroundColor Green
}
else {
Write-Host "- 3. Interactive agent scopes (skipped by user choice)" -ForegroundColor Gray
}

if ($blueprintWillCreateAgentUsers) {
Write-Host "✓ 4. Blueprint configured to create Agent ID users" -ForegroundColor Green
Write-Host "[OK] 4. Blueprint configured to create Agent ID users" -ForegroundColor Green
}
else {
Write-Host "- 4. Blueprint configured to create Agent ID users (skipped by user choice)" -ForegroundColor Gray
}

if ($hasInheritablePermissions) {
Write-Host "✓ 5. Inheritable permissions configured for agent users" -ForegroundColor Green
Write-Host "[OK] 5. Inheritable permissions configured for agent users" -ForegroundColor Green
}
else {
Write-Host "- 5. Inheritable permissions (skipped by user choice)" -ForegroundColor Gray
}

if ($hasInheritablePermissions -and $useStaticPermissions) {
Write-Host "✓ 6. Static permissions configured via required resource access" -ForegroundColor Green
Write-Host "[OK] 6. Static permissions configured via required resource access" -ForegroundColor Green
}
elseif ($hasInheritablePermissions -and -not $useStaticPermissions) {
Write-Host "✓ 6. Dynamic permissions selected (resolved at runtime)" -ForegroundColor Green
Write-Host "[OK] 6. Dynamic permissions selected (resolved at runtime)" -ForegroundColor Green
}
else {
Write-Host "- 6. Permission model selection (skipped - no inheritable permissions)" -ForegroundColor Gray
}

Write-Host "✓ 7. Consent obtained for the blueprint in this tenant" -ForegroundColor Green
Write-Host "[OK] 7. Consent obtained for the blueprint in this tenant" -ForegroundColor Green

if ($allAgentIdentities.Count -gt 0) {
Write-Host "✓ 8. Agent Identity and User Creation completed" -ForegroundColor Green
Write-Host "[OK] 8. Agent Identity and User Creation completed" -ForegroundColor Green
Write-Host " - Created $($allAgentIdentities.Count) Agent $(if ($allAgentIdentities.Count -eq 1) { 'Identity' } else { 'Identities' })" -ForegroundColor Green
if ($blueprintWillCreateAgentUsers) {
Write-Host " - Created $($allAgentUsers.Count) Agent $(if ($allAgentUsers.Count -eq 1) { 'User' } else { 'Users' })" -ForegroundColor Green
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function New-EntraBetaAgentIDForAgentIdentityBlueprint {
Write-Verbose "Could not retrieve current user details: $_"
}

# Sponsors are always required — prompt until at least one is provided
# Sponsors are always required - prompt until at least one is provided
$hasSponsors = (($SponsorUserIds -and $SponsorUserIds.Count -gt 0) -or
($SponsorGroupIds -and $SponsorGroupIds.Count -gt 0))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function New-EntraBetaAgentIdentityBlueprint {
$customHeaders = $null
}

# Sponsors are always required — prompt until at least one is provided
# Sponsors are always required - prompt until at least one is provided
$hasSponsors = (($SponsorUserIds -and $SponsorUserIds.Count -gt 0) -or
($SponsorGroupIds -and $SponsorGroupIds.Count -gt 0))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ function Revoke-EntraBetaMcpServerPermission {
$updatedGrant = Update-GrantScopes -clientSpId $clientSp.Id -resourceSpId $resourceSp.Id -targetScopes $remainingScopes

if (@($remainingScopes).Count -eq 0) {
Write-Host "✓ All permissions revoked from $($resolvedClient.Name)" -ForegroundColor Green
Write-Host "[OK] All permissions revoked from $($resolvedClient.Name)" -ForegroundColor Green
return $null
} else {
Write-Host "✓ Permissions partially revoked from $($resolvedClient.Name)" -ForegroundColor Green
Write-Host "[OK] Permissions partially revoked from $($resolvedClient.Name)" -ForegroundColor Green
Write-Verbose " Revoked scopes:"
$validScopesToRevoke | ForEach-Object { Write-Verbose " - $_"}
Write-Verbose " Remaining scopes:"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function New-EntraBetaUser {
[Parameter(ParameterSetName = "CreateUser", HelpMessage = "The user's surname (last name). Maximum length: 64 characters.")]
[System.String] $Surname,

[Parameter(ParameterSetName = "CreateUser", HelpMessage = "A list of the user’s additional email addresses (e.g., ['bob@contoso.com', 'Robert@fabrikam.com']). Supports up to 250 entries, each up to 250 characters.")]
[Parameter(ParameterSetName = "CreateUser", HelpMessage = "A list of the user's additional email addresses (e.g., ['bob@contoso.com', 'Robert@fabrikam.com']). Supports up to 250 entries, each up to 250 characters.")]
[System.Collections.Generic.List`1[System.String]] $OtherMails,

[Parameter(ParameterSetName = "CreateUser", HelpMessage = "The user's sign-in name (UPN) in the format alias@domain. It should match the user's email and use a verified domain in the tenant.")]
Expand Down
44 changes: 44 additions & 0 deletions test/Entra/SourceFileEncoding.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
# ------------------------------------------------------------------------------

# Regression guard for issue #1603.
#
# The module .psm1 files are assembled by concatenating the per-cmdlet .ps1 source
# files under module/. When a source file contains non-ASCII characters (for example
# the arrow "->", an em dash, or a check mark) and is saved without a UTF-8 BOM,
# Windows PowerShell 5.1 decodes the bytes using the system ANSI code page instead of
# UTF-8. The mis-decoded multi-byte characters turn into stray quote characters that
# unbalance the surrounding strings, so Import-Module fails with parser errors such as
# "Missing closing ')' in expression" and "The string is missing the terminator".
#
# PowerShell 7 (Core) reads BOM-less files as UTF-8, which is why the CI test runs and
# every day-to-day PowerShell 7 user import the module without error while the module
# is broken on Windows PowerShell 5.1. Keeping the shipped source ASCII-only makes the
# generated .psm1 parse identically under every encoding.

Describe "Module source file encoding (Windows PowerShell 5.1 import safety)" {
BeforeDiscovery {
$moduleRoot = Join-Path $PSScriptRoot '..' '..' 'module'
$script:SourceFileCases = @()
if (Test-Path -Path $moduleRoot) {
$script:SourceFileCases = Get-ChildItem -Path $moduleRoot -Recurse -Filter '*.ps1' -File |
ForEach-Object { @{ FullName = $_.FullName; Name = $_.Name } }
}
}

It "Should resolve the module source directory" {
(Join-Path $PSScriptRoot '..' '..' 'module') | Should -Exist
}

It "Should discover module source files to validate" {
$moduleRoot = Join-Path $PSScriptRoot '..' '..' 'module'
@(Get-ChildItem -Path $moduleRoot -Recurse -Filter '*.ps1' -File).Count | Should -BeGreaterThan 0
}

It "'<Name>' should not contain non-ASCII characters" -ForEach $script:SourceFileCases {
$bytes = [System.IO.File]::ReadAllBytes($FullName)
$nonAsciiCount = @($bytes | Where-Object { $_ -gt 0x7F }).Count
$nonAsciiCount | Should -Be 0 -Because "$Name must be ASCII-only so Windows PowerShell 5.1 imports the module regardless of a BOM (issue #1603)"
}
}