Skip to content

Commit d993ba5

Browse files
Enganga/Resolve-EntraIdTenant (#1352)
* Resolve-EntraIdTenant * Resolve-Entra*IdTenant * Edits to Docs to use the dummy GUID * Test fixes * test changes * test fixes * Fixes to tests * Fixed Comments for consistency * Test fixes * Added HelpMessage * Added HelpMessage * formatting parameters * formatting parameters * tests cleanups * Formatting improvements * Original implementation * Add HelpMessage * Updated examples, removing backticks, adding examples and removing redundant examples * Fixed examples and allow list of tenantIds and DomainNames from a file * Docs fixes * Rename to Resolve-Entra*Tenant * Nit: Case fix - Output --------- Co-authored-by: stevemutungi <stevemutungi@microsoft.com>
1 parent 9ac0400 commit d993ba5

File tree

7 files changed

+938
-0
lines changed

7 files changed

+938
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# ------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
# Licensed under the MIT License. See License in the project root for license information.
4+
# ------------------------------------------------------------------------------
5+
function Resolve-EntraTenant {
6+
[CmdletBinding(
7+
DefaultParameterSetName = 'TenantId',
8+
SupportsShouldProcess = $false,
9+
PositionalBinding = $false,
10+
HelpUri = 'https://learn.microsoft.com/',
11+
ConfirmImpact = 'Medium'
12+
)]
13+
[Alias()]
14+
[OutputType([PSCustomObject])]
15+
Param (
16+
# The TenantId in GUID format (supports multiple values)
17+
[Parameter(
18+
ParameterSetName = 'TenantId',
19+
Mandatory = $true,
20+
Position = 0,
21+
ValueFromPipeline = $true,
22+
ValueFromPipelineByPropertyName = $true,
23+
HelpMessage = "Unique Id(s) of the Tenant(s) in GUID format."
24+
)]
25+
[ValidateScript({ $_ -match "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" })]
26+
[string[]]
27+
$TenantId,
28+
29+
# The TenantDomainName in DNS Name format (supports multiple values)
30+
[Parameter(
31+
ParameterSetName = 'DomainName',
32+
Mandatory = $true,
33+
Position = 0,
34+
ValueFromPipeline = $true,
35+
ValueFromPipelineByPropertyName = $true,
36+
HelpMessage = "Unique Domain Name(s) of the Tenant(s) (e.g., contoso.com)."
37+
)]
38+
[ValidateScript({ $_ -match "^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$" })]
39+
[string[]]
40+
$DomainName,
41+
42+
# Environment to resolve Azure AD Tenant
43+
[Parameter(
44+
Mandatory = $false,
45+
Position = 1,
46+
ValueFromPipeline = $true,
47+
ValueFromPipelineByPropertyName = $true,
48+
HelpMessage = "Tenant Environment Name (Global, USGov, China, USGovDoD, Germany)."
49+
)]
50+
[ValidateSet("Global", "USGov", "China", "USGovDoD", "Germany")]
51+
[string]
52+
$Environment = "Global",
53+
54+
# Skip resolving via the OIDC Metadata endpoint
55+
[Parameter(Mandatory=$false, HelpMessage="Specify whether to skip resolving via the OIDC metadata endpoint.")]
56+
[switch]
57+
$SkipOidcMetadataEndpoint
58+
)
59+
60+
begin {
61+
# Retrieve endpoint information based on the environment
62+
$graphEndpoint = (Get-EntraEnvironment -Name $Environment).GraphEndpoint
63+
$azureAdEndpoint = (Get-EntraEnvironment -Name $Environment).AzureAdEndpoint
64+
65+
Write-Verbose ("Using $Environment login endpoint: $azureAdEndpoint")
66+
Write-Verbose ("Using $Environment Graph endpoint: $graphEndpoint")
67+
}
68+
69+
process {
70+
$itemsToProcess = if ($TenantId) { $TenantId } else { $DomainName }
71+
72+
foreach ($item in $itemsToProcess) {
73+
# Initialize headers and result object
74+
$customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand
75+
$resolveUri = $null
76+
$resolvedTenant = [ordered]@{
77+
Environment = $Environment
78+
}
79+
80+
# Set URI based on parameter set
81+
if ($PSCmdlet.ParameterSetName -eq 'TenantId') {
82+
Write-Verbose ("Resolving Azure AD Tenant by TenantId: $item")
83+
$resolveUri = "$graphEndpoint/v1.0/tenantRelationships/findTenantInformationByTenantId(tenantId='$item')"
84+
$resolvedTenant.ValueFormat = "TenantId"
85+
}
86+
elseif ($PSCmdlet.ParameterSetName -eq 'DomainName') {
87+
Write-Verbose ("Resolving Azure AD Tenant by DomainName: $item")
88+
$resolveUri = "$graphEndpoint/v1.0/tenantRelationships/findTenantInformationByDomainName(domainName='$item')"
89+
$resolvedTenant.ValueFormat = "DomainName"
90+
}
91+
92+
if ($resolveUri) {
93+
try {
94+
Write-Verbose ("Resolving Tenant Information using MS Graph API.")
95+
$resolve = Invoke-MgGraphRequest -Method Get -Uri $resolveUri -ErrorAction Stop -Headers $customHeaders |
96+
Select-Object tenantId, displayName, defaultDomainName, federationBrandName
97+
98+
# Populate resolved tenant details
99+
$resolvedTenant.Result = "Resolved"
100+
$resolvedTenant.ResultMessage = "Tenant resolved successfully."
101+
$resolvedTenant.TenantId = $resolve.tenantId
102+
$resolvedTenant.DisplayName = $resolve.displayName
103+
$resolvedTenant.DefaultDomainName = $resolve.defaultDomainName
104+
$resolvedTenant.FederationBrandName = $resolve.federationBrandName
105+
}
106+
catch {
107+
$resolvedTenant.Result = "Error"
108+
$resolvedTenant.ResultMessage = $_.Exception.Message
109+
$resolvedTenant.TenantId = $null
110+
$resolvedTenant.DisplayName = $null
111+
$resolvedTenant.DefaultDomainName = $null
112+
$resolvedTenant.FederationBrandName = $null
113+
}
114+
}
115+
116+
# Handle OIDC Metadata endpoint resolution
117+
if (-not $SkipOidcMetadataEndpoint) {
118+
$oidcMetadataUri = "$azureAdEndpoint/$item/v2.0/.well-known/openid-configuration"
119+
120+
try {
121+
$oidcMetadata = Invoke-RestMethod -Method Get -Uri $oidcMetadataUri -ErrorAction Stop -Headers $customHeaders
122+
$resolvedTenant.OidcMetadataResult = "Resolved"
123+
$resolvedTenant.OidcMetadataTenantId = $oidcMetadata.issuer.split("/")[3]
124+
$resolvedTenant.OidcMetadataTenantRegionScope = $oidcMetadata.tenant_region_scope
125+
}
126+
catch {
127+
$resolvedTenant.OidcMetadataResult = "Not Found"
128+
$resolvedTenant.OidcMetadataTenantId = $null
129+
$resolvedTenant.OidcMetadataTenantRegionScope = $null
130+
}
131+
}
132+
else {
133+
$resolvedTenant.OidcMetadataResult = "Skipped"
134+
}
135+
136+
Write-Output ([PSCustomObject]$resolvedTenant)
137+
}
138+
}
139+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# ------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
# Licensed under the MIT License. See License in the project root for license information.
4+
# ------------------------------------------------------------------------------
5+
function Get-EntraEnvironment{
6+
[CmdletBinding(DefaultParameterSetName = 'GetQuery')]
7+
param (
8+
[Parameter(ParameterSetName = "GetQuery", Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
9+
[System.String] $Name)
10+
PROCESS{
11+
$params = @{}
12+
if ($PSBoundParameters.ContainsKey("Verbose")) {
13+
$params["Verbose"] = $PSBoundParameters["Verbose"]
14+
}
15+
if ($PSBoundParameters.ContainsKey("Debug")) {
16+
$params["Debug"] = $PSBoundParameters["Debug"]
17+
}
18+
if($null -ne $PSBoundParameters["WarningVariable"])
19+
{
20+
$params["WarningVariable"] = $PSBoundParameters["WarningVariable"]
21+
}
22+
if($null -ne $PSBoundParameters["InformationVariable"])
23+
{
24+
$params["InformationVariable"] = $PSBoundParameters["InformationVariable"]
25+
}
26+
if($null -ne $PSBoundParameters["InformationAction"])
27+
{
28+
$params["InformationAction"] = $PSBoundParameters["InformationAction"]
29+
}
30+
if($null -ne $PSBoundParameters["OutVariable"])
31+
{
32+
$params["OutVariable"] = $PSBoundParameters["OutVariable"]
33+
}
34+
if($null -ne $PSBoundParameters["OutBuffer"])
35+
{
36+
$params["OutBuffer"] = $PSBoundParameters["OutBuffer"]
37+
}
38+
if($null -ne $PSBoundParameters["ErrorVariable"])
39+
{
40+
$params["ErrorVariable"] = $PSBoundParameters["ErrorVariable"]
41+
}
42+
if($null -ne $PSBoundParameters["PipelineVariable"])
43+
{
44+
$params["PipelineVariable"] = $PSBoundParameters["PipelineVariable"]
45+
}
46+
if($null -ne $PSBoundParameters["ErrorAction"])
47+
{
48+
$params["ErrorAction"] = $PSBoundParameters["ErrorAction"]
49+
}
50+
if($null -ne $PSBoundParameters["WarningAction"])
51+
{
52+
$params["WarningAction"] = $PSBoundParameters["WarningAction"]
53+
}
54+
55+
if ($null -ne $PSBoundParameters["Name"]) {
56+
$params["Name"] = $PSBoundParameters["Name"]
57+
}
58+
59+
Write-Debug("============================ TRANSFORMATIONS ============================")
60+
$params.Keys | ForEach-Object { "$_ : $($params[$_])" } | Write-Debug
61+
Write-Debug("=========================================================================`n")
62+
63+
Get-MgEnvironment @params
64+
}
65+
}
66+
67+
68+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# ------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
# Licensed under the MIT License. See License in the project root for license information.
4+
# ------------------------------------------------------------------------------
5+
function Resolve-EntraBetaTenant {
6+
[CmdletBinding(
7+
DefaultParameterSetName = 'TenantId',
8+
SupportsShouldProcess = $false,
9+
PositionalBinding = $false,
10+
HelpUri = 'https://learn.microsoft.com/',
11+
ConfirmImpact = 'Medium'
12+
)]
13+
[Alias()]
14+
[OutputType([PSCustomObject])]
15+
Param (
16+
# The TenantId in GUID format (supports multiple values)
17+
[Parameter(
18+
ParameterSetName = 'TenantId',
19+
Mandatory = $true,
20+
Position = 0,
21+
ValueFromPipeline = $true,
22+
ValueFromPipelineByPropertyName = $true,
23+
HelpMessage = "Unique Id(s) of the Tenant(s) in GUID format."
24+
)]
25+
[ValidateScript({ $_ -match "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" })]
26+
[string[]]
27+
$TenantId,
28+
29+
# The TenantDomainName in DNS Name format (supports multiple values)
30+
[Parameter(
31+
ParameterSetName = 'DomainName',
32+
Mandatory = $true,
33+
Position = 0,
34+
ValueFromPipeline = $true,
35+
ValueFromPipelineByPropertyName = $true,
36+
HelpMessage = "Unique Domain Name(s) of the Tenant(s) (e.g., contoso.com)."
37+
)]
38+
[ValidateScript({ $_ -match "^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.[A-Za-z]{2,})+$" })]
39+
[string[]]
40+
$DomainName,
41+
42+
# Environment to resolve Azure AD Tenant
43+
[Parameter(
44+
Mandatory = $false,
45+
Position = 1,
46+
ValueFromPipeline = $true,
47+
ValueFromPipelineByPropertyName = $true,
48+
HelpMessage = "Tenant Environment Name (Global, USGov, China, USGovDoD, Germany)."
49+
)]
50+
[ValidateSet("Global", "USGov", "China", "USGovDoD", "Germany")]
51+
[string]
52+
$Environment = "Global",
53+
54+
# Skip resolving via the OIDC Metadata endpoint
55+
[Parameter(Mandatory=$false, HelpMessage="Specify whether to skip resolving via the OIDC metadata endpoint.")]
56+
[switch]
57+
$SkipOidcMetadataEndpoint
58+
)
59+
60+
begin {
61+
# Retrieve endpoint information based on the environment
62+
$graphEndpoint = (Get-EntraEnvironment -Name $Environment).GraphEndpoint
63+
$azureAdEndpoint = (Get-EntraEnvironment -Name $Environment).AzureAdEndpoint
64+
65+
Write-Verbose ("Using $Environment login endpoint: $azureAdEndpoint")
66+
Write-Verbose ("Using $Environment Graph endpoint: $graphEndpoint")
67+
}
68+
69+
process {
70+
$itemsToProcess = if ($TenantId) { $TenantId } else { $DomainName }
71+
72+
foreach ($item in $itemsToProcess) {
73+
# Initialize headers and result object
74+
$customHeaders = New-EntraCustomHeaders -Command $MyInvocation.MyCommand
75+
$resolveUri = $null
76+
$resolvedTenant = [ordered]@{
77+
Environment = $Environment
78+
}
79+
80+
# Set URI based on parameter set
81+
if ($PSCmdlet.ParameterSetName -eq 'TenantId') {
82+
Write-Verbose ("Resolving Azure AD Tenant by TenantId: $item")
83+
$resolveUri = "$graphEndpoint/beta/tenantRelationships/findTenantInformationByTenantId(tenantId='$item')"
84+
$resolvedTenant.ValueFormat = "TenantId"
85+
}
86+
elseif ($PSCmdlet.ParameterSetName -eq 'DomainName') {
87+
Write-Verbose ("Resolving Azure AD Tenant by DomainName: $item")
88+
$resolveUri = "$graphEndpoint/beta/tenantRelationships/findTenantInformationByDomainName(domainName='$item')"
89+
$resolvedTenant.ValueFormat = "DomainName"
90+
}
91+
92+
if ($resolveUri) {
93+
try {
94+
Write-Verbose ("Resolving Tenant Information using MS Graph API.")
95+
$resolve = Invoke-MgGraphRequest -Method Get -Uri $resolveUri -ErrorAction Stop -Headers $customHeaders |
96+
Select-Object tenantId, displayName, defaultDomainName, federationBrandName
97+
98+
# Populate resolved tenant details
99+
$resolvedTenant.Result = "Resolved"
100+
$resolvedTenant.ResultMessage = "Tenant resolved successfully."
101+
$resolvedTenant.TenantId = $resolve.tenantId
102+
$resolvedTenant.DisplayName = $resolve.displayName
103+
$resolvedTenant.DefaultDomainName = $resolve.defaultDomainName
104+
$resolvedTenant.FederationBrandName = $resolve.federationBrandName
105+
}
106+
catch {
107+
$resolvedTenant.Result = "Error"
108+
$resolvedTenant.ResultMessage = $_.Exception.Message
109+
$resolvedTenant.TenantId = $null
110+
$resolvedTenant.DisplayName = $null
111+
$resolvedTenant.DefaultDomainName = $null
112+
$resolvedTenant.FederationBrandName = $null
113+
}
114+
}
115+
116+
# Handle OIDC Metadata endpoint resolution
117+
if (-not $SkipOidcMetadataEndpoint) {
118+
$oidcMetadataUri = "$azureAdEndpoint/$item/v2.0/.well-known/openid-configuration"
119+
120+
try {
121+
$oidcMetadata = Invoke-RestMethod -Method Get -Uri $oidcMetadataUri -ErrorAction Stop -Headers $customHeaders
122+
$resolvedTenant.OidcMetadataResult = "Resolved"
123+
$resolvedTenant.OidcMetadataTenantId = $oidcMetadata.issuer.split("/")[3]
124+
$resolvedTenant.OidcMetadataTenantRegionScope = $oidcMetadata.tenant_region_scope
125+
}
126+
catch {
127+
$resolvedTenant.OidcMetadataResult = "Not Found"
128+
$resolvedTenant.OidcMetadataTenantId = $null
129+
$resolvedTenant.OidcMetadataTenantRegionScope = $null
130+
}
131+
}
132+
else {
133+
$resolvedTenant.OidcMetadataResult = "Skipped"
134+
}
135+
136+
Write-Output ([PSCustomObject]$resolvedTenant)
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)