Skip to content

Lab 05: B2B Guest Users - CLI Solutions

Note: These are CLI/PowerShell alternatives. The portal approach in solution.md is recommended for AZ-104 exam preparation.


Prerequisites

powershell
# Install Microsoft Graph module (if not installed)
Install-Module Microsoft.Graph -Scope CurrentUser

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "User.Invite.All", "User.ReadWrite.All", "Group.ReadWrite.All", "Directory.ReadWrite.All"

# Azure CLI login
az login

Task 1-2: Review and Configure External Collaboration Settings

powershell
# Using Microsoft Graph PowerShell

# Get current authorization policy (includes guest settings)
$authPolicy = Get-MgPolicyAuthorizationPolicy
$authPolicy | Format-List *Guest*

# View guest invite restrictions
# B2BCollaborationInbound settings
Get-MgPolicyAuthorizationPolicy | Select-Object -ExpandProperty GuestUserRoleId

# Guest role IDs:
# a0b1b346-4d3e-4e8b-98f8-753987be4970 = Same as member users (most permissive)
# 10dae51f-b6af-4016-8d66-8c2a99b929b3 = Limited access (default)
# 2af84b1e-32c8-42b7-82bc-daa82404023b = Restricted access (most restrictive)

Task 3: Invite a Guest User

Using Azure CLI

bash
# Invite guest user using Azure CLI
az ad user invite \
    --invited-user-email-address "external@partner.com" \
    --invited-user-display-name "External Partner User" \
    --send-invitation-message true \
    --invitation-message-body "Welcome! You're invited to collaborate on Project Alpha"

# Output will include:
# - id (user object ID)
# - inviteRedeemUrl (acceptance URL)
# - status

Using Microsoft Graph PowerShell

powershell
# Create invitation
$invitation = New-MgInvitation `
    -InvitedUserEmailAddress "external@partner.com" `
    -InvitedUserDisplayName "External Partner User" `
    -SendInvitationMessage:$true `
    -InviteRedirectUrl "https://portal.azure.com"

# View invitation details
$invitation | Format-List *

Task 4: Examine Guest User Properties

powershell
# Find guest users
Get-MgUser -Filter "userType eq 'Guest'" | Select-Object DisplayName, UserPrincipalName, Mail, UserType

# Get specific guest user by email
$guest = Get-MgUser -Filter "mail eq 'external@partner.com'"

# View all properties
$guest | Format-List *

# Check group memberships
Get-MgUserMemberOf -UserId $guest.Id | ForEach-Object {
    Get-MgDirectoryObject -DirectoryObjectId $_.Id
}

# Check role assignments
Get-MgUserAppRoleAssignment -UserId $guest.Id

Task 5: Check Invitation Status

powershell
# List all pending invitations (users who haven't accepted)
Get-MgUser -Filter "externalUserState eq 'PendingAcceptance'" | 
    Select-Object DisplayName, Mail, ExternalUserState

# Resend invitation if needed
$user = Get-MgUser -Filter "mail eq 'external@partner.com'"

# Create new invitation for same user (resend)
New-MgInvitation `
    -InvitedUserEmailAddress "external@partner.com" `
    -InvitedUser @{ Id = $user.Id } `
    -SendInvitationMessage:$true `
    -InviteRedirectUrl "https://portal.azure.com"

Task 6: Grant Guest User Resource Access

bash
# Create resource group
az group create --name rg-guest-collaboration --location eastus

# Get guest user object ID
GUEST_ID=$(az ad user list --filter "mail eq 'external@partner.com'" --query "[0].id" -o tsv)

# Assign Reader role to guest on resource group
az role assignment create \
    --assignee $GUEST_ID \
    --role "Reader" \
    --resource-group rg-guest-collaboration

# Verify assignment
az role assignment list \
    --resource-group rg-guest-collaboration \
    --assignee $GUEST_ID \
    --output table

Task 7: Add Guest to Security Group and Assign Storage Access

bash
# Create security group
GROUP_ID=$(az ad group create \
    --display-name "sg-external-partners" \
    --mail-nickname "sg-external-partners" \
    --description "External partner access group" \
    --query id -o tsv)

# Add guest to group
az ad group member add \
    --group sg-external-partners \
    --member-id $GUEST_ID

# Verify membership
az ad group member list --group sg-external-partners --query "[].displayName"

# Create storage account
az storage account create \
    --resource-group rg-guest-collaboration \
    --name stguestcollab$RANDOM \
    --sku Standard_LRS \
    --location eastus

# Assign group role on storage account
STORAGE_ID=$(az storage account show \
    --resource-group rg-guest-collaboration \
    --name stguestcollab* \
    --query id -o tsv)

az role assignment create \
    --assignee $GROUP_ID \
    --role "Storage Blob Data Contributor" \
    --scope $STORAGE_ID

Task 8: Conditional Access (Graph API)

powershell
# Note: Creating Conditional Access policies via CLI requires 
# Microsoft Graph PowerShell or direct API calls

Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess"

# Create CA policy targeting guests
$params = @{
    DisplayName = "CA-Guest-MFA-Required"
    State = "enabledForReportingButNotEnforced"
    Conditions = @{
        Users = @{
            IncludeGuestsOrExternalUsers = @{
                GuestOrExternalUserTypes = "b2bCollaborationGuest,b2bCollaborationMember,b2bDirectConnectUser,internalGuest"
                ExternalTenants = @{
                    MembershipKind = "all"
                }
            }
        }
        Applications = @{
            IncludeApplications = @("All")
        }
    }
    GrantControls = @{
        Operator = "OR"
        BuiltInControls = @("mfa")
    }
}

New-MgIdentityConditionalAccessPolicy -BodyParameter $params

Task 9: Review Guest Sign-in Logs

powershell
# Get sign-in logs for guest users
Connect-MgGraph -Scopes "AuditLog.Read.All"

# Get recent guest sign-ins
Get-MgAuditLogSignIn -Filter "userType eq 'Guest'" -Top 50 | 
    Select-Object CreatedDateTime, UserDisplayName, UserPrincipalName, 
                  AppDisplayName, Status, ConditionalAccessStatus

# Get sign-ins for specific guest
Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'external_partner.com#EXT#@tenant.onmicrosoft.com'" |
    Select-Object CreatedDateTime, AppDisplayName, Status

Task 10: Remove Guest User

bash
# Remove from group
az ad group member remove \
    --group sg-external-partners \
    --member-id $GUEST_ID

# Remove direct role assignments
az role assignment delete \
    --assignee $GUEST_ID \
    --resource-group rg-guest-collaboration

# Delete user
az ad user delete --id $GUEST_ID

# Verify deletion
az ad user show --id $GUEST_ID  # Should fail with "Resource not found"

Cleanup

bash
# Delete resource group (includes storage account)
az group delete --name rg-guest-collaboration --yes --no-wait

# Delete security group
az ad group delete --group sg-external-partners
powershell
# Delete Conditional Access policy
$policy = Get-MgIdentityConditionalAccessPolicy | 
    Where-Object { $_.DisplayName -eq "CA-Guest-MFA-Required" }
Remove-MgIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $policy.Id

# Disconnect
Disconnect-MgGraph

Useful Commands Reference

TaskCommand
Invite guestNew-MgInvitation / az ad user invite
List guestsGet-MgUser -Filter "userType eq 'Guest'"
Add to groupaz ad group member add
Remove from groupaz ad group member remove
Delete useraz ad user delete --id <object-id>
View sign-insGet-MgAuditLogSignIn -Filter "userType eq 'Guest'"
Check invite statusGet-MgUser -Filter "externalUserState eq 'PendingAcceptance'"

Microsoft Graph API Direct Calls

bash
# Invite user via REST API
curl -X POST "https://graph.microsoft.com/v1.0/invitations" \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
        "invitedUserEmailAddress": "external@partner.com",
        "invitedUserDisplayName": "External Partner",
        "sendInvitationMessage": true,
        "inviteRedirectUrl": "https://portal.azure.com"
    }'

Released under the MIT License.