Using Parameters When Creating Tenant Configuration Management Monitors

When you create a new monitor in Tenant Configuration Management, or TCM, you provide a baseline that describes the desired configuration state of your tenant. The simplest examples usually hard-code every value directly in the baseline. That works for a quick test, but it does not scale well when the same baseline needs to be reused across multiple tenants, environments, or naming conventions.

This is where parameters become useful. Parameters let you keep tenant-specific values outside of the reusable baseline definition. Instead of creating one baseline per tenant just because the tenant domain name, tenant identifier, or naming prefix changes, you define the variable value once and reference it from the resources in the baseline.

The two places where parameters appear

A parameterized monitor payload has two related sections:

  • baseline.parameters: declares the parameters the baseline expects.
  • parameters: provides the concrete values to use when the monitor is created.

The declaration belongs inside the baseline because it describes the shape of the reusable template. The values belong at the monitor level because they describe the tenant-specific input for this monitor instance.

{
  "displayName": "Monitor for Contoso",
  "description": "Monitor created from a parameterized baseline",
  "baseline": {
    "displayName": "Reusable Exchange baseline",
    "description": "Baseline that uses tenant-specific parameters",
    "parameters": [
      {
        "displayName": "FQDN",
        "description": "The fully qualified domain name of the tenant",
        "parameterType": "String"
      }
    ],
    "resources": []
  },
  "parameters": {
    "FQDN": "contoso.onmicrosoft.com"
  }
}

By itself, the example above does not yet use the parameter. It only declares and supplies it. The important part is how the resource properties reference that value.

Referencing a parameter in a resource property

Inside a resource property, a parameter can be referenced with the parameters() expression. For example, if a property should be set to the tenant's fully qualified domain name, you can write:

{
  "Identity": "[parameters('FQDN')]"
}

That expression tells TCM to resolve the value of FQDN from the monitor-level parameters object when the monitor evaluates the baseline.

Using concat to build tenant-specific values

In many cases, the parameter is only part of the desired value. For example, an email address might always start with the same local part, while the domain changes per tenant. For that scenario, use the concat function to combine literal strings and parameter values.

{
  "PrimarySmtpAddress": "[concat('testSharedMailbox@', parameters('FQDN'))]"
}

If the monitor-level parameter value is contoso.onmicrosoft.com, the resolved value becomes:

testSharedMailbox@contoso.onmicrosoft.com

You can use the same pattern anywhere a baseline property needs a value that is partially static and partially tenant-specific.

Complete monitor creation example

The following example creates a monitor with a parameterized Exchange shared mailbox resource. The baseline declares the FQDN parameter, uses it to build the shared mailbox primary SMTP address and an additional email address, and then supplies the tenant-specific value when creating the monitor.

POST https://graph.microsoft.com/beta/admin/configurationManagement/configurationMonitors/
{
  "displayName": "Monitor for Exchange shared mailbox",
  "description": "Monitor created from a reusable parameterized baseline",
  "baseline": {
    "displayName": "Exchange shared mailbox baseline",
    "description": "Baseline with a tenant-specific domain parameter",
    "parameters": [
      {
        "displayName": "FQDN",
        "description": "The fully qualified domain name of the tenant",
        "parameterType": "String"
      }
    ],
    "resources": [
      {
        "displayName": "Shared mailbox resource",
        "resourceType": "microsoft.exchange.sharedmailbox",
        "properties": {
          "DisplayName": "TestSharedMailbox",
          "Alias": "testSharedMailbox",
          "Identity": "TestSharedMailbox",
          "Ensure": "Present",
          "PrimarySmtpAddress": "[concat('testSharedMailbox@', parameters('FQDN'))]",
          "EmailAddresses": [
            "[concat('smtp:testSharedMailbox@', parameters('FQDN'))]"
          ]
        }
      }
    ]
  },
  "parameters": {
    "FQDN": "contoso.onmicrosoft.com"
  }
}

The reusable part of this payload is the baseline. To create a similar monitor for another tenant, keep the same baseline and change the value in the monitor-level parameters object.

{
  "parameters": {
    "FQDN": "fabrikam.onmicrosoft.com"
  }
}

With that value, the PrimarySmtpAddress expression resolves to testSharedMailbox@fabrikam.onmicrosoft.com.

Using more than one parameter

A baseline can declare multiple parameters. This is useful when a value needs to combine more than one tenant-specific input, such as a naming prefix and a tenant domain.

{
  "baseline": {
    "parameters": [
      {
        "displayName": "MailboxPrefix",
        "description": "The prefix to use for generated mailbox names",
        "parameterType": "String"
      },
      {
        "displayName": "FQDN",
        "description": "The fully qualified domain name of the tenant",
        "parameterType": "String"
      }
    ],
    "resources": [
      {
        "displayName": "Shared mailbox resource",
        "resourceType": "microsoft.exchange.sharedmailbox",
        "properties": {
          "Alias": "[concat(parameters('MailboxPrefix'), 'SharedMailbox')]",
          "PrimarySmtpAddress": "[concat(parameters('MailboxPrefix'), 'SharedMailbox@', parameters('FQDN'))]"
        }
      }
    ]
  },
  "parameters": {
    "MailboxPrefix": "finance",
    "FQDN": "contoso.onmicrosoft.com"
  }
}

In this example, the primary SMTP address resolves to financeSharedMailbox@contoso.onmicrosoft.com.

Common mistakes to avoid

  • Declaring parameters that are never used: keep the baseline.parameters list limited to values that are actually referenced by the baseline.
  • Using a parameter without providing a value: every referenced parameter needs a corresponding entry in the monitor-level parameters object.
  • Hard-coding tenant-specific values in reusable baselines: if the value changes between tenants, make it a parameter.
  • Forgetting that parameters are strings: when using concat, make sure the resulting value matches the format expected by the target resource property.

When to parameterize a TCM monitor

Parameterization is most valuable when you expect the same baseline to be used more than once. Good candidates include tenant domains, tenant identifiers, environment names, prefixes, notification addresses, and any resource property that follows a predictable naming pattern. If a value is unique to one tenant but the structure of the baseline is reusable, it is usually a good parameter candidate.

The main design goal is to separate what you want to monitor from which tenant-specific values should be used for that monitor instance. That separation makes baselines easier to review, easier to version, and easier to reuse across environments.

Conclusion

Parameters make Tenant Configuration Management monitors much more reusable. Define the expected inputs in baseline.parameters, provide the actual values in the monitor-level parameters object, and use expressions such as parameters() and concat() inside resource properties to build the final values TCM should evaluate.

If you are creating monitors manually, parameters may feel like an extra step. If you are building a repeatable monitoring process across tenants, they quickly become one of the most important parts of the baseline design.