InActive-Owners.ps1
$t1 = @'
Designed and managed by ___ _ / _ \ (_) / /_\ \ __ _ _ __ ___ _ __ ___ _ _ __ | _ |/ _` | '_ ` _ \| '_ ` _ \| | '__| | | | | (_| | | | | | | | | | | | | | \_| |_/\__,_|_| |_| |_|_| |_| |_|_|_| Contact : aammir.mirza@hotmail.com '@ $t2 = @' +------------------+-----------------------------------------------------------------+----------------------------------------------+ | Switch | Usage | Example | +------------------+-----------------------------------------------------------------+----------------------------------------------+ | SecurityGroup | With this switch you can run accross all the | | | | SGs within AzureAD for which you (identity | $data = InActive-Owners -SecurityGroup | | | running functions) have access. | -Verbose | +------------------+-----------------------------------------------------------------+----------------------------------------------+ | SubscriptionTags | With this switch you can run for subscription scope | $data = InActive-Owners -SubscriptionTags | | | Tags (service tags), having owner email address. | -Owner_TagKey '<TagName>' | | | You need to specify the Tag key. | -Verbose | +------------------+-----------------------------------------------------------------+----------------------------------------------+ | AppReg | With this switch you can run for all Azure App Registrations to | $data = InActive-Owners -AppReg | | | verify the owners and If they are inActive. | -Verbose | +------------------+-----------------------------------------------------------------+----------------------------------------------+ | UPN | With this switch you can run for individual UPN/ID to | Check-UserUPN -UserUPN <UserEmailA> | | | verify the owners and If they are inActive. | -Verbose | +------------------+-----------------------------------------------------------------+----------------------------------------------+ '@ $t3 = @' _____ _ _ _ _ ______ |_ _| | | | | (_) | | ____| | | __| | ___ _ __ | |_ _| |_ _ _ | |__ _ __ _ __ ___ _ __ | | / _` |/ _ \ '_ \| __| | __| | | | | __| | '__| '__/ _ \| '__| _| || (_| | __/ | | | |_| | |_| |_| | | |____| | | | | (_) | | |_____\__,_|\___|_| |_|\__|_|\__|\__, | |______|_| |_| \___/|_| __/ | |___/ '@ $issues = @' ==================================== Known Issues with Authentication can be fixed with explisit decleration. ==================================== Example: try { 'Logging in to Azure...' Connect-AzAccount -Identity 'Logged in to Azure...' } catch { Write-Error -Message $_.Exception throw $_.Exception } $token = (Get-AzAccessToken -ResourceTypeName MSGraph).token $authHeader = @{ 'Content-Type' = 'application/json' 'Authorization' = 'Bearer ' + $token } Connect-MgGraph -AccessToken $token '@ # # Initiating MI Login # $MIConnection = Connect-AzAccount -Identity -ErrorAction SilentlyContinue # if ($MIConnection) { # $MIConnection # $token = (Get-AzAccessToken -ResourceTypeName MSGraph -ErrorAction SilentlyContinue).Token # } # # else { # # Write-Error 'MI authentication failed or missing approproate permission to MI.' # # } $authHeader = @{ 'Content-Type' = 'application/json' 'Authorization' = 'Bearer ' + ((Get-AzAccessToken -ResourceTypeName MSGraph -ErrorAction SilentlyContinue).token ) } Function Check-UserUPN { [CmdletBinding()] param ( $UserUPN ) # Authentication # $token = (Get-AzAccessToken -ResourceTypeName MSGraph -ErrorAction SilentlyContinue).token | ConvertTo-SecureString -AsPlainText -Force -ErrorAction 'SilentlyContinue' # $connect = Connect-MgGraph -AccessToken $token -ErrorAction SilentlyContinue # Write-Verbose "Connected succesfully (Encrypted) - $($connect)" # Validate Authentication $ Authorization try { if ((!(Get-AzContext)) -or (!(Get-MgContext))) { throw 'Bad thing happened' } } catch { Write-Error 'MI not configured or needed permission to read object accross tenanat. Or Missing MG-Graph Permissions.' $t3 Write-Verbose "Known issue with authentication.`n$($issues)" } # Check If user exists try { if ((Get-AzContext)) { $uriUE = "https://graph.microsoft.com/v1.0/users/$($UserUPN)" $usersExist = Invoke-MgGraphRequest ` -Uri $uriUE ` -Method GET } } catch { Write-Verbose 'User dose NOT EXIST.'; $userData = 'NOT IN AAD'; Return $userData break; } if ($usersExist) { # $res = Get-MgUser -UserId $UPN -EA 'SilentlyContinue' $filterOwner = "DisplayName, mail, accountEnabled&filter=mail eq '$UserUPN'" $filterInactiveOwner = ' and accountEnabled eq false' $body = @{} $url = 'https://graph.microsoft.com' $endpoint = "users/?`$select=" $graphversion = 'beta' $uri = "$url/$graphversion/$endpoint$filterOwner$filterInactiveOwner" $results = Invoke-MgGraphRequest ` -Uri $uri ` -Method GET ` -Body $body # function return if ($results.value) { Write-Verbose " [INACTIVE] $($UserUPN)" $userData = 'INACTIVE' } else { Write-Verbose " [ACTIVE] $($UserUPN)" $userData = 'ACTIVE' } } Return $userData } Function InActive-Owners { <# .SYNOPSIS Get DISABLED resource owners details from App reg, Security Group and subscription scoped Tags. .DESCRIPTION Get list of DISABLED / InActive owners from Azure Security Group, Azure App Registration, And any Owner level Tags (limited to subscription scope.) AUTHENTICATION Azure tenant scope authentication should be taken care before calling the commands. Identity should also have access for Connect-MgGraph Directory.ReadAll. .PARAMETER SecurityGroup Switch used to run thru all the security groups within the tenanat. No support for explicit SGs at this point. .PARAMETER AppReg Switch used to run thru all the Application Registrations within the tenanat. No support for explicit App registrations at this point. .PARAMETER SubscriptionTags Switch used in combination with Owner_TagKey. For more details look at examples. .PARAMETER Owner_TagKey Paramater that need to pass while used with SubscriptionTags. It includes the value of Tag name which holds subscription owners information. .PARAMETER UserUPN Parameter used for single UserUPN or user status check. Pass email address of user for which you need status. .EXAMPLE PS> $data = InActive-Owners -SecurityGroup $data | format-Table $data | Export-Csv -Path 'DisabledOwners.csv' -NoTypeInformation -Force .EXAMPLE PS> $data = InActive-Owners -SubscriptionTags -Owner_TagKey '<TagName>' $data | format-Table $data | Export-Csv -Path 'DisabledOwners.csv' -NoTypeInformation -Force .EXAMPLE PS> $data = InActive-Owners -AppReg $data | format-Table $data | Export-Csv -Path 'DisabledOwners.csv' -NoTypeInformation -Force .EXAMPLE PS> Check-UserUPN -UserUPN <UserEmailAddress> .LINK Other packages: https://www.powershellgallery.com/packages?q=aammir #> [CmdletBinding()] param ( [Parameter(ParameterSetName = 'One', Position = 1)]$Owner_TagKey <#= 'iicsSubscriptionOwner'#>, [Parameter(ParameterSetName = 'One', Position = 0)][switch]$SubscriptionTags, [Parameter(ParameterSetName = 'One', Position = 2)]$SubscriptionName, [Parameter(ParameterSetName = 'Three')][switch]$SecurityGroup, [Parameter(ParameterSetName = 'Four')][switch]$AppReg, [Parameter(ParameterSetName = 'Five')]$UPN, [Parameter(ParameterSetName = 'Six')]$SecurityGroupId ) # Authentication # $token = (Get-AzAccessToken -ResourceTypeName MSGraph -ErrorAction SilentlyContinue).token | ConvertTo-SecureString -AsPlainText -Force -ErrorAction 'SilentlyContinue' # $connect = Connect-MgGraph -AccessToken $token -ErrorAction SilentlyContinue # Write-Verbose "Connected succesfully (Encrypted) - $($connect)" Write-Verbose "`n$($t1)" Write-Verbose 'For more help on command and switches use -Verbose' Write-Verbose "Switches and there operations.`n$($t2)" try { if ((!(Get-AzContext)) -or (!(Get-MgContext))) { throw 'Bad thing happened' } } catch { Write-Error 'MI not configured or needed permission to read object accross tenanat. Or Missing MG-Graph Permissions.' $t3 Write-Verbose "Known issue with authentication.`n$($issues)" } if (Get-MgContext) { $url = 'https://graph.microsoft.com' $endpoint = "users/?`$select=" $graphversion = 'beta' $invalidUPN = @() if ($SubscriptionTags) { if ($SubscriptionName) { Write-Verbose "Running for $($SubscriptionName)" $subsName = Get-AzSubscription -SubscriptionName $SubscriptionName } else { $subsName = @() $subsName = (Get-AzSubscription <#| Select-Object -First 25#>) } foreach ($item in $subsName) { # | Where-Object -Property State -EQ 'Enabled')) { $context = Set-AzContext $item Write-Verbose "Execution for the subscription $($item.Name)" if ($($item.State) -eq 'Enabled') { Write-Verbose "Subscription State $($item.State)" # constructing Body $tags = Get-AzTag -ResourceId /subscriptions/$item Write-Verbose 'Parsing the TAGs now...' Write-Verbose "Checking subscription level tags for $($item.Name)" if ($tags) { foreach ($tagKey in $tags.Properties.TagsProperty.Keys) { $tagValue = $tags.Properties.TagsProperty[$tagKey] # Write-Host " | -- - $($tagKey):$($tagValue)" -ForegroundColor DarkYellow if ($tagKey -eq "$Owner_TagKey") { $iicsSubscriptionOwner = $tagValue } } if ($iicsSubscriptionOwner) { $Ownerdata = Check-UserUPN -UserUPN $iicsSubscriptionOwner if (($Ownerdata -eq 'INACTIVE') -or ($Ownerdata -eq 'NOT IN AAD')) { $invalidUPN += [PSCustomObject]@{ 'SubscriptionName' = $item.Name 'OwnerUPN' = $iicsSubscriptionOwner 'Status' = $Ownerdata } } else { Write-Verbose "$($item.Name) | $($iicsSubscriptionOwner)" } } # If block } } } } elseif ($SecurityGroup -or $SecurityGroupId) { $invalidUPN = @() $sgCount = 0; #$groupId = '11e7b41d-d876-416c-abef-0ea1a23ab762' $graphversion = 'beta' $allGroupsEndpoint = 'groups?$filter=mailEnabled eq false' if ($SecurityGroupId) { $endpointSg = "groups/$($SecurityGroupId)/owners?`$select=mail" $uriSg = "$url/$graphversion/$endpointSg" $SgOwners = Invoke-MgGraphRequest -Uri $uriSg -Method GET -Headers $authHeader $count = ($SgOwners.Value.mail).Count if ($SgOwners.Value.mail) { foreach ($currentItemName in $SgOwners.Value.mail) { $SGdata = Check-UserUPN -UserUPN $currentItemName if ($SGdata -eq 'INACTIVE' -or $SGdata -eq 'NOT IN AAD') { $invalidUPN += [PSCustomObject]@{ 'SecurityGroupName' = $($currentSG.DisplayName) 'OwnerUPN' = $currentItemName 'Status' = $SGdata 'NumberOfActiveOwners' = $count } } else { Write-Verbose " [ACTIVE] $($currentItemName)" } } } } else { # Extracting all Security Groups $uriAllSg = "$url/$graphversion/$allGroupsEndpoint" $resultsAllSG = Invoke-MgGraphRequest ` -Uri $uriAllSg ` -Method GET -Headers $authHeader $CloudSecurityGroups = $resultsAllSG.Value $UserNextLink = $resultsAllSG.'@odata.nextLink' while ($UserNextLink -ne $null) { $resultsAllSG = (Invoke-MgGraphRequest -Uri $UserNextLink -Method Get -Headers $authHeader) $UserNextLink = $resultsAllSG.'@odata.nextLink' $CloudSecurityGroups += $resultsAllSG.value } Write-Verbose '-------------------------------' Write-Verbose "Total SG Count - $($CloudSecurityGroups.Count)" Write-Verbose '-------------------------------' foreach ($currentSG in $CloudSecurityGroups) { $sgCount = $sgCount + 1 Write-Verbose "$($sgCount) - $($currentSG.displayName)" $endpointSg = "groups/$($currentSG.id)/owners?`$select=mail" $uriSg = "$url/$graphversion/$endpointSg" $SgOwners = Invoke-MgGraphRequest -Uri $uriSg -Method GET -Headers $authHeader $count = ($SgOwners.Value.mail).Count foreach ($currentItemName in $SgOwners.Value.mail) { if ($currentItemName) { $count = $count - 1 $SGdata = Check-UserUPN -UserUPN $currentItemName if ($SGdata -eq 'INACTIVE' -or $SGdata -eq 'NOT IN AAD') { $invalidUPN += [PSCustomObject]@{ 'SecurityGroupName' = $($currentSG.DisplayName) 'OwnerUPN' = $currentItemName 'Status' = $SGdata 'NumberOfActiveOwners' = $count } } } # If block } Write-Verbose "Active owners are - $($count)" } } } elseif ($AppReg) { $invalidUPN = @() # Fetching all the App registrations $appRegistrations = Get-AzADApplication # | Select-Object -First 25 foreach ($appRegistration in $appRegistrations) { Write-Verbose '-------------------------------------' Write-Verbose " +++++ Running for $($appRegistration.DisplayName)" $owner = "https://graph.microsoft.com/v1.0/myorganization/applications/$($appRegistration.Id)/owners" $response2 = Invoke-RestMethod $owner -Method 'GET' -Headers $authHeader $count = ($response2.Value.userPrincipalName).Count Write-Verbose "+++ List of owners $($count)`n$($response2.Value.userPrincipalName)" foreach ($currentItemName in $response2.Value.userPrincipalName) { if ($currentItemName) { $count = $count - 1 $data = Check-UserUPN -UserUPN $currentItemName if (($data -eq 'INACTIVE') -or ($data -eq 'NOT IN AAD')) { $invalidUPN += [PSCustomObject]@{ 'AppRegName' = $($appRegistration.DisplayName) 'OwnerUPN' = $currentItemName 'Status' = $data # 'NumberOfActiveOwners' = $count } } } # If block } Write-Verbose "Active owners are - $($count)" } } elseif ($UPN) { $data = Check-UserUPN -UserUPN $UPN $invalidUPN += [PSCustomObject]@{ 'OwnerUPN' = $UPN 'Status' = $data } } else { Write-Warning "You must select one of the switch to perform the operation.`n$($t2)" } # Function Output Write-Output $invalidUPN } } |