Repository navigation
Microsoft Teams Certificate Authentication on Linux
o365-connect-tms-cert-linux.ps1 creates and uses file-based X.509 certificates
to connect to Microsoft Teams with application (app-only) authentication from
Linux. It is the Linux counterpart of o365-connect-tms-cert.ps1 and avoids the
Windows certificate store: the script loads an encrypted PFX into memory and
passes the resulting certificate object to Connect-MicrosoftTeams.
The script has two mutually exclusive modes:
- Generate: create a certificate and export an encrypted private-key PFX plus a public DER CER file.
- Connect: load a PFX, validate it, and establish a Teams PowerShell connection in the current PowerShell process.
Generation works without installing PowerShell modules. Optional Entra app provisioning requires Microsoft Graph PowerShell authentication. Connecting requires the Microsoft Teams PowerShell module. The script does not deploy a service, start a persistent process, or save the PFX password.
- Linux and PowerShell 7.4 or later.
- MicrosoftTeams 5.0.0 or later for a Teams connection.
- Microsoft.Graph.Authentication 2.0.0 or later only when using
-ProvisionEntraApp. - Network access to PowerShell Gallery, Microsoft Graph, and Microsoft Teams when installing modules, provisioning, or connecting.
Missing modules are installed for CurrentUser, without sudo, when
interactive installation is allowed. In headless mode, required modules must
already be installed. An already-loaded module that is older than the required
version causes an error; start a fresh pwsh session after installing a newer
version.
Use the Windows counterpart on Windows. After a successful connection, run Teams cmdlets in the same PowerShell session. The connection ends when that process exits.
-GenerateLocalCertificate creates a self-signed RSA-2048 certificate using
SHA-256 and the Digital Signature key usage. The certificate is valid for the
requested number of years, beginning a few minutes before generation to avoid
clock-boundary issues. The private key is exported in an encrypted PFX; the
public certificate is exported as a CER. The output filenames use the
certificate thumbprint.
The PFX password is prompted for unless -GeneratedPfxPassword or
-CertificatePasswordEnvironmentVariable supplies it. Empty passwords are
rejected. Without -ProvisionEntraApp, generation stops after reporting the
certificate paths and thumbprint. This mode is useful when an administrator
will configure an Entra app separately.
Add -ProvisionEntraApp to generation for interactive, one-time setup. The
script signs in to Microsoft Graph using device-code authentication in the
specified tenant. It then creates or reuses a single-tenant application with
the requested display name, uploads the public certificate, configures the
required Microsoft Graph application permissions, creates or reuses the app's
service principal, grants those application roles, assigns the Teams
Administrator directory role at tenant scope, and saves a connection profile.
The administrator signing in must be able to grant the application consent and assign directory roles, such as a Privileged Role Administrator or Global Administrator. The script requests these delegated setup scopes:
Application.ReadWrite.AllAppRoleAssignment.ReadWrite.AllRoleManagement.ReadWrite.Directory
The provisioned app receives these Microsoft Graph application permissions:
Organization.Read.AllUser.Read.AllGroup.ReadWrite.AllAppCatalog.ReadWrite.AllTeamSettings.ReadWrite.AllChannel.Delete.AllChannelSettings.ReadWrite.AllChannelMember.ReadWrite.All
The combination with the tenant-wide Teams Administrator role is powerful. It
allows broad Teams administration and write operations involving groups,
Teams, channels, and the app catalog. Review the permission set and tenant
policy before provisioning. This script does not add
Directory.ReadWrite.All or Skype and Teams Tenant Admin API permissions.
If an application with the same display name already exists, the script reuses it; multiple exact-name matches are an error. Existing application permissions and certificate credentials are retained while the required permissions and generated certificate are added or updated. If Graph does not return enough public-key information to safely retain existing credentials, provisioning stops rather than removing them. Role and permission changes can take time to replicate, so connect from a fresh PowerShell session after provisioning.
-UseCertificateAuth selects a saved profile, resolves its PFX path, prompts
for the PFX password, validates the certificate, imports MicrosoftTeams if
needed, and calls Connect-MicrosoftTeams with the tenant, application ID,
and loaded certificate object. The script leaves the successful Teams
connection open for subsequent commands in that session.
An existing Entra application can be used without provisioning. Supply the
tenant, app ID, and PFX path explicitly. A profile can also be used, with
explicit values overriding corresponding profile values. Windows profiles
that contain only a certificate thumbprint are insufficient on Linux; provide
an exported PFX using -CertificateFilePath.
By default, certificate files are stored in:
$XDG_DATA_HOME/ciaops/tms, or ~/.local/share/ciaops/tms when
XDG_DATA_HOME is unset or not an absolute path.
The JSON profile map is stored in:
$XDG_CONFIG_HOME/ciaops/tms/o365-tms-cert-auth.json, or
~/.config/ciaops/tms/o365-tms-cert-auth.json when XDG_CONFIG_HOME is unset
or not an absolute path.
The profile map can be either a JSON array or an object with a profiles
array. Each saved profile records its name, tenant/organization, app ID,
certificate thumbprint, and PFX path. It never stores the PFX password. When a
profile contains a relative PFX path, the script resolves it relative to the
profile map's directory. Saving a profile replaces an entry with the same app
ID and profile name; other entries are retained.
The script restricts storage directories to owner-only mode 700 and files
to owner read/write mode 600, independent of the user's umask. This includes
custom output and profile-map directories, which must therefore be dedicated
to this script. Symbolic links in storage paths are rejected. The profile map
is written through a temporary file and moved into place while holding a
lock file to avoid concurrent updates colliding.
For an existing PFX, the script rejects symbolic links and any file whose
permissions grant access beyond the owner; set its mode to 600 before
connecting. It imports the private key with an ephemeral key-storage flag,
avoiding persistent installation into a system certificate store.
| Parameter | Purpose |
|---|---|
-GenerateLocalCertificate |
Generate a certificate and encrypted PFX. |
-UseCertificateAuth |
Connect to Teams with a certificate. Exactly one of this and generation must be selected. |
-GeneratedCertSubject |
Certificate common name and default app display name; defaults to O365-TMS-AppAuth. |
-GeneratedCertYearsValid |
Certificate lifetime in years, from 1 through 100; defaults to 2. |
-GeneratedCertOutputPath |
Certificate directory; defaults to the XDG data directory above. |
-GeneratedPfxPassword |
SecureString password for a generated PFX. |
-ProvisionEntraApp |
During generation, interactively configure the Entra app, permissions, role, and profile. Requires -Tenant; cannot be combined with headless mode. |
-AppDisplayName |
Provisioned app display name; defaults to -GeneratedCertSubject. Duplicate exact matches are rejected. |
-SetupClientId |
Optional tenant-approved public client ID used for Graph device-code sign-in. |
-UseCertificateAuth inputs: -Tenant, -AppId, -CertificateFilePath
|
Explicit connection values. All three are needed when not resolved from a profile. -Organization is an alias for -Tenant. |
-ProfileName |
Name of the saved profile to select. A unique match is required after applying any tenant filter. |
-CertificateMapPath |
Path to the JSON profile map. Defaults to the XDG config path above. |
-CertificatePassword |
SecureString password for the PFX used during connection. |
-CertificatePasswordEnvironmentVariable |
Process environment variable name from which to read a PFX password when the relevant SecureString parameter is omitted. |
-CertificateThumbprint |
Optional expected thumbprint; also read from a profile and compared with the imported PFX. |
-Headless / -noprompt
|
Disable prompts. Requires supplied passwords and preinstalled modules; skips the module update check and is not allowed for provisioning. |
-noupdate |
Skip the MicrosoftTeams Gallery version check. |
-enableLog |
Write a transcript under the private data directory. Provisioning logs may contain short-lived device codes. |
Use Get-Help ./o365-connect-tms-cert-linux.ps1 -Full for PowerShell's
parameter help.
./o365-connect-tms-cert-linux.ps1 -GenerateLocalCertificate -ProvisionEntraApp -Tenant contoso.onmicrosoft.comThe script prompts for a PFX password, then starts device-code sign-in for an administrator. The resulting profile defaults to the app display name.
./o365-connect-tms-cert-linux.ps1 -UseCertificateAuth -ProfileName O365-TMS-AppAuth
Get-CsTenantThe password is prompted for by default. Run the Teams command in the same PowerShell session as the connection.
./o365-connect-tms-cert-linux.ps1 `
-UseCertificateAuth `
-Tenant contoso.onmicrosoft.com `
-AppId 11111111-2222-3333-4444-555555555555 `
-CertificateFilePath /home/admin/auth.pfx `
-CertificatePassword $vaultPassword `
-noupdateEnsure the PFX is owner-only (chmod 600) and pass a SecureString obtained
from a secret vault where possible.
./o365-connect-tms-cert-linux.ps1 `
-UseCertificateAuth `
-ProfileName O365-TMS-AppAuth `
-Headless `
-noupdate `
-CertificatePasswordEnvironmentVariable TMS_PFX_PASSWORDThe environment variable must be populated by the scheduler or secret manager before starting the script. The script reads it from the current process, converts it to a SecureString in memory, and does not print or save the password. Other processes running as the same user may be able to access process environment values, so a vault-supplied SecureString is preferable.
Before importing MicrosoftTeams, the script:
- Requires a tenant, app ID, and PFX path, resolved either explicitly or from exactly one matching profile.
- Requires an owner-only, non-symbolic-link PFX and a nonempty password.
- Confirms the PFX contains a private key.
- Compares the certificate thumbprint with the optional/profile expectation.
- Checks that the certificate is currently valid and warns if it expires in fewer than 30 days.
It then imports MicrosoftTeams version 5.0.0 or later and connects with
Connect-MicrosoftTeams -TenantId ... -ApplicationId ... -Certificate ....
If connection fails, the imported certificate object is disposed. On success,
it is retained for the live Teams session because the module may need it for
token renewal.
-
-Headless(alias-noprompt) disables prompts. Supply the relevant password by SecureString or environment-variable name, and install required modules in advance. Headless mode also skips the Teams Gallery update check. - Without
-noupdate, an interactive connection checks the Gallery for a newer MicrosoftTeams version when an acceptable version is already installed. A failed update check is a warning; accepting an available update installs it for the current user. -
-enableLogcreates a transcript in the private data directory. Treat the transcript as sensitive: during provisioning it may capture a temporary device code. Protect and remove logs according to local policy. - Keep PFX files and passwords separately protected. The PFX is encrypted, but its file permissions and password handling still matter.
- The script does not disconnect Teams after a successful connection. Close the PowerShell process to end that session.
Application authentication is not supported by every Teams PowerShell cmdlet. Administrative Unit scenarios may need permissions not granted by this script. Consult Microsoft's Teams PowerShell application authentication documentation for current cmdlet support and requirements.
This script is provided as-is, without warranty. Review the requested permissions and certificate lifecycle before using it in production.