pcit.az.rbac.psm1

#requires -module az.accounts
#requires -module Az.Resources

using module az.accounts
using module Az.Resources
#using module pcit.az.rbac
using namespace Microsoft.Azure.Commands.Resources.Models.Authorization
#requires -Version 5.1
$ErrorActionPreference = 'stop'



Enum pcitAZElementType {
    managementGroup = 0
    Subscription = 1
    ResourceGroup = 2
    Ressource = 3
}

Class pcitAZRBACDelegatableElement {
    [pcitAZElementType]$Type
    [string]$Id
    [string]$AzureRMPath
    [string]$Name
    [string]$DisplayName
    [guid]$TenantID

    pcitAZRBACDelegatableElement(
        [pcitAZElementType]$Type,
        [string]$Id,
        [string]$Name,
        [string]$DisplayName,
        [guid]$TenantID
    ){
        $This.Type = $Type
        $This.Id = $Id
        $This.Name = $Name
        $This.DisplayName = $DisplayName
        $This.TenantID = $TenantID
    }

    pcitAZRBACDelegatableElement(
        [pcitAZElementType]$Type,
        [string]$Id,
        [string]$Name,
        [string]$DisplayName,
        [guid]$TenantID,
        [string]$AzureRMPath
    ){
        $This.Type = $Type
        $This.Id = $Id
        $This.Name = $Name
        $This.DisplayName = $DisplayName
        $This.TenantID = $TenantID
        $this.AzureRMPath = $AzureRMPath
    }
}

Class pcitAZRBACRoleAssignmentElement : Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment {
    [string]$CurrentPermissionScope
    [bool]$IsInherited

    pcitAZRBACRoleAssignmentElement (
        [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment]$AzureRoleAssignment,
        [string]$CurrentPermissionScope
    ) {
        $This.RoleAssignmentName = $AzureRoleAssignment.RoleAssignmentName
        $This.RoleAssignmentId = $AzureRoleAssignment.RoleAssignmentId
        $This.Scope = $AzureRoleAssignment.Scope
        $This.DisplayName = $AzureRoleAssignment.DisplayName
        $This.SignInName = $AzureRoleAssignment.SignInName
        $This.RoleDefinitionName = $AzureRoleAssignment.RoleDefinitionName
        $This.RoleDefinitionId = $AzureRoleAssignment.RoleDefinitionId
        $This.ObjectId = $AzureRoleAssignment.ObjectId
        $This.ObjectType = $AzureRoleAssignment.ObjectType
        $This.CanDelegate = $AzureRoleAssignment.CanDelegate
        $This.Description = $AzureRoleAssignment.Description
        $This.ConditionVersion = $AzureRoleAssignment.ConditionVersion
        $This.Condition = $AzureRoleAssignment.Condition
        $this.CurrentPermissionScope = $CurrentPermissionScope
        $this.IsInherited = (-not ($CurrentPermissionScope -eq $AzureRoleAssignment.Scope))
    }
}

function Connect-pcitAzureTenant {
    [CmdletBinding()]
    <#
    .SYNOPSIS
        A short one-line action-based description, e.g. 'Tests if a function is valid'
    .DESCRIPTION
        A longer description of the function, its purpose, common use cases, etc.
    .NOTES
        Information or caveats about the function e.g. 'This function is not supported in Linux'
    .LINK
        Specify a URI to a help page, this will show when Get-Help -Online is used.
    .EXAMPLE
        Test-MyTestFunction -Verbose
        Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
    #>

    param (
        [parameter(ParameterSetName="ID",Mandatory=$true)][guid]$tenantID,
        [parameter(ParameterSetName="Name",Mandatory=$true)][string]$tenantName
    )

    If ($tenantID) {
        [string]$private:TenantInfo = $tenantID.Guid
    }
    ElseIf ($tenantName) {
        [string]$private:TenantInfo = $tenantName
    }
    Try {
        $private:TenantDetail = Get-AzContext
        If (-not $private:TenantDetail) {
            throw [System.Management.Automation.PSInvalidOperationException]::new("Context is null")
        }
    }
    Catch [System.Management.Automation.PSInvalidOperationException] {
        Connect-AzAccount -Tenant $private:TenantInfo `
        | Out-Null 

        $private:TenantDetail = Get-AzContext
    }

    Return $private:TenantDetail
}

function get-pcitAZmanagementGroups {
    [CmdletBinding()]
    param (
        [parameter(ParameterSetName="ID",Mandatory=$true)][guid]$tenantID,
        [parameter(ParameterSetName="Name",Mandatory=$true)][string]$tenantName
    )
    
    Try {
        # Generation of list not working - using array
        #$private:AzmanagementGroupList = New-Object -TypeName System.Collections.Generic.List[pcitAZRBACDelegatableElement]
        $private:AzmanagementGroupList = @()
        If ($tenantID) {
            Connect-pcitAzureTenant -tenantID $tenantID.Guid `
            | Out-Null
            
        }
        ElseIf ($tenantName) {
            Connect-pcitAzureTenant -tenantName $tenantName `
            | Out-Null
        }


        $private:AzManagementGroups = Get-AzManagementGroup

        Foreach ($AzManagementGroup in $private:AzManagementGroups) {
            [pcitAZRBACDelegatableElement]$private:pcitAZManagementGroup = [pcitAZRBACDelegatableElement]::new(
                [pcitAZElementType]::managementGroup,
                ($AzManagementGroup.id.split('/')[-1]),
                $AzManagementGroup.Name,
                $AzManagementGroup.DisplayName,
                $AzManagementGroup.TenantId,
                $AzManagementGroup.id
            )
            $Private:AzmanagementGroupList += $private:pcitAZManagementGroup
        }
        return $Private:AzmanagementGroupList
        
    }
    Catch {
        Throw $error[0]
    }

}

function get-pcitAZSubscriptions {
    [CmdletBinding()]
    param (
        [parameter(ParameterSetName="ID",Mandatory=$true)][guid]$tenantID,
        [parameter(ParameterSetName="Name",Mandatory=$true)][string]$tenantName
    )
    
    Try {
        # Generation of list not working - using array
        #$private:AzSubscriptionGroupList = New-Object -TypeName System.Collections.Generic.List[pcitAZRBACDelegatableElement]
        $private:AzSubscriptionGroupList = @()
        If ($tenantID) {
            Connect-pcitAzureTenant -tenantID $tenantID.Guid `
            | Out-Null
            
        }
        ElseIf ($tenantName) {
            Connect-pcitAzureTenant -tenantName $tenantName `
            | Out-Null
        }


        $private:AzSubscriptions = Get-AzSubscription

        Foreach ($AzAzSubscription in $private:AzSubscriptions) {
            [pcitAZRBACDelegatableElement]$private:pcitAZSubscription = [pcitAZRBACDelegatableElement]::new(
                [pcitAZElementType]::Subscription,
                $AzAzSubscription.id,
                $AzAzSubscription.Name,
                $AzAzSubscription.Name,
                $AzAzSubscription.TenantId,
                ("/subscriptions/$($AzAzSubscription.Id)")
            )
            $Private:AzSubscriptionGroupList += $private:pcitAZSubscription
        }
        return $Private:AzSubscriptionGroupList
        
    }
    Catch {
        Throw $error[0]
    }


}

function get-PcitAZResourceGroups {
    [CmdletBinding()]
    param (
        [parameter(ParameterSetName="ID",Mandatory=$true)][guid]$tenantID,
        [parameter(ParameterSetName="Name",Mandatory=$true)][string]$tenantName,
        [parameter(Mandatory=$false)][guid]$subscriptionID
        
    )   
    $private:AzRessourceGroupList = @()
    If ($tenantID) {
        $tenantInfo = Connect-pcitAzureTenant -tenantID $tenantID.Guid
        
    }
    ElseIf ($tenantName) {
        $tenantInfo = Connect-pcitAzureTenant -tenantName $tenantName
    }

    $private:pcitAZSubscriptions = get-pcitAZSubscriptions -tenantID $tenantInfo.Tenant.Id
    If ($subscriptionID) {
        $private:pcitAZSubscriptions = $private:pcitAZSubscriptions `
        | Where-Object {$_.id -eq $subscriptionID.Guid}
    }
    Foreach ($private:pcitAZSubscription in $private:pcitAZSubscriptions) {
        $private:CurrentAZContext = Set-AzContext -Subscription $private:pcitAZSubscription.id
        $private:resourceGroups = Get-AzResourceGroup
        Foreach ($private:ressourceGroup in $private:resourceGroups) {
            [pcitAZRBACDelegatableElement]$private:pcitAZRessourceGroup = [pcitAZRBACDelegatableElement]::new(
                [pcitAZElementType]::ResourceGroup,
                $ressourceGroup.ResourceGroupName,
                $ressourceGroup.ResourceGroupName,
                $ressourceGroup.ResourceGroupName,
                ($private:CurrentAZContext.Tenant.Id),
                $ressourceGroup.ResourceId
            )
            $Private:AzRessourceGroupList += $private:pcitAZRessourceGroup            
        }
     }   
     return $Private:AzRessourceGroupList
}

function get-PcitAZResources {
    [CmdletBinding()]
    param (
        [parameter(ParameterSetName="ID",Mandatory=$true)][guid]$tenantID,
        [parameter(ParameterSetName="Name",Mandatory=$true)][string]$tenantName,
        [parameter(Mandatory=$false)][guid]$subscriptionID,
        [parameter(Mandatory=$false)][string]$ressourceGroup
    )   
    $private:AzResourceList = @()
    If ($tenantID) {
        $tenantInfo = Connect-pcitAzureTenant -tenantID $tenantID.Guid
        
    }
    ElseIf ($tenantName) {
        $tenantInfo = Connect-pcitAzureTenant -tenantName $tenantName
    }

    $private:pcitAZSubscriptions = get-pcitAZSubscriptions -tenantID $tenantInfo.Tenant.Id
    If ($subscriptionID) {
        $private:pcitAZSubscriptions = $private:pcitAZSubscriptions `
        | Where-Object {$_.id -eq $subscriptionID.Guid}
    }
    Foreach ($private:pcitAZSubscription in $private:pcitAZSubscriptions) {
        $private:CurrentAZContext = Set-AzContext -Subscription $private:pcitAZSubscription.id
        $private:resourceGroups = get-PcitAZResourceGroups -tenantID $private:pcitAZSubscription.tenantID -subscriptionID $private:pcitAZSubscription.id
        Foreach ($private:resourceGroup in $private:resourceGroups) {
            $private:Resources = Get-AZResource -ResourceGroupName $private:resourceGroup.Name
            Foreach ($private:Resource in $private:Resources) {
                [pcitAZRBACDelegatableElement]$private:pcitAZRessource = [pcitAZRBACDelegatableElement]::new(
                    [pcitAZElementType]::Ressource,
                    $Resource.Name,
                    $Resource.Name,
                    $Resource.Name,
                    ($private:CurrentAZContext.Tenant.Id),
                    $Resource.ResourceId
                )
            }
            $Private:AzResourceList += $private:pcitAZRessource            
        }
     }
     return $Private:AzResourceList      
}

Function Get-pcitAZRoleDelegationReport {
    [CmdletBinding()]
    param (
        [parameter(ParameterSetName="id-defined",Mandatory=$true)]
        [parameter(ParameterSetName="id-query",Mandatory=$true)]
            [guid]$tenantID,
        [parameter(ParameterSetName="name-defined",Mandatory=$true)]
        [parameter(ParameterSetName="name-query",Mandatory=$true)]        
            [string]$tenantName,
        [parameter(ParameterSetName="id-defined",Mandatory=$false)]
        [parameter(ParameterSetName="name-defined",Mandatory=$true)]
        [pcitAZRBACDelegatableElement[]]$Objects = $null,
        [parameter(ParameterSetName="id-query",Mandatory=$false)]
        [parameter(ParameterSetName="name-query",Mandatory=$false)]
            [pcitAZElementType]$ResourceLevel = [pcitAZElementType]::ResourceGroup,
        [switch]$ReportInheritance
    )  
    If ($tenantID) {
        $tenantInfo = Connect-pcitAzureTenant -tenantID $tenantID.Guid
        
    }
    ElseIf ($tenantName) {
        $tenantInfo = Connect-pcitAzureTenant -tenantName $tenantName
    }

    [pcitAZRBACRoleAssignmentElement[]]$PermissionReportList = @()
    [pcitAZRBACDelegatableElement[]]$ScopeObjects = $null
    If (-not $Objects) {
        [pcitAZRBACDelegatableElement[]]$Objects = @()
        if ($ResourceLevel.value__ -ge ([pcitAZElementType]::managementGroup.value__)) {
            [pcitAZRBACDelegatableElement[]]$ScopeObjects += get-pcitAZmanagementGroups -tenantID $tenantInfo.tenant.Id
        }
        If ($ResourceLevel.value__ -ge ([pcitAZElementType]::Subscription.value__)) {
            [pcitAZRBACDelegatableElement[]]$ScopeObjects += get-pcitAZSubscriptions -tenantID $tenantInfo.tenant.Id
        }
        If ($ResourceLevel.value__ -ge ([pcitAZElementType]::ResourceGroup.value__)) {
            [pcitAZRBACDelegatableElement[]]$ScopeObjects += get-PcitAZResourceGroups -tenantID $tenantInfo.tenant.Id
        }
        If ($ResourceLevel.value__ -ge ([pcitAZElementType]::Ressource.value__)) {
            [pcitAZRBACDelegatableElement[]]$ScopeObjects += get-PcitAZResources -tenantID $tenantInfo.tenant.Id
        }        
    }
    Else {
        [pcitAZRBACDelegatableElement[]]$ScopeObjects = $Objects
    }

    Foreach ($Object in $ScopeObjects) {
        $ScopePermissions = get-AZRoleAssignment -Scope $Object.AzureRMPath
        Foreach ($ScopePermission in $ScopePermissions) {
            $CurrentPermissionElement = [pcitAZRBACRoleAssignmentElement]::new($ScopePermission,$Object.AzureRMPath)
            If ($ReportInheritance) {
                $PermissionReportList += $CurrentPermissionElement
            }
            Else {
                If ($CurrentPermissionElement.IsInherited -ne $true) {
                    $PermissionReportList += $CurrentPermissionElement
                }
            }
        }
    }
    return $PermissionReportList
}

export-modulemember -Function @(
    "Connect-pcitAzureTenant",
    "get-pcitAZmanagementGroups",
    "get-pcitAZSubscriptions",
    "get-PcitAZResourceGroups",
    "get-PcitAZResources",
    "Get-pcitAZRoleDelegationReport"
)