PSGISP.MicrosoftGraph.psm1

#---------------------------------------------
#------------------ Classes ------------------
#---------------------------------------------

#---------------------------------------------
#------------- Private Functions -------------
#---------------------------------------------


#---------------------------------------------
#-------------- Public Functions -------------
#---------------------------------------------
Function Approve-MgSessionPermission {
    <#
        .SYNOPSIS
        Checks if Connection is established with correct permissions

        .DESCRIPTION
        Checks if Connection is established with correct permissions

        .PARAMETER Permission
        Permissions which are needed in this session

        .INPUTS
        System.String[]

        .OUTPUTS
        none

        .EXAMPLE
        Approve-MgSessionPermission -Permission "User.ReadWrite.All","Application.ReadWrite.All"

        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = "Permissions which are needed in this session"
        )]
    [System.String[]]$Scope
    )
    Begin {
        $Session = Get-MgContext
    }
    Process {
        if ($null -ne $Session) {
            $Scope | ForEach-Object {
                $ScopeVar = $_
                if ($null -eq ($Session.Scopes | Where-Object {$_ -eq $ScopeVar})){
                    Throw "Not all Scope Permissions are granted! Please add this one to the Session: $Scope"
                }
            }
        }else {
            Throw "You are not connected to the Microsoft Graph API."
        }
    }
    End {
    }
}
Function Add-MgApplicationAPIPermission {
    <#
        .SYNOPSIS
        This function adds API permission to azure ad application.

        .DESCRIPTION
        This function adds application API permission to a azure ad application. It also automatically grants the permission.

        .PARAMETER ClientId
        ID of the application which will get new permissions.

        .PARAMETER APIPermissionId
        ID of the new permission. You can find those permissions with this command: Find-MgGraphPermission

        .PARAMETER APIPermissionType
        Type of API Permission. Role is used by default.

        .PARAMETER Wait
        The wait parameter must be used in loops and scripts.
        If you do not use this parameter the function will exceed before the new permissions are set in azure ad.
        Otherwise the new permissions aren't set successfully.

        .INPUTS
        System.String
        System.Management.Automation.SwitchParameter

        .OUTPUTS
        None

        .EXAMPLE
        Add-MgApplicationAPIPermission -ClientId $Application.Id -APIPermissionId $_.Id -Wait $true

        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'ID of the application which will get new permissions.'
        )]
        [System.String]$ClientId,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'ID of the new permission. You can find those permissions with this command: Find-MgGraphPermission'
        )]
        [System.String]$APIPermissionId,

        [Parameter(
            Mandatory = $false,
            HelpMessage = 'Type of API Permission. Role is used by default.'
        )]
        [ValidateSet('Role', 'Scope')]
        [System.String]$APIPermissionType = 'Role',

        [Parameter(
            Mandatory = $false,
            HelpMessage = "The wait parameter must be used in loops and scripts. If you do not use this parameter the function will exceed before the new permissions are set in azure ad Otherwise the new permissions aren't set successfully."
        )]
        [System.Management.Automation.SwitchParameter]$Wait = $false
    )
    Begin {
        #check authentication status
        Approve-MgSessionPermission -Scope @('Application.ReadWrite.All','Directory.ReadWrite.All')

        #Get Application existing Permisisons
        $Application = Get-MgApplication -ApplicationId $ClientId -ErrorAction Stop

        #Get Service Principal for App Role
        $MGServicePrincipal = Get-MgServicePrincipal -All -Property Id,DisplayName,AppId,Approles | Where-Object{$_.AppRoles.Id -eq $APIPermissionId}
        if ($null -eq $MGServicePrincipal) {
            Throw ('The Application API Permissions Id: ' + $APIPermissionId + ' cannot be found. This function can only use Application Permission Type')
        }
        
        #Get Service Principal of App $Application
        $ApplicationServicePrincipal = Get-MgServicePrincipal -All | Where-Object {$_.AppId -eq $Application.AppId}
        if ($null -eq $ApplicationServicePrincipal) {
            Throw ('The Application does no have a Service Principal. Create one before try to grant any permissions.')
        }
    }
    Process {
        #############################################
        ######Add API permission to application######
        #############################################
        #create new permissions
        [Microsoft.Graph.PowerShell.Models.MicrosoftGraphResourceAccess]$NewResourceAccess =  @{
            id = $APIPermissionId
            type = $APIPermissionType
        }
        [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphRequiredResourceAccess[]]$NewRequiredResourceAccess = @{
            ResourceAppId = $MGServicePrincipal.AppId
            ResourceAccess = @($NewResourceAccess)
        }

        #edit app object
        switch ($Application) {
            {$null -ne ($_ | Where-Object {$_.RequiredResourceAccess.ResourceAccess.Id -eq $APIPermissionId})}{
                Write-Warning ("API Permission " + $APIPermissionId + " exists already.")
            }
            {$null -ne ($_ | Where-Object {$_.RequiredResourceAccess.ResourceAppId -eq $MGServicePrincipal.AppId})} {
                ($Application.RequiredResourceAccess | Where-Object {$_.ResourceAppId -eq $MGServicePrincipal.AppId}).ResourceAccess += $NewResourceAccess
                #Update Application settings
                Update-MgApplication -ApplicationId $Application.Id -BodyParameter $Application
            }
            Default {
                $Application.RequiredResourceAccess += $NewRequiredResourceAccess
                #Update Application settings
                Update-MgApplication -ApplicationId $Application.Id -BodyParameter $Application
            }
        }

        ################################################
        ######Grant API permission for application######
        ################################################
        #Create hashtable for new ServicePrincipalAppRoleAssignedTo
        $Params = @{
            PrincipalId = $ApplicationServicePrincipal.Id
            ResourceId = $MgServicePrincipal.Id
            AppRoleId = $APIPermissionId
        }
        
        #check if assigment already exists
        if ($null -eq (Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $Params.ResourceId | Where-Object {$_.PrincipalId -eq $Params.PrincipalId -and $_.ResourceId -eq $Params.ResourceId -and $_.AppRoleId -eq $Params.AppRoleId})) {
            New-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $MgServicePrincipal.Id -BodyParameter $Params > $null
        }else {
            Write-Warning ('Grant Permission ' + $APIPermissionId + ' exists already.')
        }

        #if Wait parameter is used, wait until new application permission are set before exiting funtion
        while ($true) {
            if ($null -ne (Get-MgApplication -ApplicationId $Application.Id | Where-Object {$_.RequiredResourceAccess.ResourceAccess.Id -eq $APIPermissionId})) {
                Break
            }else {
                Start-Sleep -Seconds 1
            }
        }
    }
    End {
    }
}
Function Remove-MgApplicationAPIPermission {
    <#
        .SYNOPSIS
        This function removes API permission to azure ad application.

        .DESCRIPTION
        This function removes application API permission to a azure ad application. It also automatically ungrants the permission.

        .PARAMETER ClientId
        ID of the application which will get lose permissions.

        .PARAMETER APIPermissionId
        ID of the removed permission. You can find those permissions with this command: Find-MgGraphPermission

        .PARAMETER Wait
        The wait parameter must be used in loops and scripts.
        If you do not use this parameter the function will exceed before the new permissions are set in azure ad.
        Otherwise the new permissions aren't set successfully.

        .INPUTS
        System.String
        System.Management.Automation.SwitchParameter

        .OUTPUTS
        None

        .EXAMPLE
        Remove-MgApplicationAPIPermission -ClientId $Application.Id -APIPermissionId $_.Id -Wait

        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'ID of the application which will get lose permissions.'
        )]
        [System.String]$ClientId,

        [Parameter(
            Mandatory = $true,
            HelpMessage = 'ID of the removed permission. You can find those permissions with this command: Find-MgGraphPermission'
        )]
        [System.String]$APIPermissionId,

        [Parameter(
            Mandatory = $false,
            HelpMessage = "The wait parameter must be used in loops and scripts. If you do not use this parameter the function will exceed before the new permissions are set in azure ad. Otherwise the new permissions aren't set successfully."
        )]
        [System.Management.Automation.SwitchParameter]$Wait = $false
    )
    Begin {
        #check authentication status
        Approve-MgSessionPermission -Scope @('Application.ReadWrite.All','Directory.ReadWrite.All')

        #Get Application existing Permisisons
        $Application = Get-MgApplication -ApplicationId $ClientId -ErrorAction Stop

        #Get Service Principal for App Role
        $MGServicePrincipal = Get-MgServicePrincipal -All -Property Id,DisplayName,AppId,Approles | Where-Object{$_.AppRoles.Id -eq $APIPermissionId}
        if ($null -eq $MGServicePrincipal) {
            Throw ('The Application API Permissions Id: ' + $APIPermissionId + ' cannot be found. This function can only use Application Permission Type')
        }
        
        #Get Service Principal of App $Application
        $ApplicationServicePrincipal = Get-MgServicePrincipal -All | Where-Object {$_.AppId -eq $Application.AppId}
    }
    Process {
        #############################################
        ####remove API permission to application#####
        #############################################
        #remove the specififc resourceaccess object
        $ActiveResourceAccess = $application.RequiredResourceAccess | Where-Object {$_.ResourceAppId -eq $MGServicePrincipal.AppId}
        $RemovedResourceAccess = $ActiveResourceAccess.ResourceAccess | Where-Object {$_.Id -ne $APIPermissionId}

        #if this object wasn't the only one in the RequiredResourceObject add the other ResourceAccess Objects again
        if ($null -ne $RemovedResourceAccess) {
            ($Application.RequiredResourceAccess | Where-Object {$_.ResourceAppId -eq $MGServicePrincipal.AppId}).ResourceAccess = $RemovedResourceAccess
        }
        #if the object was the last, remove the whole RequiredResourceAccess Object
        else {
            #if there is any other RequiredResourceAccess just add the other again
            if ($Application.RequiredResourceAccess.Count -gt 1) {
                $Application.RequiredResourceAccess = $Application.RequiredResourceAccess | Where-Object {$_.ResourceAppId -ne $MGServicePrincipal.AppId}
            }
            #if there is no other RequiredResourceAccess just add an empty object
            else{
                $Application.RequiredResourceAccess = [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphRequiredResourceAccess[]]@{}
            }
        }
        
        #Update Application settings
        Update-MgApplication -ApplicationId $ClientId -BodyParameter $Application

        ################################################
        ##remove Grant API permission for application###
        ################################################
        #Get Exsiting ServicePrincipalAppRoleAssignment
        $ServicePrincipalAppRoleAssignment = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $MgServicePrincipal.Id | Where-Object {$_.PrincipalId -eq $ApplicationServicePrincipal.Id -and $_.ResourceId -eq $MgServicePrincipal.Id -and $_.AppRoleId -eq $APIPermissionId}

        #check if assigment already exists
        if ($null -ne $ServicePrincipalAppRoleAssignment) {
            Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $MgServicePrincipal.Id -AppRoleAssignmentId $ServicePrincipalAppRoleAssignment.Id > $null
        }else {
            Write-Warning ('Permission ' + $APIPermissionId + ' was not granted')
        }

        #if Wait parameter is used, wait until new application permission are set before exiting funtion
        while ($true) {
            if ($null -eq (Get-MgApplication -ApplicationId $Application.Id | Where-Object {$_.RequiredResourceAccess.ResourceAccess.Id -eq $APIPermissionId})) {
                Break
            }else {
                Start-Sleep -Seconds 1
            }
        }
    }
    End {
    }
}
Function Add-MgApplicationCertificate {
    <#
        .SYNOPSIS
        Add a Certificate to Azure AD Application

        .DESCRIPTION
        Add a Certificate to Azure AD Application

        .PARAMETER ClientId
        ID of the application which will get new permissions.

        .PARAMETER Certificate
        Certificate which will be added to the azure ad application

        .INPUTS
        System.String
        System.Security.Cryptography.X509Certificates.X509Certificate2

        .OUTPUTS
        None

        .EXAMPLE
        Add-MgAppCertificate -Application $Application -Certificate $Certificate

        .LINK
        https://github.com/gisp497/psgisp
    #>

    [CmdletBinding()]
    param (
        [Parameter(
            Mandatory = $true,
            HelpMessage = 'ID of the application which will get new permissions.'
        )]
        [System.String]$ClientId,

        [Parameter(
            Mandatory = $true,
            HelpMessage = "Certificate which will be added to the azure ad application"
        )]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
    )
    Begin {
        #Approve Permissions of current Session
        Approve-MGSessionPermission -Scope 'Application.ReadWrite.All'

        #Get Application existing Permisisons
        $Application = Get-MgApplication -ApplicationId $ClientId -ErrorAction Stop
    }
    Process {
        [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphKeyCredential]$NewKeyCredentials = @{
            Type = "AsymmetricX509Cert";
            Usage = "Verify";
            key = $Certificate.RawData
        }

        #add new permissions to the existing application
        $Application.KeyCredentials += $NewKeyCredentials

        #Update new Application with the correct certificate
        Update-MgApplication -ApplicationId $Application.Id -BodyParameter $Application -ErrorAction Stop
    }
    End {
    }
}

#---------------------------------------------
#--------- Export Public Functions -----------
#---------------------------------------------
$PublicFunctions = @(
    'Add-MgApplicationAPIPermission',
    'Remove-MgApplicationAPIPermission',
    'Add-MgApplicationCertificate',
    'Approve-MgSessionPermission'
)
Export-ModuleMember -Function $PublicFunctions