> For the complete documentation index, see [llms.txt](https://docs.realmjoin.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.realmjoin.com/automation/runbooks/runbook-references/org/general/sync-sharedchannel-owners_scheduled.md).

# Sync Sharedchannel Owners Scheduled

### Description

Teams shared channels do not inherit ownership from their parent team. This scheduled runbook closes that gap: for each team named in a mapping, it ensures the members of a mapped security group are owners of the team and of every shared channel the team hosts. The team-name-to-owner-group mapping is maintained centrally as a RealmJoin org setting. The runbook is add-only - existing owners and members are never removed - so newly created shared channels are simply picked up on the next run. It can optionally email a report and/or upload the CSV results as a download link. See the accompanying documentation for the mapping rules and configuration.

### How it works

On each run the runbook:

1. Reads the team-name-to-owner-group mapping from the org setting `SharedChannelOwners.Mapping`.
2. For each entry, looks up the team by its **exact display name**.
3. Expands that entry's owner group to its transitive **user** members (guests are skipped - they cannot belong to a shared channel).
4. Ensures those users are owners of the team and of every **hosted** shared channel of the team.

The runbook is **add-only**: it never demotes or removes existing owners or members. Newly created shared channels are therefore picked up automatically on the next scheduled run, without disturbing anything already in place.

#### Mapping configuration

The mapping lives centrally in the RealmJoin org settings (Runbook Customization → `Settings` → `SharedChannelOwners.Mapping`) so it is maintained once and shared by every schedule. It is a list of `{ TeamName, OwnerGroupId }` objects, where `TeamName` is the **exact team display name** (see the *Notes* section for a ready-to-use example). The hidden `TeamOwnerGroupMapping` parameter is injected from this setting; the runbook accepts it either as a structured array (recommended sub-setting form) or as a JSON string and normalizes both.

#### Team matching

Each mapping entry targets one explicitly named team:

* A team is matched by its **exact display name** (case-insensitive, consistent with Microsoft Graph; surrounding whitespace in the configured name is ignored). Only that team is processed - there is no prefix or wildcard behaviour, so naming an entry `EXT Service A` never affects `EXT Service A Backup` or similar.
* Display names are not guaranteed unique in Entra ID. If several teams share the configured name, the owner group is applied to **all** of them. If no team matches, the entry is reported as *not found* and skipped.

#### Team selection

For every configured `TeamName` the runbook runs a Graph `displayName eq '...'` lookup and keeps only Microsoft 365 groups that are provisioned as a **Team**.

#### What gets changed

* **Team (optional, `IncludeTeamOwners`, default on):** the owner-group users are added as owners and members of the parent M365 group. Team membership is also the technical prerequisite for becoming a shared-channel owner, so this step enables the channel step.
* **Shared channels:** for every hosted shared channel (`membershipType eq 'shared'`), each owner-group user is ensured as a channel **owner** - added directly if absent, or promoted if already a member. If a direct owner-add is rejected (e.g. membership replication lag), the runbook falls back to adding the user as a member first and then promoting.

#### Dry run

Set **`WhatIfMode`** to log what would change without writing anything. In this mode the runbook prints, up front, the teams it would process (with their owner group) and any configured team names that were not found.

#### Reporting (optional, both default off)

* **`SendEmailReport`** sends a RealmJoin-branded email (via `Send-RjReportEmail`) with run statistics and two CSV attachments: a per-team summary and a per-change detail list. The sender is taken from the `RJReport.EmailSender` setting.
* **`CreateDownloadLink`** uploads the same CSVs to a storage account and returns time-limited SAS download links (also embedded into the email when both options are enabled). The target storage account is taken from the `RJReport.StorageAccount.*` settings.

The storage upload authenticates with the Automation account's managed identity; that identity needs the **Storage Blob Data Contributor** RBAC role on the target storage account (this is an Azure RBAC assignment, not a Graph application permission).

#### Scheduling

Designed to run unattended on a schedule. Because configuration is centralized in the org settings and the runbook is add-only and idempotent, a single recurring schedule keeps all mapped teams and their shared channels in sync as people and channels come and go.

### Location

Organization → General → Sync Sharedchannel Owners (Scheduled)

**Full Runbook name**

rjgit-org\_general\_sync-sharedchannel-owners\_scheduled

### Permissions

#### Application permissions

* **Type**: Microsoft Graph
  * Group.ReadWrite.All
  * GroupMember.ReadWrite.All
  * Channel.ReadBasic.All
  * ChannelMember.ReadWrite.All
  * Mail.Send

### Parameters

#### TeamOwnerGroupMapping

Mapping of an exact team display name to an owner security group object id, e.g. \[{ "TeamName": "EXT Service A", "OwnerGroupId": "00000000-0000-0000-0000-000000000000" }]. Hidden parameter, bound to the org Setting "SharedChannelOwners.Mapping". The RealmJoin portal injects that value; the runbook accepts it either as the deserialized object/array (structured sub-settings) or as a JSON string and normalizes both.

| Property      | Value  |
| ------------- | ------ |
| Required      | false  |
| Default Value | \[]    |
| Type          | Object |

#### IncludeTeamOwners

When enabled (default), the owner-group members are also ensured as owners and members of the parent team itself (M365 group owners/members). Team membership is also the prerequisite for channel ownership.

| Property      | Value   |
| ------------- | ------- |
| Required      | false   |
| Default Value | True    |
| Type          | Boolean |

#### WhatIfMode

When enabled, the runbook only logs the changes it would make without writing anything.

| Property      | Value   |
| ------------- | ------- |
| Required      | false   |
| Default Value | False   |
| Type          | Boolean |

#### SendEmailReport

When enabled, a RealmJoin-branded email report is sent via Send-RjReportEmail after the run. The body contains run statistics and two CSV attachments (per-team summary and per-change detail).

| Property      | Value   |
| ------------- | ------- |
| Required      | false   |
| Default Value | False   |
| Type          | Boolean |

#### EmailTo

Recipient email address(es) for the report (comma-separated). Only used when SendEmailReport is enabled.

| Property      | Value  |
| ------------- | ------ |
| Required      | false  |
| Default Value |        |
| Type          | String |

#### EmailFrom

Sender mailbox for the report. Bound to the org Setting "RJReport.EmailSender".

| Property      | Value  |
| ------------- | ------ |
| Required      | false  |
| Default Value |        |
| Type          | String |

#### CreateDownloadLink

When enabled, the CSV report(s) are uploaded to a storage account and a time-limited download link is returned (and included in the email report if that is also enabled). Default off.

| Property      | Value   |
| ------------- | ------- |
| Required      | false   |
| Default Value | False   |
| Type          | Boolean |

#### ContainerName

Storage container used for the upload. Configured per runbook (not a global RJReport setting).

| Property      | Value                 |
| ------------- | --------------------- |
| Required      | false                 |
| Default Value | shared-channel-owners |
| Type          | String                |

#### ResourceGroupName

Resource group that contains the storage account. Bound to "RJReport.StorageAccount.ResourceGroup".

| Property      | Value  |
| ------------- | ------ |
| Required      | false  |
| Default Value |        |
| Type          | String |

#### StorageAccountName

Storage account used for the upload. Bound to "RJReport.StorageAccount.StorageAccountName".

| Property      | Value  |
| ------------- | ------ |
| Required      | false  |
| Default Value |        |
| Type          | String |

#### LinkExpiryDays

Days until the generated download link expires. Bound to "RJReport.StorageAccount.LinkExpiryDays".

| Property      | Value |
| ------------- | ----- |
| Required      | false |
| Default Value | 6     |
| Type          | Int32 |

[Back to Runbook Reference overview](/automation/runbooks/runbook-references.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.realmjoin.com/automation/runbooks/runbook-references/org/general/sync-sharedchannel-owners_scheduled.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
