Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,5 @@ jobs:
tag="epp-packages-${RUN_NUMBER}-${RUN_ATTEMPT}"
gh release create "$tag" artifacts/*.zip artifacts/SHA256SUMS.txt \
--target "$COMMIT_SHA" --title "EPP Function ZIPs ${RUN_NUMBER}.${RUN_ATTEMPT}" \
--latest \
--notes "Packaged and tested from commit ${COMMIT_SHA}. Download one language ZIP and verify it against SHA256SUMS.txt. JavaScript includes production dependencies. .NET contains project and C# source only: extract and build/publish the project before deployment. Python is a source ZIP requiring Azure remote build on Linux. Neither source ZIP is a direct run-from-package artifact. Configure runtime, app settings, Key Vault access, and Easy Auth separately. No cloud deployment or live provider verification is performed."
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ Easy Auth. The home tenant remains allowed by Entra. Policy activation remains m

## Download a Function ZIP

Download the preview ZIP for your chosen language:
Download the latest successful CI ZIP for your chosen language:

| Language | Download | Contents |
|---|---|---|
| JavaScript | [epp-javascript.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/download/epp-guided-setup-preview-20260915/epp-javascript.zip) | Application and production dependencies |
| .NET | [epp-dotnet-source.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/download/epp-guided-setup-preview-20260915/epp-dotnet-source.zip) | C# Function source and project file; build/publish before deployment |
| Python | [epp-python-source.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/download/epp-guided-setup-preview-20260915/epp-python-source.zip) | Source for Azure remote build on Linux |
| JavaScript | [epp-javascript.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/latest/download/epp-javascript.zip) | Application and production dependencies |
| .NET | [epp-dotnet-source.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/latest/download/epp-dotnet-source.zip) | C# Function source and project file; build/publish before deployment |
| Python | [epp-python-source.zip](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/latest/download/epp-python-source.zip) | Source for Azure remote build on Linux |

Customers do not need PowerShell or a local build toolchain to download these files. Verify downloads
against the corresponding release's `SHA256SUMS.txt`. Configure the target Function App's runtime, app settings,
Expand All @@ -52,14 +52,14 @@ project; Python requires remote build to install dependencies. Neither source ZI
as a run-from-package artifact. GitHub's **Code > Download ZIP**
is the whole source repository, not a Function deployment package.

After the packaging workflow is merged, each successful `main` build tests all three implementations,
builds and inspects the ZIPs, and publishes a new versioned release. Get those builds from
Each successful `main` build tests all three implementations, builds and inspects the ZIPs, and
publishes a new versioned release marked as the latest release.
The direct links above and guided setup therefore track the newest successful CI package build. Get builds from
[Latest release](https://github.com/Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample/releases/latest).
Older releases remain available; existing assets are not overwritten. Pull requests build downloadable
workflow artifacts only and cannot publish releases. GitHub sign-in may be required for workflow
artifacts, but public release downloads do not require a local build. Packaging does not deploy or
verify live provider delivery. The current preview is built from the packaging branch, not a merged
release of the separate provider feature branches.
verify live provider delivery.

## Build ZIPs Locally

Expand Down
2 changes: 1 addition & 1 deletion docs/ONBOARDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Use [CONTRACT.md](CONTRACT.md) for the full request contract and production limi
| `EPP_PROVIDER_APP_ID`, `EPP_PROVIDER_SCOPE` | Soprano API application ID and the exact selected scope, including `/.default`. These are not the calling application's ID. Leave blank for API-key providers. |
| `EPP_OUTBOUND_CLIENT_ID` | Existing calling application's Application (client) ID used during setup, not its Object ID or Soprano's API ID. |
| `EPP_OUTBOUND_MI_CLIENT_ID` | Setup-created outbound user-assigned identity's Client ID, not its principal/Object ID. |
| `EPP_PROVIDER_TIMEOUT_MS`, `EPP_PROVIDER_RETRY_INTERVAL_MS`, `EPP_PROVIDER_TEST_CONFIGURATION` | Selected profile values, as strings. Retry interval and test-configuration metadata do not enable runtime retries or shutter mode. |
| `EPP_PROVIDER_TIMEOUT_MS`, `EPP_PROVIDER_RETRY_INTERVAL_MS` | Selected profile values, as strings. The retry interval does not enable runtime retries or shutter mode. |
| `KEY_VAULT_URL` | Setup-created or explicitly selected credential vault URL, not a secret value. |

The outbound IDs are used only for Soprano OAuth; leave them blank for local API-key-only
Expand Down
1 change: 0 additions & 1 deletion docs/local.settings.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"EPP_PROVIDER_SCOPE": "",
"EPP_PROVIDER_TIMEOUT_MS": "1500",
"EPP_PROVIDER_RETRY_INTERVAL_MS": "30000",
"EPP_PROVIDER_TEST_CONFIGURATION": "false",

"EPP_OUTBOUND_CLIENT_ID": "",
"EPP_OUTBOUND_MI_CLIENT_ID": "",
Expand Down
2 changes: 1 addition & 1 deletion setup/EPP-Setup.psd1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@{
PackageName = 'EPP endpoint deployment'
PackageVersion = '0.3.0'
PackageVersion = '0.3.1'
EntryPoint = 'Setup-Epp.ps1'
MinimumPowerShellVersion = '7.0'
Support = @('support/Epp.Setup.psm1', 'support/Epp.Packages.ps1')
Expand Down
4 changes: 3 additions & 1 deletion setup/Setup-Epp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
No Azure resources are changed until you approve the complete plan.
.PARAMETER SourceRepository
Public GitHub owner/repository containing the setup files. Use with SourceRef to test a fork.
.PARAMETER PackageReleaseTag
Optional stable epp-packages release tag. By default, setup uses the latest stable CI package release.
.PARAMETER InstallPrerequisites
Install missing Microsoft Graph modules and the Azure CLI Bicep component after explicit opt-in.
.PARAMETER ForceAuthentication
Expand All @@ -27,13 +29,13 @@ param(
[string] $Provider,
[string] $Channel,
[string] $EndpointRegion,
[string] $ProviderAccountName,
[string] $ResourcePrefix,
[string] $Language,
[string] $OutputDirectory = (Join-Path $PSScriptRoot 'epp-output'),
[ValidatePattern('^[A-Za-z0-9][A-Za-z0-9-]*/[A-Za-z0-9][A-Za-z0-9_.-]*$')]
[string] $SourceRepository = 'Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample',
[string] $SourceRef = 'main',
[string] $PackageReleaseTag,
[switch] $NonInteractive,
[switch] $InstallPrerequisites,
[switch] $ForceAuthentication,
Expand Down
94 changes: 47 additions & 47 deletions setup/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,22 @@ The customer does not clone this repository or download Bicep/support scripts se

## Availability

Choose **JavaScript, .NET, or Python**, then **Telesign or Soprano**, **SMS or voice**, and a
**Global or EU endpoint**. The private test branch uses its matching fork preview release so the
package and provider-authentication contract stay in sync. There is no package URL or checksum to
enter. Setup verifies `SHA256SUMS.txt` automatically and performs the required build and publication
for the selected language.

Provider profiles contain complete channel/region route objects. Unknown values use **explicit dummy
test values**, not a separate placeholder list or empty fields that block setup. They are written
into the Function App's **actual environment settings** after approval. Telesign's supplied route
URLs, tenant, and timings are preserved, while its zero application IDs remain test-labelled.
Soprano contains its provider tenant, production Global/EU routes, API application ID, scope, and
timing values.
The plan and saved summary identify test configuration. Deployment does not make these values
working endpoints or credentials. The provider files contain the complete deployment contract.

The default download URLs below become usable when this change is published upstream. Before merging,
test from a published public fork using `-SourceRepository <owner/repository>` and
Choose **SMS or voice**, a **Global or EU tenant scope**, **Telesign or Soprano**, and an
Azure Function **platform**: Node.js, .NET, or Python. By default, setup resolves the source repository's latest stable
`epp-packages-*` release produced by CI. A private test branch can use its matching fork release.
There is no package URL or checksum to enter. Setup verifies `SHA256SUMS.txt` automatically and
performs the required build and publication for the selected language.

Provider profiles contain complete channel/region route objects. Telesign contains its supplied
route URLs, tenant, authentication, and timings. Soprano contains its provider tenant, Global/EU
routes, API application ID, scope, authentication, and timings. The provider files contain the
complete deployment contract.

To test unpublished upstream changes, publish them to a public fork with a matching stable package
release, then use `-SourceRepository <owner/repository>` and
`-SourceRef <branch-or-full-commit-sha>`. Both options must identify the same source as the downloaded
launcher. Unpublished worktree changes are not downloadable from GitHub.
launcher. Use `-PackageReleaseTag` if the fork contains more than one stable package release.
Unpublished worktree changes are not downloadable from GitHub.

## Step 1 - manually create the application

Expand Down Expand Up @@ -129,17 +126,20 @@ Force explicit account selection when testing on a shared or multi-account compu
The flow is:

1. **Collect missing customer inputs:** tenant, subscription, existing application client ID, Azure
region, and provider account/sender name. Supplied values
are reused without prompts. Credentials are never requested as ordinary string parameters.
2. **Choose one language**. Setup looks up its GitHub release and checksum file in
`packages/catalog.json`; there are no `PackageUrl` or `PackageSha256` inputs.
3. **Choose a provider**, then **SMS or voice**, then **Global or EU endpoint**. Setup downloads the
provider JSON and resolves one complete route containing endpoint, authentication, app-ID/scope
region, and resource prefix. Supplied values are reused without prompts. Credentials are never
requested as ordinary string parameters.
2. **Choose SMS or voice**, then the **Global or EU tenant scope**.
3. **Choose a provider**, then an Azure Function **platform**: Node.js, .NET, or Python. Setup downloads the provider JSON,
resolves one complete route containing endpoint, authentication, app-ID/scope
when applicable, timeout, and retry interval. Explicit test values are allowed, shown as test
configuration, and passed to Azure settings. Malformed or disabled profiles still fail before
resource creation.
4. **Enter a resource prefix**, such as `contoso`: 2-8 lowercase letters/digits, starting with a
letter. Every top-level resource name then adds the meaningful `epp` marker, for example
configuration, and passed to Azure settings. It also resolves the latest stable CI package
release and looks up the language asset name in `packages/catalog.json`; there are no
`PackageUrl` or `PackageSha256` inputs. Use
`-PackageReleaseTag epp-packages-<run>-<attempt>` to pin a previous CI release. Malformed or
disabled profiles still fail before resource creation.
4. **Enter a resource prefix**, such as `contoso`. All resources created by the script start with
this prefix. Use 2-8 lowercase letters or digits, starting with a letter. Every top-level
resource name then adds the meaningful `epp` marker, for example
`contoso-epp-rg-<suffix>`. A deterministic suffix derived from the
subscription, application ID, and prefix reduces global-name collisions. Reruns use the same names.
5. **Check prerequisites and sign in.** Missing Graph modules or Bicep can be installed after a
Expand Down Expand Up @@ -197,9 +197,7 @@ explicitly rather than hidden.

The public certificate and a timestamped identifier
summary are saved to `epp-output` beside the downloaded script, or to `-OutputDirectory`.
Private keys remain in the user's certificate store and Key Vault, not in that summary. With dummy
profiles, `EPP_PROVIDER_TEST_CONFIGURATION=true` is stored alongside the real environment settings.
This is a label, not a replacement for caller authentication or a guarantee of provider connectivity.
Private keys remain in the user's certificate store and Key Vault, not in that summary.

For unattended runs, supply every input, authenticate both clients first, and explicitly authorize
the whole displayed plan with **both** `-NonInteractive -ApproveDeployment`. `-NonInteractive`
Expand All @@ -210,35 +208,37 @@ alone never approves changes. There is no `-Stage`, `-Resume`, `-ConfigPath`, or
`-SourceRepository` defaults to `Azure-Samples/ExternalPhoneProvider-AzureFunction-Sample`.
The small entry point resolves `-SourceRef` (default `main`) to a single commit in that repository. All supporting
PowerShell, Bicep, the catalog, and the selected provider profile are downloaded from that commit.
Use a reviewed full commit SHA for repeatable deployments. Provider JSON selects data only; it
cannot redirect execution to another script. Download failures stop setup, and temporary downloads
are removed on completion or failure. Select only a repository whose code you trust: its supporting
The package catalog supplies asset names, while setup resolves the latest stable `epp-packages-*`
release from the same repository and verifies the selected asset against that release's
`SHA256SUMS.txt`. The plan and saved summary record the concrete versioned URL and hashes.
For a fully repeatable deployment, use both a reviewed full commit SHA and
`-PackageReleaseTag epp-packages-<run>-<attempt>`. Provider JSON selects data only; it cannot
redirect execution to another script. Download failures stop setup, and temporary downloads are
removed on completion or failure. Select only a repository whose code you trust: its supporting
PowerShell is executed locally.

## Step 3 - manually validate and activate policy

1. Save the Step 2 summary and confirm its tenant, application client ID, endpoint URL, encryption
key ID, and certificate with the EPP onboarding owner. **Replace all test provider values** and
provision the adapter-named API credentials in Key Vault. Verify the package's channel routing
and retry behavior; the tenant/scope metadata and test label do not enable unsupported behavior.
and retry behavior.
2. Validate the deployed endpoint with synthetic, non-delivering evaluation requests first.
Missing/invalid credentials and unauthorized callers must be rejected by Easy Auth. An admitted
caller's valid encrypted request must return the matching nonce. Then verify live SMS/voice
provider acceptance and handset delivery through the supported test procedure. Never put
phone numbers, messages, tokens, private keys, or nonce values in shared logs.
3. An **Authentication Policy Administrator**, using the approved Microsoft Graph tool and delegated
`Policy.ReadWrite.AuthenticationMethod`, must verify that the tenant's currently supported EPP
contract is available. For the preview contract formerly handled by Step 3, inspect
`https://graph.microsoft.com/beta/$metadata` for `authenticationMethodsPolicy.cyot` and its
`endpoint`, `appId`, and `migrated` fields. **If absent or different, stop and obtain the supported
onboarding procedure from Microsoft; do not send a guessed PATCH or enable a different method.**
4. Read `https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy` using that supported
contract, save the existing `cyot` value with tenant ID and timestamp, and independently approve
the migration choice. `migrated` is a routing decision, not a script default.
5. Re-read immediately before a manual change, stop if the policy changed, and use `If-Match` when
an ETag is available. Patch **only** the `cyot` property with the tested endpoint, the same
application client ID, and the deliberately chosen migration Boolean. Read it back and compare
before considering activation complete.
`Policy.ReadWrite.AuthenticationMethod`, must read the selected channel configuration:
`https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/Sms`
for SMS or the same path ending in `/Voice` for voice. If the selected configuration or its
`url` and `appId` properties are unavailable, stop and obtain the supported onboarding procedure
from Microsoft rather than sending a guessed update.
4. Save the existing channel configuration with the tenant ID and timestamp. Re-read it immediately
before a manual change, stop if it changed, and use `If-Match` when an ETag is available.
5. Update `url` with the highlighted Function endpoint and `appId` with the highlighted endpoint
application client ID printed by setup. Preserve all other properties, then read the configuration
back and compare those values before considering activation complete.

Policy activation, policy backups, and policy rollback are administrator-owned manual operations.
No policy API is called by the setup package. For rollback, restore only the reviewed prior EPP
Expand Down
21 changes: 5 additions & 16 deletions setup/docs/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,24 +68,13 @@ the intended `-SourceRepository` and `-SourceRef`. The current version asks for
and reads its package URL and published checksum automatically. Remove old package URL/hash
arguments from saved commands.

## Provider settings are dummy values

Telesign remains explicitly labelled with `deployment.testConfiguration: true` because its route
application IDs are still zero GUIDs. The selected values are written into the actual Function App
environment with `EPP_PROVIDER_TEST_CONFIGURATION=true`. Soprano now has provider-supplied tenant,
endpoint, application ID, scope, and timing values and is not labelled as test configuration.

The script can deploy code with these values, but dummy routes cannot deliver real messages.
Update the provider-owned profile before live use. Telesign requires its API-key secrets in Key
Vault. Soprano uses the selected OAuth tenant/scope/app ID and outbound managed-identity federation;
provider consent and API roles remain external onboarding steps.

## A checksum or package download fails

Each language entry points to a versioned GitHub ZIP and the same release's `SHA256SUMS.txt`.
The file must contain exactly one valid entry for that asset. Missing, duplicate, malformed, or
mismatched checksums fail closed; there is no manual-hash or skip-verification workaround.
Verify the catalog's links and your access to GitHub/release assets.
Setup resolves the latest stable `epp-packages-*` CI release by default, then downloads the selected
catalog asset and that release's `SHA256SUMS.txt`. Use `-PackageReleaseTag` when reproducing a
specific release. The checksum file must contain exactly one valid entry for that asset. Missing,
duplicate, malformed, or mismatched checksums fail closed; there is no manual-hash or
skip-verification workaround. Verify the release assets and your access to GitHub.

Supporting tools, Bicep, catalogs, and provider JSON all come from the commit selected at startup.
For a public-fork branch, pass both source options. A full commit SHA avoids branch-resolution
Expand Down
Loading
Loading