{"id":28524934,"url":"https://github.com/microsoftgraph/group-membership-management-tenant","last_synced_at":"2026-01-29T12:39:38.536Z","repository":{"id":66157011,"uuid":"519276796","full_name":"microsoftgraph/group-membership-management-tenant","owner":"microsoftgraph","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-02T23:49:42.000Z","size":334,"stargazers_count":6,"open_issues_count":1,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-06-09T11:13:10.002Z","etag":null,"topics":["devxpartner"],"latest_commit_sha":null,"homepage":"","language":"Bicep","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/microsoftgraph.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-29T16:15:33.000Z","updated_at":"2025-04-02T23:49:29.000Z","dependencies_parsed_at":"2024-03-08T08:16:16.982Z","dependency_job_id":null,"html_url":"https://github.com/microsoftgraph/group-membership-management-tenant","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/microsoftgraph/group-membership-management-tenant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoftgraph%2Fgroup-membership-management-tenant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoftgraph%2Fgroup-membership-management-tenant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoftgraph%2Fgroup-membership-management-tenant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoftgraph%2Fgroup-membership-management-tenant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/microsoftgraph","download_url":"https://codeload.github.com/microsoftgraph/group-membership-management-tenant/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/microsoftgraph%2Fgroup-membership-management-tenant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28877876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devxpartner"],"created_at":"2025-06-09T11:12:23.458Z","updated_at":"2026-01-29T12:39:38.529Z","avatar_url":"https://github.com/microsoftgraph.png","language":"Bicep","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Group Membership Management (GMM) tool Overview\n\nThis tool enables admins to sync the membership of Microsoft 365 Groups using one or more security groups that may or may not be nested, and keep the memberships up to date by syncing with the source groups at regular intervals.\n\nPlease read before proceeding:\n\n-   The tool is based on .Net, Azure Functions, and Azure Table Storage. All of these are requirements and must be deployed by the customer onto their Azure subscription.\n-   The tool interacts with Microsoft cloud using Graph APIs as a data source. The app needs to be onboarded and granted permissions by the customer tenant admin.\n-   The tool allows the user to specify: source security groups, the destination Microsoft 365 Group, frequency of syncs, and start date of sync.\n\n\u003cbr\u003e\n\n# Table of Contents\n\n1. [GMM Setup Overview](#gmm-setup-overview)\n    * [Setup prerequisites](#setup-prerequisites)\n    * [Resource groups overview](#resource-groups-overview)\n    * [Prereqs keyvault overview](#prereqs-keyvault-overview)\n    * [ARM templates and parameter files overview](#arm-templates-and-parameter-files-overview)\n    * [GMM environments](#gmm-environments)\n2. [GMM Setup](#gmm-Setup)\n    * [Create Azure Devops Repositories](#create-azure-devops-repositories)\n    * [Create resource groups and the prereqs keyvault](#create-resource-groups-and-the-prereqs-keyvault)\n    * [Create the Graph application and populate prereqs keyvault](#create-the-graph-application-and-populate-prereqs-keyvault)\n    * [Create the WebAPI application and populate prereqs keyvault](#create-the-webapi-application-and-populate-prereqs-keyvault)\n    * [Adding a new GMM environment](#adding-a-new-gmm-environment)\n    * [Create a Service Connection](#create-a-service-connection)\n    * [Set up email notifications](#set-up-email-notifications)\n    * [Create an Azure DevOps environment](#create-an-azure-devops-environment)\n    * [Create an Azure DevOps pipeline](#create-an-azure-devops-pipeline)\n    * [Post-Deployment tasks](#post-deployment-tasks)\n    * [(Optional) Set up a production environment(s)](#optional-set-up-a-production-environment)\n3. [Using GMM](#using-gmm)\n    * [Adding Graph application as an owner to GMM managed destination group](#adding-graph-application-as-an-owner-to-gmm-managed-destination-group)\n    * [Creating synchronization jobs for source groups](#creating-synchronization-jobs-for-source-groups)\n    * [Dry Run Settings](#dry-run-settings)\n4. [Setting AzureMaintenance function](#setting-azuremaintenance-function)\n5. [Setting GMM in a demo tenant](#setting-gmm-in-a-demo-tenant)\n6. [Setting up GMM UI](#setting-up-gmm-ui)\n7. [Steps to debug and troubleshoot a failing sync](#steps-to-debug-and-troubleshoot-a-failing-sync)\n8. [Breaking changes](#breaking-changes)\n\n\u003cbr\u003e\n\n\n# GMM Setup Overview\n\nThis section aims to provide the background information needed to understand the GMM setup process. If you would like to skip this section and start setting up GMM, please see [GMM Setup](#GMM-Setup).\n\n## Setup prerequisites:\n\n-   Azure Subscription - [Try Azure](https://azure.microsoft.com/en-us/free/).\n-   Azure DevOps - [Try Azure DevOps Services](https://azure.microsoft.com/en-us/pricing/details/devops/azure-devops-services/)\n\n    Note: to follow these steps you will need to sign in to [Azure Portal](https://portal.azure.com/) and [Azure DevOps Portal](https://dev.azure.com/.) with an account that has permissions to create new Azure resources.\n\n-   Powershell Core v7.x [Download and install Windows PowerShell 7.x](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows)\n\n- [Visual Studio](https://visualstudio.microsoft.com/downloads/) or [Visual Studio Code](https://visualstudio.microsoft.com/downloads/)\n- [.NET SDK Version 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)\n\n    Note: the .NET version targeted by GMM can be changed in the ` global.json` file.\n\n    To find out what .NET SDK versions you currently have installed run this command from the command line:\n\n        dotnet --list-sdks\n\n## Resource groups overview\n\nGMM logically separates the resources it uses into three [resource groups](https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/manage-resource-groups-portal#what-is-a-resource-group):\n\n-   prereqs\n-   data\n-   compute\n\nThroughout this document we will use the following tokens as place holders:\n\n- `\u003cSolutionAbbreviation\u003e` - This is a name prefix (2 to 3 characters long). The current default value is '`gmm`'. See the Notes section below for information on how to change this value.\n- `\u003cEnvironmentAbbreviation\u003e` - This is the name of your environment (2 to 6 characters long). Each environment should have an unique value to avoid name collisions. See the Notes section below for more information.\n\nWhen setting up GMM, you will need to provide the value for each one of these as they will be used to name the Azure resources. Please Avoid using the names on this document as they are already in use and some Azure resources are required to have a unique name across all tenants globally.\n\nThe naming convention for the resource groups and other resources is `\u003cSolutionAbbreviation\u003e`-`\u003cResourceGroupName\u003e`-`\u003cEnvironmentAbbreviation\u003e`, i.e gmm-data-ua, gmm-data-prod, gmm-compute-prod.\n\n### Notes:\n\nBoth `\u003cSolutionAbbreviation\u003e` and `\u003cEnvironmentAbbreviation\u003e` must only contain numbers and/or lowercase letters! Using capital letters in either will cause problems!\n\nCurrently,  the default value for `\u003cSolutionAbbreviation\u003e` is `gmm`. To change this default, update the `solutionAbbreviation` variable in the `vsts-cicd.yml` file of your `Private` repo.\n\nThe length restrictions for both `\u003cSolutionAbbreviation\u003e` and `\u003cEnvironmentAbbreviation\u003e` can be changed by updating their `minLength` and `maxLength` variables in the ARM templates (`template.bicep`).\n\nWe recommend trying to use unique `\u003cSolutionAbbreviation\u003e` and `\u003cEnvironmentAbbreviation\u003e` names, since some resources in Azure require to have unique names globally so it is possible to have name collisions.\n\n\n## Prereqs keyvault overview\n\nEach resource group will have a corresponding keyvault; The naming convention for the keyvault is the same as the resource groups.\n\nInitially, we will use a script to create the `\u003cSolutionAbbreviation\u003e`-prereqs-`\u003cEnvironmentAbbreviation\u003e` keyvault. This keyvault needs to be populated before deploying the ARM templates, as these rely on it to deploy the resources needed for GMM.  This keyvault must be created under its corresponding resource group: `\u003cSolutionAbbreviation\u003e`-prereqs-`\u003cEnvironmentAbbreviation\u003e`.\n\nThese two keyvaults will be created by the ARM templates, so no action is needed for these two:\n\n-   `\u003cSolutionAbbreviation\u003e`-data-`\u003cEnvironmentAbbreviation\u003e`\n-   `\u003cSolutionAbbreviation\u003e`-compute-`\u003cEnvironmentAbbreviation\u003e`\n\n## ARM templates and parameter files overview\n\nGMM leverages infrastructure as code through the use of ARM templates. Most of the Azure resources needed by GMM are created by the ARM templates on the `Public` repository. We use parameter files to pass user specific information or settings to the ARM templates. ARM templates and parameter files can be found within the `Infrastructure` folders of the parent project and each of the functions apps:\n\n    -   Documentation\n    -   Infrastructure\n        -   data\n            -   parameters\n    -   Scripts\n    -   Service\n        -   Hosts\n            -   JobTrigger\n                -   Infrastructure\n                    -   data\n                        -   parameters\n                    -   compute\n                        -   parameters\n            -   ...\n    -   yaml\n\n## GMM environments\n\nA `GMM environment` is the collection of resource groups, resources, and operating tenant that make a GMM instance.\n\nThe code is provided with a sample environment, `env`. The [vsts-cicd.yml](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/vsts-cicd.yml) `yaml/deploy-pipeline.yml` template and parameter files for the `env` environment are provided to serve as a guide to create new environments. This name must not be reused.\n\nThe steps in this document will setup a single environment i.e. prodv2, if you would like to setup other environments i.e. int and ua, you will need to go through these steps again replacing `\u003cEnvironmentAbbreviation\u003e` accordingly.\n\n\n# GMM Setup\n\n## Create Azure Devops Repositories\n\n### IMPORTANT: Make sure you have access to Azure DevOps Pipelines \u0026 Repos\n\n1. ### Sign in to [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/)\n\n2. ### Create a private project:\n\n    -   You can create a new project by following these instructions: [Create a project in AzureDevOps](https://docs.microsoft.com/en-us/azure/devops/organizations/projects/create-project?view=azure-devops\u0026tabs=preview-page)\n    -   You can also use an existing project in your organization.\n\n3. ### Create two new repositories\n\n    - `Public` repository:\n        - Your `Public` repo will mimic [group-membership-management ](https://github.com/microsoftgraph/group-membership-management) GitHub repository.\n        - Create the `Public` repo based off this GitHub repo by following the [Manually Importing a Repo](https://docs.microsoft.com/en-us/azure/devops/repos/git/import-git-repository?view=azure-devops#manually-import-a-repo) documentation.\n        - Keep the commit history of your `Public` repo in sync with this GitHub repo by running the following commands from your `Public` repo:\n\n                git remote add upstream https://github.com/microsoftgraph/group-membership-management.git\n                git fetch upstream\n                git checkout upstream/main -b main\n                git merge upstream/main\n                git push --set-upstream origin main -f\n\n    - `Private` repository:\n        - Your `Private` repo will refer to your `Public` repo as a submodule.\n        - Create your `Private` repo based off [this](https://github.com/microsoftgraph/group-membership-management-tenant) repo by following the [Manually Importing a Repo](https://docs.microsoft.com/en-us/azure/devops/repos/git/import-git-repository?view=azure-devops#manually-import-a-repo) documentation.\n        - You should see `public` submodule within your `Private` repo.\n        - Run the following commands in PowerShell to clone `Private` repo with the submodule:\n\n                git clone \u003curl-of-private-repo\u003e\n                ls # you should see public submodule\n                cd \\public\n                ls # no contents within public submodule\n                git submodule update --init --recursive\n                ls # you should see contents within public submodule\n\n        - Let’s say that a new commit is added to the main branch of your `Public` repository. To add that new commit to the submodule in the `Private` repository, run the following commands:\n\n                git submodule update --remote --merge\n                git add *\n                git commit -m “updated public submodule”\n                git push\n\n    Note: Make sure that you set the default branch by going to Azure DevOps -\u003e Repos -\u003e Branches\n\n## Create resource groups and the prereqs keyvault\n\nSee [Resource Groups](#resource-groups-overview) and [Prereqs Keyvault](#prereqs-keyvault-overview).\n\nThe following script is going to create the Azure resource groups and the prereqs keyvault required to set up GMM. We create these resource groups and keyvault in order for the ARM templates to be able to create additional resources and deploy the code.\n\nFrom your `PowerShell Core 7.x` command prompt navigate to the Scripts folder of your `Public` repo and type these commands:\n\n    1. . ./Set-Environment.ps1\n    2. Set-Environment  -solutionAbbreviation \"\u003csolutionAbbreviation\u003e\" `\n                        -environmentAbbreviation \"\u003cenvironmentAbbreviation\u003e\" `\n                        -objectId \"\u003cobjectId\u003e\" `\n                        -resourceGroupLocation \"\u003cresourceGroupLocation\u003e\" `\n                        -overwrite $true\n\nWhere:\n* `\u003cobjectId\u003e` - the Azure Object Id of the user, group or service principal to which access to the prereqs keyvault is going to be granted. To find your Object Id go to `Azure Portal -\u003e AAD -\u003e Search Tenant Search Bar`, look up your Microsoft email, and click on your profile. Your Object Id should be under the `Identity` section.\n* `\u003cresourceGroupLocation\u003e` - the Azure location where the resources are going to be created. Please refer to [this](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/resource-location?tabs=azure-powershell) documentation to know the available resource locations.\n\n\u003cb\u003eNote:\u003c/b\u003e If you get an error stating \"script is not digitally signed\" when running any of the provided PowerShell scripts, try running this cmdlet and rerunning the script:\n\n    Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass\n\n## Create the Graph application and populate prereqs keyvault\n\nThe following PowerShell script will create a new application, `\u003csolutionAbbreviation\u003e`-Graph-`\u003cenvironmentAbbreviation\u003e`,  that will allow GMM to access the Microsoft Graph API. It will also save these settings in the prereqs keyvault:\n\n-   graphAppClientId\n-   graphAppTenantId\n-   graphAppClientSecret\n\nNote that this script will create an new application and authentication will be done using a client id and client secret pair. If you prefer to use a certificate skip to the next section.\n\nFrom your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo and run these commands:\n\n    1. . ./Set-GraphCredentialsAzureADApplication.ps1\n    2. Set-GraphCredentialsAzureADApplication\t-SubscriptionName \"\u003cSubscriptionName\u003e\" `\n                                                -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\" `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -TenantIdToCreateAppIn \"\u003cApp-TenantId\u003e\" `\n                                                -TenantIdWithKeyVault \"\u003cKeyVault-TenantId\u003e\" `\n                                                -Clean $true `\n                                                -Verbose\nFollow the instructions on the screen.\n\nNote:\n* AppTenantId \u003capp-tenant-id\u003e - If the application is going to be installed in a different tenant, set that tenant id here. Refer to [Setting GMM in a demo tenant](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/DemoTenant.md) if you want to set up a demo tenant.\n* KeyVaultTenantId \u003ckeyvault-tenant-id\u003e - This is the tenant where your GMM resources are located, i.e. keyvaults, storage account.\n* If you only have one tenant, these will be set to the same tenant id.\n\n### Creating the certificate\nIf you don't need to use a certificate for authentication you can skip this step.\n\nIt is also possible to use a certificate instead of an id / secret pair to authenticate and query Graph API. GMM will automatically look for a certificate and use it if available, otherwise it will fallback to id / secret.\n\nWe need to create a certificate that is going to be used for authentication, we are going to use the prereqs keyvault to create and store the certificate. Take note of the certificate name since we need to provide it in the next step.\nSee [Quickstart: Set and retrieve a certificate from Azure Key Vault using the Azure portal](https://docs.microsoft.com/en-us/azure/key-vault/certificates/quick-create-portal) documentation.\n\nYou can also use an existing certificate and upload it to the prereqs keyvault, you will need to provide a friendly certificate name that we will need in the next step.\n\nThe script will create these settings in your prereqs keyvault.\n\n-   graphAppClientId\n-   graphAppTenantId\n-   graphAppClientSecret\n-   graphAppCertificateName\n\nFrom your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo and run these commands:\n\n    1. . ./Set-GraphCredentialsAzureADApplication.ps1\n    2. Set-GraphCredentialsAzureADApplication\t-SubscriptionName \"\u003cSubscriptionName\u003e\" `\n                                                -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\" `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -TenantIdToCreateAppIn \"\u003cApp-TenantId\u003e\" `\n                                                -TenantIdWithKeyVault \"\u003cKeyVault-TenantId\u003e\" `\n                                                -CertificateName \"\u003cCertificateName\u003e\" `\n                                                -Clean $true `\n                                                -Verbose\n\nFollow the instructions on the screen.\n\nFrom your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Private` repo and run these commands:\n\n    1. . ./Set-GMMSqlMembershipAzureADApplication.ps1\n    2. Set-GMMSqlMembershipAzureADApplication\t-SubscriptionName \"\u003cSubscriptionName\u003e\" `\n                                                -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\" `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -Clean $true `\n                                                -Verbose\n\n### Upload the certificate to your `\u003csolutionAbbreviation\u003e`-Graph-`\u003cenvironmentAbbreviation\u003e` application.\n\nIf you don't need to use a certificate for authentication you can skip this step.\n\nWe need to upload the certificate to the `\u003csolutionAbbreviation\u003e`-Graph-`\u003cenvironmentAbbreviation\u003e` application, in order to do that we need to export it from the prerqs keyvault.\n\nExporting the certificate:\n\n1. In the Azure Portal navigate to your prereqs keyvault, it will be named following this convention `\u003csolutionAbbreviation\u003e`-prereqs-`\u003cenvironmentAbbreviation\u003e`.\n2. Locate and click on the Certificates blade on the left menu.\n3. Click on your certificate from the list.\n4. Click on the latest version.\n5. On the top menu click on 'Download in CER format' button to download the certificate.\n\nIf you need more details on how to export the certificate please see [Quickstart: Set and retrieve a certificate from Azure Key Vault using the Azure portal](https://docs.microsoft.com/en-us/azure/key-vault/certificates/quick-create-portal) documentation.\n\nUploading the certificate:\n\n1. In the Azure Portal navigate to your 'Azure Active Directory'. If you don't see it on your screen you can use the top search bar to locate it.\n2. Navigate to 'App registrations' blade on the left menu.\n3. Click on 'All applications\" to locate and open your `\u003csolutionAbbreviation\u003e`-Graph-`\u003cenvironmentAbbreviation\u003e` application.\n4. On your application screen click on 'Certificates and secrets' blade on the left menu.\n5. Click on the 'Upload certificate' button.\n6. Locate and add your certificate.\n\n### Granting permissions\n\nOnce your application is created, we need to grant the requested permissions to use Microsoft Graph API:\n\n1. In the Azure Portal navigate to your `Azure Active Directory`. If you don't see it on your screen you can use the top search bar to locate it.\n2. Navigate to `App registrations` blade on the left menu.\n3. Click on `All applications` to locate and open your `\u003csolutionAbbreviation\u003e`-Graph-`\u003cenvironmentAbbreviation\u003e` application.\n4. On your application screen click on `API permissions` blade on the left menu.\n5. Click on the 'Grant admin consent for `\u003cYourOrganizationName\u003e`' button.\n6. You might need to refresh the page to see the permissions status updated.\n\n## Create the WebAPI application and populate prereqs keyvault\n\nSee [WebApiSetup.md](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Hosts/WebApi/Documentation/WebApiSetup.md) for more information.\n\n## Create the UI application and populate prereqs keyvault\n\nSee [UISetup.md](https://github.com/microsoftgraph/group-membership-management/blob/main/UI/Documentation/UISetup.md) for more information.\n\n## Set sender address for email notification \n\nSee [SetSenderAddressForEmailNotification.md](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Repositories.Mail/Documentation/SetSenderAddressForEmailNotification.md) for more information.\n\n## Adding a new GMM environment\n\nSee [GMM Environments](#gmm-environments) and [ARM templates and parameter files overview](#ARM-templates-and-parameter-files-overview).\n\n### To add a new GMM environment:\n\n\n1. In your `Private` repo, locate and open file [vsts-cicd.yml](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/vsts-cicd.yml)\n2. Locate the `yaml/deploy-pipeline.yml` template of the `env` environment. It should look like this:\n```\n    - template: yaml/deploy-pipeline.yml\n    parameters:\n        solutionAbbreviation: '$(SolutionAbbreviation)'\n        environmentAbbreviation: '\u003cenv\u003e'\n        tenantId: $(tenantId)\n        subscriptionName: $(subscriptionName_nonprod)\n        subscriptionId: $(subscriptionId_nonprod)\n        location: $(location)\n        serviceConnection: '$(SolutionAbbreviation)-serviceconnection-\u003cenv\u003e'\n        dependsOn:\n        - Build_Common\n        - Build_CopyParameters\n        stageName: 'NonProd_\u003cenv\u003e'\n        functionApps:\n        - function:\n        name: 'NonProdService'\n        - function:\n        name: 'GraphUpdater'\n        - function:\n        name: 'MembershipAggregator'\n        dependsOn:\n        - 'GraphUpdater'\n        - function:\n        name: 'GroupMembershipObtainer'\n        dependsOn:\n        - 'MembershipAggregator'\n        - function:\n        name: 'AzureMaintenance'\n        - function:\n        name: 'TeamsChannel'\n        dependsOn:\n        - 'MembershipAggregator'\n        - function:\n        name: 'JobTrigger'\n        dependsOn:\n        - 'MembershipAggregator'\n        - function:\n        name: 'Notifier'\n        deployJobScheduler: true\n        condition: |\n        and(\n            succeeded('Build_Common'),\n            succeeded('Build_CopyParameters'),\n            eq(variables['Build.SourceBranch'], 'refs/heads/develop'),\n            in(variables['Build.Reason'], 'IndividualCI', 'Manual')\n        )\n```\n3. Copy and paste the template located in step two, then replace the values for these settings accordingly using the name of your new environment:\n    - environmentAbbreviation\n    - serviceConnection\n    - stageName\n\n    Save your changes.\n\n4. In your `Private` repo, locate and open file [vsts-cicd.yml](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/vsts-cicd.yml)\n\n5. Locate the `yaml/copy-deploy-webapp.yml` template of the `env` environment. It should look like this:\n```\n    - template: yaml/copy-deploy-webapp.yml\n    parameters:\n        alias: ''\n        solutionAbbreviation: '$(SolutionAbbreviation)'\n        environmentAbbreviation: '\u003cenv\u003e'\n        tenantId: $(tenantId)\n        subscriptionId: $(subscriptionId_prod)\n        location: $(location)\n        serviceConnection: '$(SolutionAbbreviation)-serviceconnection-\u003cenv\u003e'\n        buildRelease: ${{variables.buildRelease}}\n        stageName: 'Prod_webapp_\u003cenv\u003e'\n        condition: |\n        and(\n            succeeded('Build_WebApp'),\n            in(variables['Build.SourceBranch'], 'refs/heads/main'),\n            in(variables['Build.Reason'], 'IndividualCI', 'Manual')\n        )\n```\n6. Edit the following fields of the duplicated template:\n    * There are three parameters that you must be set to your `\u003cEnvironmentAbbreviation\u003e`:\n\n        - environmentAbbreviation\n        - serviceConnection\n        - stageName\n\n7. Save your changes.\n\n8. In your `Private` repo, locate and open file [vsts-cicd.yml](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/vsts-cicd.yml)\n\n9. Locate the `repositories` information at the top. It should look like this:\n    ```\n    resources:\n      repositories:\n      - repository: group-membership-management\n        type: git\n        name: \u003cADO-PROJECT\u003e/\u003cADO-GMM-PUBLIC-REPOSITORY\u003e\n        ref: refs/tags/\u003cTAG\u003e\n    ```\n10. - Replace `\u003cADO-PROJECT\u003e/\u003cADO-GMM-PUBLIC-REPOSITORY\u003e` with your project name and your `Public` repository name.\n    - Change `\u003cTAG\u003e` to the latest tag. The latest Git tag for a repository can be found next to the commit. If you see multiple tags on a commit, please specify one among those. Alternatively, you can replace the line `ref: refs/tags/\u003cTAG\u003e` to `ref: main` so that it will pick up the latest commit from the `main` branch during build/release.\n\n11. Save your changes.\n\n12. Create parameter files based off the provided `parameters.env.json` by using the [Add-ParamFiles.ps1](https://github.com/microsoftgraph/group-membership-management/blob/main/Scripts/Add-ParamFiles.ps1) script:\n    * From your PowerShell command prompt navigate to the Scripts folder of your `Public` repo and type these commands.\n\n            1. . ./Add-ParamFiles.ps1\n            2. Add-ParamFiles   -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                -SourceEnvironmentAbbreviation \"\u003cSourceEnvironmentAbbreviation\u003e\" `\n                                -RepoPath \"\u003cRepoPath\u003e\"\n        * Use `\"env\"` for `\u003cSourceEnvironmentAbbreviation\u003e` and the absolute path to your private repository for `\u003cRepoPath\u003e`.\n    * This command will go into each of the `parameters` folders and copy and rename the `parameters.env.json` file to `parameters.\u003cEnvironmentAbbreviation\u003e.json`. These new parameter files will be used to by the ARM templates to deploy the resources of the new environment.\n    * You may create an AAD Group and provide the values for sqlAdministratorsGroupId and sqlAdministratorsGroupName in [data/parameters](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/Infrastructure/data/parameters) and [data/private/parameters](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/Infrastructure/data/private/parameters/parameters.env.json) files.\n    * You also want to provide values for `branch` and `repositoryUrl` in [your UI param file](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/Service/GroupMembershipManagement/Hosts/UI/Infrastructure/compute/parameters). You can provide \"\" for `customDomainName` if you have not set up a custom domain.\n     * You also want to replace values for `\u003ctenant-id\u003e`, `\u003csubscription-id\u003e`, `\u003cdata-resource-group-name\u003e` and `\u003cdata-key-vault-name\u003e` in SqlMembershipObtainer/Infrastructure/compute/param file.\n\n### To remove a GMM environment:\n\n1. Delete your environment from [vsts-cicd.yml](https://github.com/microsoftgraph/group-membership-management-tenant/blob/main/vsts-cicd.yml) and save your changes. You might need to update any templates that had a dependency on the deleted template. For instance `dependsOn` and `condition` settings.\n\n2. Use the [Remove-ParamFiles.ps1](https://github.com/microsoftgraph/group-membership-management/blob/main/Scripts/Remove-ParamFiles.ps1) script to remove the parameter files of the given environment:\n    * From your PowerShell command prompt navigate to the Scripts folder of your `Public` repo and type these commands.\n\n            1. . ./Remove-ParamFiles.ps1\n            2. Remove-ParamFiles    -TargetEnvironmentAbbreviation \"\u003cTargetEnvironmentAbbreviation\u003e\" `\n                                    -RepoPath \"\u003cRepoPath\u003e\"\n        * Use the `\u003cEnvironmentAbbreviation\u003e` of the environment you want to remove for `\u003cTargetEnvironmentAbbreviation\u003e` and the path to your private repository for `\u003cRepoPath\u003e`.\n\n## Create a Service Connection\n\nIn order to deploy GMM resources through a pipeline, we need to create a [Service Connection](https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops\u0026tabs=yaml) and grant permissions to it.\n\nThe following PowerShell scripts create a Service Principal and set up a Service Connection for your environment:\n\n1.  Set-ServicePrincipal.ps1\n\n    This script will create a new service principal for your environment.\n\n    From your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo and type these commands.\n\n        1. . ./Set-ServicePrincipal.ps1\n        2. Set-ServicePrincipal -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\"  `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -Verbose\n\n    Follow the instructions on the screen.\n\n    Locate the service connection name on the screen. It follows this naming convention: `\u003cSolutionAbbreviation\u003e`-serviceconnection-`\u003cEnvironmentAbbreviation\u003e`.\n\n2.  Set-ServicePrincipalManagedIdentityRoles.ps1\n\n    This script will grant the service principal `Contributor` role over all resource groups for GMM. This script must be run by someone with the \u003cb\u003eOwner role on the subscription.\u003c/b\u003e\n\n    From your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo and type these commands.\n\n        1. . ./Set-ServicePrincipalManagedIdentityRoles.ps1\n        2. Set-ServicePrincipalManagedIdentityRoles -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\"  `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -Verbose\n\n3. Set-ServiceConnection.ps1\n\n    This script sets up the service connection for your environment. You must be an owner of the the service principal created in step 1 to run this script.\n\n    From your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo, run these commands, and follow the instructions on the screen:\n\n        1. . ./Set-ServiceConnection.ps1\n        2. Set-ServiceConnection -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\"  `\n                                                -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                                                -OrganizationName \"\u003cOrganizationName\u003e\" `\n                                                -ProjectName \"\u003cProjectName\u003e\" `\n                                                -Clean $true `\n                                                -Verbose\n\n    Where:\n    *  `\u003cOrganizationName\u003e` - This is the name of your organization used in Azure DevOps.\n    *  `\u003cProjectName\u003e` - This is the name of the project in Azure DevOps we just created in a previous step.\n\n4. Give service connection access to the keyvaults\n\n    Go to your `\u003cSolutionAbbreviation\u003e`-prereqs-`\u003cEnvironmentAbbreviation\u003e` keyvault \u003e Click on 'Access policies' \u003e Click on Create \u003e Select Get, List, and Set secrets permissions and then add your `\u003cSolutionAbbreviation\u003e`-serviceconnection-`\u003cEnvironmentAbbreviation\u003e` as the principal.\n\n5. In addition to the Contributor role, the service connection needs addional actions no included in the Contributor role. To grant these permissions, follow the steps:\n   From your `PowerShell 7.x` command prompt navigate to the `Scripts` folder of your `Public` repo, run these commands:\n\n        1. . ./Set-CustomRole.ps1\n        2. Set-CustomRole -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\"  `\n                          -EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n                          -Verbose\n\n    The script will create a custom role named \"GMM Custom Role\" and assign it to the service principal. This role has the following permissions:\n    - Microsoft.Authorization/locks/*\n\n## Setup the Notifier\n\nPlease follow the instructions in the [Notifier Setup](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/NotifierSetup.md) documentation.\n\n## Create an Azure DevOps environment\n\nAn environment is necessary to manage deployment approvals. To create the environment:\n\n1. On Azure DevOps left menu locate Pipelines menu click on `Environments`.\n2. Click on `New environments` or `Create environment` button depending on which one is presented to you.\n3. Fill in the `Name` field following this naming convention: \u003cSolutionAbbreviation\u003e-\u003cEnvironmentAbbreviation\u003e\n4. Add a description (optional).\n5. Click on `Create` button.\n6. Once created, locate and click on your environment.\n7. Click on `More Actions` button. It's displayed as a vertical ellipsis (three vertical dots).\n8. Click on `Approvals and checks` option.\n9. Click on `Add check` button.\n10. Select `Approvals` option then click on `Next` button.\n11. Add the user(s) or group(s) that will approve the deployment.\n12. Click on `Create` button.\n\n## Create an Azure DevOps pipeline\n\nIn Azure DevOps, we need to create a pipeline that will create your resources and deploy your code.\n\n-   See [Create your first pipeline](https://docs.microsoft.com/en-us/azure/devops/pipelines/create-first-pipeline?view=azure-devops\u0026tabs=java%2Cyaml%2Cbrowser%2Ctfs-2018-2) documentation for more information:\n    1. On Azure DevOps left menu locate and click on Pipelines.\n    2. Click on 'Create Pipeline' or 'New Pipeline' depending on which one is presented to you.\n    3. Select Azure Repos Git as your code location.\n    4. Select your `Private` repo.\n    5. From the list of options select 'Existing Azure Pipelines YAML file'.\n    6. Select your branch.\n    7. Select '/vsts-cicd.yml' in the Path field.\n    8. Click continue.\n    9. You will be presented with the \"Review your pipeline YAML\" screen.\n    10. Locate and click on the \"Variables\" button on the top right side of your screen.\n    11. Create the following variables:\n\n        * `location` - This is the location where the Azure resources are going to be created. See [Resource Locations](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/resource-location?tabs=azure-powershell).\n\n        * `subscriptionId_prod` - This is the subscription Id of your production environment.\n\n        * `subscriptionId_nonprod` - This is the subscription Id of your non-production environment.\n\n        * `tenantId` - This is the Azure Active Directory tenant Id, where GMM Azure resources were created.\n\n        * `SolutionAbbreviation` - This is the abbreviation of your solution.\n\n    12. Follow [Update Build/Release Pipeline variables](https://github.com/microsoftgraph/group-membership-management/blob/main/UI/Documentation/UISetup.md) to create additional variables and deploy WebAPI \u0026 UI.\n    13. Once all variables have been created click on the \"Save\" button.\n    14. Run your pipeline.\n\n    When running the pipeline for the first time you might be prompted to authorize resources, click on \"Authorize resources\" buttons.\n\n    *Points to remember while running the pipeline:*\n        * *If you see an error task `mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks` is missing, install it from [here](https://marketplace.visualstudio.com/items?itemName=mspremier.BuildQualityChecks\u0026ssr=false\u0026referrer=https%3A%2F%2Fapp.vssps.visualstudio.com%2F#overview)*\n        * *If you see an error `no hosted parallelism has been purchased or granted`, please fill out [this](https://aka.ms/azpipelines-parallelism-request) form to request a free parallelism grant. Please note that it could take 2-3 business days to approve the request.*\n        * *If you see an error `MissingSubscriptionRegistration`, go to Subscription -\u003e Resource Providers and register the missing provider*\n        * *If you see deployment failing at RunJobScheduler, run Set-PostDeploymentRoles (next step) and rerun the release*\n\n    15. If you want to set up AzureUserReader Durable Function, please follow the instruction here: [AzureUserReader](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Hosts/AzureUserReader/Documentation/README.md). Otherwise, you can remove this function from vsts-cicd.yml file\n\n## Post-Deployment tasks\n\nOnce the pipeline has completed building and deploying GMM code and resources to your Azure resource groups, we need to make some final configuration changes.\n\n### Grant functions and web api access to required resources:\n\nThe following script:\n1. Grants all functions access to App Configuration.\n2. Grants GroupMembershipObtainer, MembershipAggregator, and GraphUpdater functions access to storage account.\n\nFrom your `PowerShell 7.x` command prompt navigate to the `Scripts/PostDeployment` folder of your `Public` repo, run these commands, and follow the instructions on the screen:\n\n        1. . ./Set-PostDeploymentRoles.ps1\n        2. Set-PostDeploymentRoles  -SolutionAbbreviation \"\u003csolutionAbbreviation\u003e\" `\n                                    -EnvironmentAbbreviation \"\u003cenvironmentAbbreviation\u003e\" `\n                                    -Verbose\n\nWhere:\n* `\u003cSolutionAbbreviation\u003e` and `\u003cEnvironmentAbbreviation\u003e` are as before.\n\n### Grant the WebAPI access to SQL Server Database\n\nWebAPI will access the database using its system identity to authenticate with the database to prevent the use of credentials.\n\nOnce the WebAPI is deployed (`\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi`) and has been created we need to grant it access to the SQL Server DB.\n\nServer name follows this naming convention `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e` and `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e-r` for the replica server.\nDatabase name follows this naming convention `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e` and `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e-r` for the replica database.\n\n1. Connect to your SQL Server Database using Sql Server Management Studio (SSMS) or Azure Data Studio.\n- Server name : `\u003cserver-name\u003e.database.windows.net`\n- User name: Use your Azure account.\n- Authentication: Azure Active Directory - Universal with MFA\n- Database name: `\u003cdatabase-name\u003e`\n\n2. Run these SQL command\n\n- This script needs to run only once per database.\n- Make sure you are connected to right database. Sometimes SSMS will default to the master database.\n\n```\nIF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi')\nBEGIN\n CREATE USER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi] FROM EXTERNAL PROVIDER;\n ALTER ROLE db_datareader ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi];\n ALTER ROLE db_datawriter ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi];\n ALTER ROLE db_ddladmin ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi];\nEND\n```\n\nVerify it ran successfully by running:\n```\nSELECT * FROM sys.database_principals WHERE name = N'\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-webapi'\n```\nYou should see one record for your webapi app.\nRepeat the steps for both databases.\n\n### Grant the Azure Functions access to SQL Server Database\n\nAzure Functions connect to SQL server via MSI (System Identity), once the database is created as part of the deployment we need to grant access to the functions to read and write to the database.\n\nFor these functions:\nJobTrigger, GroupMembershipObtainer, SqlMembershipObtainer, AzureMaintenance, PlaceMembershipObtainer*, AzureUserReader, GraphUpdater, JobScheduler, MembershipAggregator, NonProdService, Notifier, GroupOwnershipObtainer, TeamsChannelMembershipObtainer, TeamsChannelUpdater, DestinationAttributesUpdater\n\nRun this commands, in your SQL Server database where the jobs table was created:\n\n    --Production slot\n    CREATE USER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e] FROM EXTERNAL PROVIDER\n    ALTER ROLE db_datareader ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e] -- gives permission to read to database\n    ALTER ROLE db_datawriter ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e] -- gives permission to write to database\n\n    --Staging slot\n    CREATE USER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e/slots/staging] FROM EXTERNAL PROVIDER\n    ALTER ROLE db_datareader ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e/slots/staging] -- gives permission to read to database\n    ALTER ROLE db_datawriter ADD MEMBER [\u003cSolutionAbbreviation\u003e-compute-\u003cEnvironmentAbbreviation\u003e-\u003cfunction\u003e/slots/staging] -- gives permission to write to database\n\nRepeat the steps above for each function.\n\n*Points to remember:*\n        * *Try logging into SQL database via Azure Portal by adding the IP address*\n\n## Grant the Service Connection access to SQL Server Database\n\nYour service connection needs MSI access to the SQL Server DB so it can deploy the DACPAC file.\n\nOnce the SQL server and databases are created as part of the deployment we will need to run these SQL statements before we can deploy the DACPAC file and run scripts on the jobs database.\n\nSyncJobs DB\n- Server name follows this naming convention `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e`.\n- Database name follows this naming convention `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e`.\n\n1. Connect to your SQL Server Database using Sql Server Management Studio (SSMS) or Azure Data Studio.\n- Server name : `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e.database.windows.net`\n- User name: Your account.\n- Authentication: Azure Active Directory - Universal with MFA\n- Database name: `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e`\n\n2. Run these SQL commands\n\nSyncJobs DB\n\n- This script needs to run only once.\n- Make sure you are connected to SyncJobs database: `\u003cSolutionAbbreviation\u003e-data-\u003cEnvironmentAbbreviation\u003e`.\n\n```\nIF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'\u003cSolutionAbbreviation\u003e-serviceconnection-\u003cEnvironmentAbbreviation\u003e')\nBEGIN\n    CREATE USER [\u003cSolutionAbbreviation\u003e-serviceconnection-\u003cEnvironmentAbbreviation\u003e] FROM EXTERNAL PROVIDER\n    ALTER ROLE db_datareader ADD MEMBER [\u003cSolutionAbbreviation\u003e-serviceconnection-\u003cEnvironmentAbbreviation\u003e] -- gives permission to read to database\n    ALTER ROLE db_datawriter ADD MEMBER [\u003cSolutionAbbreviation\u003e-serviceconnection-\u003cEnvironmentAbbreviation\u003e] -- gives permission to write to database\nEND\n```\n\nVerify it ran successfully by running:\n```\nSELECT * FROM sys.database_principals WHERE name = N'\u003cSolutionAbbreviation\u003e-serviceconnection-\u003cEnvironmentAbbreviation\u003e'\n```\nYou should see one record for your service connection resource.\n\n### Create the jobs table:\n\nThe jobs table contains all the sync jobs that GMM will perform.\n\n#### Create jobs table in SQL database\n\n* Go to https://`\u003csolutionAbbreviation\u003e`-compute-`\u003cenvironmentAbbreviation\u003e`-webapi.azurewebsites.net/swagger/index.html\n* Hit the endpoint `admin/databaseMigration`. This will create the jobs table in `\u003csolutionAbbreviation\u003e`-data-`\u003cenvironmentAbbreviation\u003e` database\n    * *Note: To hit the endpoint: 1- Add `ASPNETCORE_ENVIRONMENT: development` in `\u003csolutionAbbreviation\u003e`-compute-`\u003cenvironmentAbbreviation\u003e`-webapi, 2-update the value of config setting `ConnectionStrings:JobsContext` in `\u003csolutionAbbreviation\u003e`-compute-`\u003cenvironmentAbbreviation\u003e`-webapi with the value of `jobsMSIConnectionString` which you can find in your data key vault*\n* Run [this script](/Scripts/New-GmmGroupMembershipSyncJob.ps1) to add a job to sql database\n\n#### Create notifications tables in storage account:\n\nOpen your jobs storage account on Azure Explorer:\n\n* Go to the [Azure Portal](https://ms.portal.azure.com/#home)\n* Go to `Subscription` and select the subscription used for GMM\n* Go to `Resource Groups` and select `gmm-data-\u003cEnvironmentAbbreviation\u003e`\n* Under `Resources`, select the `jobs\u003cEnvironmentAbbreviation\u003e\u003cID\u003e` storage account, and open it on Azure Explorer\n* Create a new table called `notifications`\n    * Go to the table and click on `Import` at the top bar\n    * Import the `thresholdNotificationSample.csv` file located under the `Documentation` folder of your `Public` repo\n    * IMPORTANT: Remove the sample entry from the table before proceeding\n\n#### * Please follow Post Deployment tasks on SqlMembershipObtainer/Documentation/README.md\n\n## (Optional) Set up a production environment\n\nTo create a production environment:\n\n1. Using the same Azure DevOps repositories and pipeline created on the first iteration, follow the steps of [GMM Setup](#gmm-setup) to create another environment.\n2. Use the following `yaml/deploy-pipeline.yml` template for step 2 of [Adding a new GMM environment](#adding-a-new-gmm-environment):\n\n        - template: yaml/deploy-pipeline.yml\n        parameters:\n            solutionAbbreviation: '$(SolutionAbbreviation)'\n            environmentAbbreviation: '\u003cProdEnvironmentAbbreviation\u003e'\n            tenantId: $(tenantId)\n            subscriptionId: $(subscriptionId_prod)\n            location: $(location)\n            serviceConnection: '$(SolutionAbbreviation)-serviceconnection-\u003cProdEnvironmentAbbreviation\u003e'\n            dependsOn:\n            - Build_Common\n            - Build_CopyParameters\n            - NonProd_\u003cNonProdEnvironmentAbbreviation\u003e\n            stageName: 'Prod_production'\n            functionApps:\n            - function:\n            name: 'GraphUpdater'\n            - function:\n            name: 'MembershipAggregator'\n            dependsOn:\n            - 'GraphUpdater'\n            - function:\n            name: 'GroupMembershipObtainer'\n            dependsOn:\n            - 'MembershipAggregator'\n            - function:\n            name: 'AzureMaintenance'\n            - function:\n            name: 'JobScheduler'\n            - function:\n            name: 'Notifier'\n            - function:\n            name: 'JobTrigger'\n            dependsOn:\n            - 'GroupMembershipObtainer'\n            condition: |\n            and(\n                succeeded('Build_Common'),\n                succeeded('Build_CopyParameters'),\n                succeeded('NonProd_\u003cNonProdEnvironmentAbbreviation\u003e'),\n                in(variables['Build.SourceBranch'], 'refs/heads/master', 'refs/heads/main'),\n                in(variables['Build.Reason'], 'IndividualCI', 'Manual')\n            )\n\n    Where:\n\n    * `\u003cProdEnvironmentAbbreviation\u003e` - The new environment being created or your production environment.\n    * `\u003cNonProdEnvironmentAbbreviation\u003e` - The previously created environment or your non-production environment.\n\n    Note: if you notice the condition section, it states that your non-production environment must deploy successfully for your production environment to deploy.\n\n3. Add the following variables to your pipeline as you did in step 11 of [Creating a Pipeline](#create-an-azure-devops-pipeline):\n\n    * `subscriptionId_prod` - This is the subscription Id of your production environment.\n    * `SolutionAbbreviation` - This is the abbreviation of your solution.\n\n\n\n# Using GMM\n\n## Creating synchronization jobs for source groups\n\nOnce GMM is up and running you might want to start creating synchronization jobs for your groups.\n\n### Adding Graph application as an owner to GMM managed destination group\n\nThe previously created `\u003csolutionAbbreviation\u003e-Graph-\u003cenvironmentAbbreviation\u003e` application must be added as an owner to any destination group that will be managed by GMM in order for GMM to have the right permissions to update the group.\n\nTo add the application as an owner of a group, follow the next steps:\n1. In the Azure Portal navigate to your `Azure Active Directory`. If you don't see it on your screen, you can use the top search bar to locate it.\n2. Navigate to the `Groups` blade on the left menu.\n3. Locate and open the group you would like to use.\n4. Navigate to `Owners` on the left menu.\n5. Click on `Add owners` and add your `\u003csolutionAbbreviation\u003e-Graph-\u003cenvironmentAbbreviation\u003e` application.\n\n### Adding synchronization jobs to the jobs table\n\nA synchronization job must have the following properties populated:\n\n- Requestor\n- Destination\n- TargetOfficeGroupId\n- Status\n- LastRunTime\n- LastSuccessfulRunTime\n- LastSuccessfulStartTime\n- Period\n- Query\n- StartDate\n- ThresholdPercentageForAdditions\n- ThresholdPercentageForRemovals\n- ThresholdViolations\n- IsDryRunEnabled\n- DryRunTimeStamp\n\nSee [syncJobs properties](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/syncJobsProperties.md) for more information.\n\n\nA PowerShell script [New-GmmGroupMembershipSyncJob.ps1](/Scripts/New-GmmGroupMembershipSyncJob.ps1) is provided to help you create the synchronization jobs.\n\nThe Query field requires a JSON object that must follow this format:\n\n```\n[\n    {\n        \"type\": \"GroupMembership\",\n        \"source\": \"\u003cguid-group-objet-id-1\u003e\"\n    },\n    {\n        \"type\": \"GroupMembership\",\n        \"source\": \"\u003cguid-group-objet-id-2\u003e\"\n    },\n    {\n        \"type\": \"GroupMembership\",\n        \"source\": \"\u003cguid-group-objet-id-n\u003e\"\n    }\n]\n```\nFrom your `PowerShell 7.x` command prompt navigate to [New-GmmGroupMembershipSyncJob.ps1](/Scripts/New-GmmGroupMembershipSyncJob.ps1), run these commands, and follow the instructions on the screen:\n\n    1. . ./New-GmmGroupMembershipSyncJob.ps1\n    2. New-GmmGroupMembershipSyncJob\t-SubscriptionName \"\u003cSubscriptionName\u003e\" `\n                            -SolutionAbbreviation \"\u003cSolutionAbbreviation\u003e\" `\n\t\t\t\t\t\t\t-EnvironmentAbbreviation \"\u003cEnvironmentAbbreviation\u003e\" `\n\t\t\t\t\t\t\t-Requestor \"\u003cRequestorEmailAddress\u003e\" `\n\t\t\t\t\t\t\t-TargetOfficeGroupId \"\u003cDestinationGroupObjectId\u003e\" `\n                            -Destination \"\u003cJSON string\u003e\" `\n\t\t\t\t\t\t\t-Query \"\u003cJSON string\u003e\" `\n\t\t\t\t\t\t\t-Period \u003cin hours, integer only\u003e `\n\t\t\t\t\t\t\t-ThresholdPercentageForAdditions \u003cinteger only\u003e `\n\t\t\t\t\t\t\t-ThresholdPercentageForRemovals \u003cinteger only\u003e `\n\t\t\t\t\t\t\t-Verbose\n\nYou can also add, edit or delete synchronization jobs via `SyncJobs` SQL table`.\n\n### Setting up the NonProdService function\n\nThe NonProdService function will create and populate test groups in the tenant for use in GMM integration testing (or for sources in your own manual tests as well). See [Setting up NonProdService function](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Hosts/NonProdService/Documentation/README.md).\n### Dry Run Settings\n\nDry run settings are present in GMM to provide users the ability to test new changes without affecting the group membership. This configuration is present in the application configuration table.\nIf you would like to have the default setting to be false, then please update the settings in the app configuration to false for the GraphUpdater and GroupMembershipObtainer.\n\nThere are 3 Dry Run flags in GMM. If any of these Dry run flags are set, the sync will be completed but destination membership will not be affected.\n1. IsDryRunEnabled: This is a property that is set on an individual sync. Setting this to true will run this sync in dry run.\n2. GroupMembershipObtainer:IsDryRunEnabled: This is a property that is set in the app configuration table. Setting this to true will run all Security Group syncs in dry run.\n3. IsMembershipAggregatorDryRunEnabled: This is a property that is set in the app configuration table. Setting this to true will run all syncs in dry run.\n\n# Setting GroupOwnershipObtainer function\n[GroupOwnershipObtainer function](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Hosts/GroupOwnershipObtainer/Documentation/GroupOwnershipObtainer.md)\n\n# Setting GMM in a demo tenant\n\nIn the event that you are setting up GMM in a demo tenant refer to [Setting GMM in a demo tenant](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/DemoTenant.md) for additional guidance.\n\n# Setting up WebAPI and GMM UI\n\nPlease refer to [Create React App](https://github.com/microsoftgraph/group-membership-management/blob/main/UI/web-app/README.md) for additional guidance.\nPlease refer to [WebAPI](https://github.com/microsoftgraph/group-membership-management/blob/main/Service/GroupMembershipManagement/Hosts/WebApi/Documentation/WebApiSetup.md) for additional guidance.\nPlease refer to [GMM UI](https://github.com/microsoftgraph/group-membership-management/blob/main/UI/Documentation/UISetup.md) for additional guidance.\n\n# Steps to debug and troubleshoot a failing sync\n\nTo troubleshoot any issues that might occur we can use Log Analytics and Application Insights.\n\n1. Find Logs in the Log analytics workspace following the instructions [here](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/FindLogEntriesInLogAnalyticsForASync.md).\n2. Find failures and exceptions with Application Insights [here](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/TroubleshootWithApplicationInsights.md).\n3. In case jobs appear to be stuck in progress without any visible exceptions, it is recommended to restart GMM. This can be done by running this script [Restart-GMM.ps1](https://github.com/microsoftgraph/group-membership-management/blob/main/Scripts/Restart-GMM.ps1).\n\n# Tearing down your GMM environment.\nIn the event that you want to reset the GMM environment refer to [Delete GMM environment](https://github.com/microsoftgraph/group-membership-management/blob/main/Documentation/DeleteEnvironment.md) for additional guidance.\n\n# Breaking changes\nSee [Breaking changes](https://github.com/microsoftgraph/group-membership-management/blob/main/breaking_changes.md)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoftgraph%2Fgroup-membership-management-tenant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmicrosoftgraph%2Fgroup-membership-management-tenant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmicrosoftgraph%2Fgroup-membership-management-tenant/lists"}