Automation with GitHub Workflows
1. Prepare your tenant
To allow GitHub workflow accessing your Microsoft Entra ID tenant, you first need to:- Register an application
- Consent to the required permission. Each operations may require different permission. For more information check the Graph API documentation. You can also find the permissions and their IDs in the Microsoft Graph permissions reference.
- Create application secret
1.1 Register GitHub application
function Add-GitHubWorkflowApp { # Create app registration $params = @{ displayName = "GitHub workflow" description = "This application use to authentication the GitHub workflow" signInAudience = "AzureADMyOrg" requiredResourceAccess = @( @{ resourceAppId = "00000003-0000-0000-c000-000000000000" resourceAccess = @( @{ id = "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9" type = "Role" } @{ id = "246dd0d5-5bd0-4def-940b-0421030a5b68" type = "Role" } @{ id = "01c0a623-fc9b-48e9-b794-0756f8e8f067" type = "Role" } ) } ) } $appRegistration = New-MgApplication -BodyParameter $params Write-Host "App registration created with app ID" $appRegistration.AppId # Create corresponding service principal $params = @{ appId = $appRegistration.AppId } $servicePrincipal = New-MgServicePrincipal -BodyParameter $params Write-Host "Service principal created with ID" $servicePrincipal.Id # Get Microsoft Graph service principal $graphServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId appId='00000003-0000-0000-c000-000000000000' # Grant admin consent $roles = @('1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9', '246dd0d5-5bd0-4def-940b-0421030a5b68', '01c0a623-fc9b-48e9-b794-0756f8e8f067') foreach ($role in $roles) { $params = @{ "PrincipalId" =$servicePrincipal.Id "ResourceId" = $graphServicePrincipal.Id "AppRoleId" = $role } New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $graphServicePrincipal.Id -BodyParameter $params | Out-Null } # Create an application secret $params = @{ passwordCredential = @{ displayName = "GitHub workflow app client secret" } } Add-MgApplicationPassword -ApplicationId $appRegistration.Id -BodyParameter $params | Format-List } # Connect to Microsoft Entra tenant with all the required scopes Connect-MgGraph -Scopes "Application.ReadWrite.All AppRoleAssignment.ReadWrite.All" # Run the script Add-GitHubWorkflowApp # Get your tenant ID Get-MgContext | Format-List -Property TenantID
1.2 Copy the application details
2. Prepare your GitHub repo
2.1 Create secrets
GitHub secrets allow you to store sensitive information in your organization, repository, or repository environments.- On GitHub.com, navigate to the main page of the repository.
- Under your repository name, click Settings. If you cannot see the "Settings" tab, select the dropdown menu, then click Settings.
- In the "Security" section of the sidebar, select Secrets and variables, then click Actions.
- Select the Secrets tab.
- Click New repository secret.
- In the Name field, enter TenantId.
- In the Secret field, enter your tenant ID.
- Select Add secret.
- Repeat the last 4 steps and add ClientId with the client ID your registered. And ClientSecret with the application secret your created earlier.
2.2 [Optionally] Create variables
GitHub variables provide a way to store and reuse non-sensitive configuration information. You can store any configuration data such as compiler flags, usernames, or server names as variables. Variables are interpolated on the runner machine that runs your workflow. The example presented later in this page, use a variable name WebAppId which is passed into the PowerShell script that creates or update a conditional access policy.- On GitHub.com, navigate to the main page of the repository.
- Under your repository name, click Settings. If you cannot see the "Settings" tab, select the dropdown menu, then click Settings.
- In the "Security" section of the sidebar, select Secrets and variables, then click Actions.
- Select the Variables tab.
- Click New repository variable.
- In the Name field, , enter a name for your variable.
- In the Secret field, enter the value for your variable.
- Select Add variable.
2.3 Add your PowerShell script
Add your PowerShell script to the root directory of the repository. The following example shows an example ConditionalAccessPolicy.ps1 script that creates or update a conditional access policy.function Add-ConditionalAccessPolicy { param ( $PolicyName, $AppId ) # Define the conditional access policy $params = @{ templateId = $undefinedVariable displayName = $PolicyName state = "enabled" sessionControls = $undefinedVariable conditions = @{ userRiskLevels = @() signInRiskLevels = @( "high" "medium" ) clientAppTypes = @( "all" ) platforms = $undefinedVariable locations = $undefinedVariable times = $undefinedVariable deviceStates = $undefinedVariable devices = $undefinedVariable clientApplications = $undefinedVariable applications = @{ includeApplications = @( $AppId ) excludeApplications = @() includeUserActions = @() includeAuthenticationContextClassReferences = @() applicationFilter = $undefinedVariable } users = @{ includeUsers = @( "All" ) excludeUsers = @() includeGroups = @() excludeGroups = @() includeRoles = @() excludeRoles = @() includeGuestsOrExternalUsers = $undefinedVariable excludeGuestsOrExternalUsers = $undefinedVariable } } grantControls = @{ operator = "OR" builtInControls = @( "mfa" ) customAuthenticationFactors = @() termsOfUse = @() authenticationStrength = $undefinedVariable } } # Try to find the policy by name $ca = Get-MgBetaIdentityConditionalAccessPolicy -Filter "displayName eq '$PolicyName'" # Create or update the conditional access policy if ($null -ne $ca ) { # Check the existence of multiple policies with the same name. if ($ca.Count -gt 1 ) { $policyCount = $ca.Count Write-Error -Message "The operation could not be completed because $policyCount '$PolicyName' policies found in the directory." return } Write-Host "Updating policy " $ca.Id Update-MgBetaIdentityConditionalAccessPolicy -ConditionalAccessPolicyId $ca.Id -BodyParameter $params Write-Host "The conditional access policy has been successfully update" } else { Write-Host "Creating new policy" New-MgBetaIdentityConditionalAccessPolicy -BodyParameter $params | Format-List Write-Host "The conditional access policy has been successfully created" } } # Connect to Microsoft Entra tenant with the required scope Connect-MgGraph -Scopes "Policy.ReadWrite.ConditionalAccess" # Run the script Add-ConditionalAccessPolicy -PolicyName "Woodgrove demo - sign in risk" -AppId {App-ID}
1.3 Add a GitHub workflow
- In your repository, create the .github/workflows/ directory to store your workflow files.
- In the .github/workflows/ directory, create a new file called ConfigWorkflow.yml.
-
Add the
following code
# This is a basic workflow to help you get started with Actions name: Test Graph PowerShell # Controls when the workflow will run on: # Triggers the workflow on push request events but only for the "main" branch push: branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v4 # Run a command to check PowerShell version - name: Check PowerShell version shell: pwsh run: Write-Host $PSVersionTable.PSVersion # Setup cache for Microsoft Graph PowerShell - name: Setup PowerShell module cache id: cacher uses: actions/cache@v3 with: path: "~/.local/share/powershell/Modules" key: ${{ runner.os }}-MicrosoftGraphBeta # Install Microsoft Graph PowerShell modules - name: Install required PowerShell modules if: steps.cacher.outputs.cache-hit != 'true' shell: pwsh run: | Set-PSRepository PSGallery -InstallationPolicy Trusted Install-Module Microsoft.Graph.Beta -ErrorAction Stop # Connect to Entra ID, run the PowerShell script and disconnect - name: Connect, run the script and disconnect shell: pwsh env: TenantId: ${{ secrets.TenantId }} ClientId: ${{ secrets.ClientId }} ClientSecret: ${{ secrets.ClientSecret }} WebAppId: ${{ vars.WebAppId }} run: | Write-Host "Connect to Microsoft Entra ID with app ID and app secret" $SecuredPassword = ConvertTo-SecureString -String "$env:ClientSecret" -AsPlainText -Force $ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "$env:ClientId", $SecuredPassword Connect-MgGraph -TenantId "$env:TenantId" -ClientSecretCredential $ClientSecretCredential -NoWelcome Write-Host "Loading the PowerShell script .\ConditionalAccessPolicy.ps1" . .\ConditionalAccessPolicy.ps1 Write-Host "Running the PowerShell script" Add-ConditionalAccessPolicy -PolicyName "Woodgrove demo - sign in risk" -AppId ${{ env.WebAppId }} Write-Host "Disconnect from Microsoft Entra ID" Disconnect-MgGraph | Out-Null