From 166ebd0a8d926f89dbad2360e4d63315cb892784 Mon Sep 17 00:00:00 2001 From: Steve Temple Date: Mon, 27 Jul 2026 14:42:35 +0100 Subject: [PATCH 1/5] Update editorconfig --- src/.editorconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/.editorconfig b/src/.editorconfig index 2162dd5..9c3f60c 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -6,7 +6,7 @@ root = true ############################################################################### # Set default behavior to: # a UTF-8 encoding, -# Unix-style line endings, +# Windows-style line endings, # a newline ending the file, # 2 space indentation, and # trimming of trailing whitespace @@ -80,7 +80,6 @@ dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.symbols [*.cs] - # Define the 'private_fields' symbol group: dotnet_naming_symbols.private_fields.applicable_kinds = field dotnet_naming_symbols.private_fields.applicable_accessibilities = private From 9f9b14194e5cab6ce1b997cf324cf63f59cc34b4 Mon Sep 17 00:00:00 2001 From: Steve Temple Date: Tue, 28 Jul 2026 08:39:01 +0100 Subject: [PATCH 2/5] Add demo site script --- README.md | 20 ++++ scripts/install-demo-site.ps1 | 159 ++++++++++++++++++++++++++ scripts/install-demo-site.sh | 206 ++++++++++++++++++++++++++++++++++ 3 files changed, 385 insertions(+) create mode 100644 scripts/install-demo-site.ps1 create mode 100644 scripts/install-demo-site.sh diff --git a/README.md b/README.md index cf7daaa..ad75527 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,26 @@ Select the instructions for your Umbraco version [v13](README-v13.md) [v15+](README-v15plus.md) +## Local development + +To spin up a local Umbraco site for manually testing changes to this package, run one of the demo site scripts from the repo root: + +``` +./scripts/install-demo-site.ps1 # Windows/PowerShell +./scripts/install-demo-site.sh # macOS/Linux/bash +``` + +This scaffolds a new Umbraco site under `demo/`, references it against the `Umbraco.Community.AzureSSO` project in `src/` (so your local changes are picked up directly, no need to pack/publish), and creates `Umbraco.Community.AzureSSO.local.slnx` combining both projects for convenience. The `AzureSSO` configuration section is added to the demo site's `appsettings.Development.json` disabled by default, with placeholder `REPLACE_WITH_*` values. + +To use it: + +1. Follow [EntraIDSetup.md](EntraIDSetup.md) to create an App Registration in Azure +2. Fill in the `AzureSSO.Credentials` values in `demo/Umbraco.Community.AzureSSO.DemoSite/appsettings.Development.json` +3. Set `AzureSSO.Enabled` to `true` +4. Open `Umbraco.Community.AzureSSO.local.slnx`, build, and run the `Umbraco.Community.AzureSSO.DemoSite` project + +Both scripts accept `-Force`/`--force` to recreate the demo site from scratch, and `-SkipTemplateInstall`/`--skip-template-install` to skip reinstalling the Umbraco templates on repeat runs. The demo site and local solution file are gitignored. + ## Advanced usage ### Manually composing diff --git a/scripts/install-demo-site.ps1 b/scripts/install-demo-site.ps1 new file mode 100644 index 0000000..a79d77b --- /dev/null +++ b/scripts/install-demo-site.ps1 @@ -0,0 +1,159 @@ +# Demo Site Setup Script +# Creates a local Umbraco site referencing this repo's Umbraco.Community.AzureSSO project, +# for manually testing the SSO login flow during development. + +param( + [switch]$SkipTemplateInstall, + [switch]$Force +) + +$ErrorActionPreference = "Stop" + +# Determine repository root (parent of scripts folder) +$ScriptDir = $PSScriptRoot +$RepoRoot = (Resolve-Path (Split-Path -Parent $ScriptDir)).Path + +# Change to repository root to ensure consistent behavior +Push-Location $RepoRoot + +Write-Host "=== Umbraco.Community.AzureSSO Demo Site Setup ===" -ForegroundColor Cyan +Write-Host "Working directory: $RepoRoot" -ForegroundColor Gray +Write-Host "" + +# Detect the Umbraco template version to scaffold against. The project multi-targets several +# Umbraco majors at once (net6.0-net10.0); we always demo against the newest one (net10.0), reading +# its Umbraco.Cms.Web.Common version straight out of the csproj so this stays in lockstep with it. +$csprojPath = Join-Path $RepoRoot "src\Umbraco.Community.AzureSSO\Umbraco.Community.AzureSSO.csproj" +if (-not (Test-Path $csprojPath)) { + Write-Host "ERROR: Could not find $csprojPath" -ForegroundColor Red + exit 1 +} +$csprojContent = Get-Content $csprojPath -Raw +# Requires "==" before net10.0 so this doesn't match the sibling ItemGroup keyed on +# "!= 'net10.0'" (for older TFMs), which also mentions net10.0. +if ($csprojContent -match '(?s)Condition="[^"]*==[^"]*net10\.0[^"]*">(.*?)') { + $net10Block = $matches[1] +} else { + Write-Host "ERROR: Could not find the net10.0 ItemGroup in $csprojPath" -ForegroundColor Red + exit 1 +} +if ($net10Block -match 'Umbraco\.Cms\.Web\.Common"[^>]*>\s*([^<]+)') { + $TemplateVersion = $matches[1] +} elseif ($net10Block -match 'Umbraco\.Cms\.Web\.Common"\s+Version="([^"]+)"') { + $TemplateVersion = $matches[1] +} else { + Write-Host "ERROR: Could not find the Umbraco.Cms.Web.Common version for net10.0 in $csprojPath" -ForegroundColor Red + exit 1 +} +$VersionMajor = [int]($TemplateVersion -split '\.')[0] +$IsTemplatePrerelease = $TemplateVersion -match '-' +Write-Host "Target Umbraco.Cms template version: $TemplateVersion (v$VersionMajor)" -ForegroundColor Gray +Write-Host "" + +$DemoDir = "demo" +$DemoSiteName = "Umbraco.Community.AzureSSO.DemoSite" +$DemoSiteDir = "$DemoDir\$DemoSiteName" +$SolutionName = "Umbraco.Community.AzureSSO.local" +$LibraryProject = "src\Umbraco.Community.AzureSSO\Umbraco.Community.AzureSSO.csproj" + +# Check if demo already exists +if ((Test-Path $DemoDir) -and -not $Force) { + Write-Host "Demo folder '$DemoDir' already exists. Use -Force to recreate." -ForegroundColor Yellow + Write-Host "Or open the existing $SolutionName.slnx" -ForegroundColor Yellow + exit 0 +} + +# Clean up existing demo if Force +if ($Force -and (Test-Path $DemoDir)) { + Write-Host "Removing existing demo folder '$DemoDir'..." -ForegroundColor Yellow + Remove-Item -Recurse -Force $DemoDir +} + +if ($Force -and (Test-Path "$SolutionName.slnx")) { + Remove-Item -Force "$SolutionName.slnx" +} + +# Step 1: Install Umbraco templates +if (-not $SkipTemplateInstall) { + Write-Host "Installing Umbraco templates ($TemplateVersion)..." -ForegroundColor Green + + # Uninstall any existing version to avoid conflicts + Write-Host "Removing any existing Umbraco.Templates installations..." -ForegroundColor Gray + $installedTemplates = dotnet new uninstall 2>&1 | Out-String + if ($installedTemplates -match "Umbraco\.Templates") { + try { + dotnet new uninstall Umbraco.Templates 2>&1 | Out-Null + } catch { + # Ignore errors during uninstall + } + } + + if ($IsTemplatePrerelease) { + # Prerelease templates require the umbracoprereleases MyGet feed to be configured. + # If not yet configured: dotnet nuget add source https://www.myget.org/F/umbracoprereleases/api/v3/index.json --name UmbracoPreReleases + Write-Host "NOTE: Prerelease template ($TemplateVersion) requires the umbracoprereleases MyGet source." -ForegroundColor Yellow + } + dotnet new install "Umbraco.Templates::$TemplateVersion" --force +} + +# Step 2: Create the Umbraco demo site +Write-Host "Creating demo folder '$DemoDir'..." -ForegroundColor Green +New-Item -ItemType Directory -Path $DemoDir -Force | Out-Null + +Write-Host "Creating Umbraco demo site..." -ForegroundColor Green +Push-Location $DemoDir +dotnet new umbraco --force -n $DemoSiteName --friendly-name "Administrator" --email "admin@example.com" --password "password1234" --development-database-type SQLite +Pop-Location + +# Step 3: Add project reference to Umbraco.Community.AzureSSO +Write-Host "Adding project reference to Umbraco.Community.AzureSSO..." -ForegroundColor Green +$demoProject = "$DemoSiteDir\$DemoSiteName.csproj" +dotnet add $demoProject reference $LibraryProject + +# Step 4: Add a placeholder AzureSSO config section +# Disabled by default so the demo site boots cleanly without an Entra ID app registration. +# See EntraIDSetup.md and README-v15plus.md for what these values mean and how to fill them in. +Write-Host "Adding placeholder AzureSSO configuration..." -ForegroundColor Green +$devSettingsPath = "$DemoSiteDir\appsettings.Development.json" +$devSettings = Get-Content $devSettingsPath -Raw | ConvertFrom-Json +$azureSsoSettings = [PSCustomObject]@{ + Enabled = $false + DisplayName = "Azure AD" + Credentials = [PSCustomObject]@{ + Instance = "https://login.microsoftonline.com/" + Domain = "REPLACE_WITH_DOMAIN" + TenantId = "REPLACE_WITH_TENANT_ID" + ClientId = "REPLACE_WITH_CLIENT_ID" + ClientSecret = "REPLACE_WITH_CLIENT_SECRET" + CallbackPath = "/umbraco-microsoft-signin/" + SignedOutCallbackPath = "/umbraco-microsoft-signout/" + } +} +$devSettings | Add-Member -NotePropertyName "AzureSSO" -NotePropertyValue $azureSsoSettings -Force +$devSettings | ConvertTo-Json -Depth 10 | Out-File -FilePath $devSettingsPath -Encoding utf8 -Force + +# Step 5: Create unified solution +Write-Host "Creating unified solution..." -ForegroundColor Green +dotnet new sln -n $SolutionName --force +dotnet sln "$SolutionName.slnx" add $LibraryProject --solution-folder "Library" +dotnet sln "$SolutionName.slnx" add $demoProject --solution-folder "Demo" + +Write-Host "" +Write-Host "=== Setup Complete! ===" -ForegroundColor Green +Write-Host "" +Write-Host "Solution: $SolutionName.slnx" -ForegroundColor Cyan +Write-Host "Demo site: $DemoSiteDir" -ForegroundColor Cyan +Write-Host "" +Write-Host "Credentials:" -ForegroundColor Yellow +Write-Host " Email: admin@example.com" +Write-Host " Password: password1234" +Write-Host "" +Write-Host "Next steps:" -ForegroundColor Yellow +Write-Host " 1. Follow EntraIDSetup.md to create an App Registration in Azure" +Write-Host " 2. Fill in the AzureSSO.Credentials values in $DemoSiteDir\appsettings.Development.json" +Write-Host " 3. Set AzureSSO.Enabled to true" +Write-Host " 4. Open $SolutionName.slnx in your IDE, build, and run the $DemoSiteName project" +Write-Host "" + +# Restore original directory +Pop-Location diff --git a/scripts/install-demo-site.sh b/scripts/install-demo-site.sh new file mode 100644 index 0000000..acc6a3a --- /dev/null +++ b/scripts/install-demo-site.sh @@ -0,0 +1,206 @@ +#!/bin/bash +# Demo Site Setup Script +# Creates a local Umbraco site referencing this repo's Umbraco.Community.AzureSSO project, +# for manually testing the SSO login flow during development. + +set -e + +# Determine repository root (parent of scripts folder) +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &>/dev/null && pwd )" +REPO_ROOT="$( cd "$SCRIPT_DIR/.." &>/dev/null && pwd )" + +# Change to repository root to ensure consistent behavior +cd "$REPO_ROOT" || exit 1 + +# Parse arguments +SKIP_TEMPLATE_INSTALL=false +FORCE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-template-install|-s) + SKIP_TEMPLATE_INSTALL=true + shift + ;; + --force|-f) + FORCE=true + shift + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -s, --skip-template-install Skip reinstalling Umbraco.Templates" + echo " -f, --force Recreate demo if it already exists" + echo " -h, --help Show this help message" + exit 0 + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +echo "=========================================" +echo "Umbraco.Community.AzureSSO Demo Site Setup" +echo "=========================================" +echo "Working directory: $REPO_ROOT" +echo "" + +# Detect the Umbraco template version to scaffold against. The project multi-targets several +# Umbraco majors at once (net6.0-net10.0); we always demo against the newest one (net10.0), reading +# its Umbraco.Cms.Web.Common version straight out of the csproj so this stays in lockstep with it. +CSPROJ_PATH="$REPO_ROOT/src/Umbraco.Community.AzureSSO/Umbraco.Community.AzureSSO.csproj" +if [ ! -f "$CSPROJ_PATH" ]; then + echo "ERROR: Could not find $CSPROJ_PATH" >&2 + exit 1 +fi +# Excludes the sibling ItemGroup keyed on "!= 'net10.0'" (for older TFMs), which also mentions net10.0. +NET10_BLOCK=$(awk '/ItemGroup/ && /net10\.0/ && !/!=/{flag=1} flag{print} flag && /<\/ItemGroup>/{exit}' "$CSPROJ_PATH") +if [ -z "$NET10_BLOCK" ]; then + echo "ERROR: Could not find the net10.0 ItemGroup in $CSPROJ_PATH" >&2 + exit 1 +fi +TEMPLATE_VERSION=$(echo "$NET10_BLOCK" | grep -A1 'Umbraco\.Cms\.Web\.Common' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.]+)?' | head -1) +if [ -z "$TEMPLATE_VERSION" ]; then + echo "ERROR: Could not find the Umbraco.Cms.Web.Common version for net10.0 in $CSPROJ_PATH" >&2 + exit 1 +fi +VERSION_MAJOR=$(echo "$TEMPLATE_VERSION" | cut -d. -f1) +IS_TEMPLATE_PRERELEASE=false +if echo "$TEMPLATE_VERSION" | grep -q '-'; then + IS_TEMPLATE_PRERELEASE=true +fi +echo "Target Umbraco.Cms template version: $TEMPLATE_VERSION (v$VERSION_MAJOR)" +echo "" + +DEMO_DIR="demo" +DEMO_SITE_NAME="Umbraco.Community.AzureSSO.DemoSite" +DEMO_SITE_DIR="${DEMO_DIR}/${DEMO_SITE_NAME}" +SOLUTION_NAME="Umbraco.Community.AzureSSO.local" +LIBRARY_PROJECT="src/Umbraco.Community.AzureSSO/Umbraco.Community.AzureSSO.csproj" + +# Check if demo already exists +if [ -d "$DEMO_DIR" ] && [ "$FORCE" = false ]; then + echo "Demo folder '$DEMO_DIR' already exists. Use --force to recreate." + echo "Or open the existing ${SOLUTION_NAME}.slnx" + exit 0 +fi + +# Clean up existing demo if Force +if [ "$FORCE" = true ] && [ -d "$DEMO_DIR" ]; then + echo "Removing existing demo folder '$DEMO_DIR'..." + rm -rf "$DEMO_DIR" +fi + +if [ "$FORCE" = true ] && [ -f "${SOLUTION_NAME}.slnx" ]; then + rm -f "${SOLUTION_NAME}.slnx" +fi + +# Step 1: Install Umbraco templates +if [ "$SKIP_TEMPLATE_INSTALL" = false ]; then + echo "Installing Umbraco templates ($TEMPLATE_VERSION)..." + # Uninstall any existing version to avoid conflicts + echo "Removing any existing Umbraco.Templates installations..." + if dotnet new uninstall 2>&1 | grep -q "Umbraco\.Templates"; then + dotnet new uninstall Umbraco.Templates 2>/dev/null || true + fi + if [ "$IS_TEMPLATE_PRERELEASE" = true ]; then + # Prerelease templates require the umbracoprereleases MyGet feed to be configured. + # If not yet configured: dotnet nuget add source https://www.myget.org/F/umbracoprereleases/api/v3/index.json --name UmbracoPreReleases + echo "NOTE: Prerelease template ($TEMPLATE_VERSION) requires the umbracoprereleases MyGet source." + fi + dotnet new install "Umbraco.Templates::${TEMPLATE_VERSION}" --force +fi + +# Step 2: Create the Umbraco demo site +echo "Creating demo folder '$DEMO_DIR'..." +mkdir -p "$DEMO_DIR" + +echo "Creating Umbraco demo site..." +pushd "$DEMO_DIR" > /dev/null +dotnet new umbraco --force -n "$DEMO_SITE_NAME" --friendly-name "Administrator" --email "admin@example.com" --password "password1234" --development-database-type SQLite +popd > /dev/null + +# Step 3: Add project reference to Umbraco.Community.AzureSSO +echo "Adding project reference to Umbraco.Community.AzureSSO..." +DEMO_PROJECT="${DEMO_SITE_DIR}/${DEMO_SITE_NAME}.csproj" +dotnet add "$DEMO_PROJECT" reference "$LIBRARY_PROJECT" + +# Step 4: Add a placeholder AzureSSO config section +# Disabled by default so the demo site boots cleanly without an Entra ID app registration. +# See EntraIDSetup.md and README-v15plus.md for what these values mean and how to fill them in. +echo "Adding placeholder AzureSSO configuration..." +DEV_SETTINGS_PATH="${DEMO_SITE_DIR}/appsettings.Development.json" +if command -v jq >/dev/null 2>&1; then + jq '.AzureSSO = { + "Enabled": false, + "DisplayName": "Azure AD", + "Credentials": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "REPLACE_WITH_DOMAIN", + "TenantId": "REPLACE_WITH_TENANT_ID", + "ClientId": "REPLACE_WITH_CLIENT_ID", + "ClientSecret": "REPLACE_WITH_CLIENT_SECRET", + "CallbackPath": "/umbraco-microsoft-signin/", + "SignedOutCallbackPath": "/umbraco-microsoft-signout/" + } + }' "$DEV_SETTINGS_PATH" > "${DEV_SETTINGS_PATH}.tmp" + mv "${DEV_SETTINGS_PATH}.tmp" "$DEV_SETTINGS_PATH" +else + # jq isn't guaranteed to be installed; fall back to a plain text insert. This relies on the + # scaffolded appsettings.Development.json being a normal single top-level JSON object, which is + # what `dotnet new umbraco` produces. + echo " (jq not found, falling back to a plain text insert)" + CONTENT=$(cat "$DEV_SETTINGS_PATH") + TRIMMED="${CONTENT%\}}" + TRIMMED="${TRIMMED%"${TRIMMED##*[![:space:]]}"}" + { + printf '%s' "$TRIMMED" + cat <<'JSON_EOF' +, + "AzureSSO": { + "Enabled": false, + "DisplayName": "Azure AD", + "Credentials": { + "Instance": "https://login.microsoftonline.com/", + "Domain": "REPLACE_WITH_DOMAIN", + "TenantId": "REPLACE_WITH_TENANT_ID", + "ClientId": "REPLACE_WITH_CLIENT_ID", + "ClientSecret": "REPLACE_WITH_CLIENT_SECRET", + "CallbackPath": "/umbraco-microsoft-signin/", + "SignedOutCallbackPath": "/umbraco-microsoft-signout/" + } + } +} +JSON_EOF + } > "${DEV_SETTINGS_PATH}.tmp" + mv "${DEV_SETTINGS_PATH}.tmp" "$DEV_SETTINGS_PATH" +fi + +# Step 5: Create unified solution +echo "Creating unified solution..." +dotnet new sln -n "$SOLUTION_NAME" --force +dotnet sln "${SOLUTION_NAME}.slnx" add "$LIBRARY_PROJECT" --solution-folder "Library" +dotnet sln "${SOLUTION_NAME}.slnx" add "$DEMO_PROJECT" --solution-folder "Demo" + +echo "" +echo "=========================================" +echo "Setup Complete!" +echo "=========================================" +echo "" +echo "Solution: ${SOLUTION_NAME}.slnx" +echo "Demo site: $DEMO_SITE_DIR" +echo "" +echo "Credentials:" +echo " Email: admin@example.com" +echo " Password: password1234" +echo "" +echo "Next steps:" +echo " 1. Follow EntraIDSetup.md to create an App Registration in Azure" +echo " 2. Fill in the AzureSSO.Credentials values in $DEV_SETTINGS_PATH" +echo " 3. Set AzureSSO.Enabled to true" +echo " 4. Open ${SOLUTION_NAME}.slnx in your IDE, build, and run the $DEMO_SITE_NAME project" +echo "" From 19cb222c5233a2d917bb8f94176488b3318a7cf2 Mon Sep 17 00:00:00 2001 From: Steve Temple Date: Tue, 28 Jul 2026 08:39:20 +0100 Subject: [PATCH 3/5] Update gitignore --- .gitignore | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 276e411..cd8ba1d 100644 --- a/.gitignore +++ b/.gitignore @@ -194,6 +194,12 @@ applicationhost.config *.orig # ============================ -# Web test +# Web test # ============================ -*.webtestresult \ No newline at end of file +*.webtestresult + +# ============================ +# Demo site (generated by scripts/install-demo-site.ps1 or scripts/install-demo-site.sh) +# ============================ +demo/ +*.local.slnx \ No newline at end of file From 47cc555bbe64bf5b908511e159c941009c5999e3 Mon Sep 17 00:00:00 2001 From: Steve Temple Date: Tue, 28 Jul 2026 09:28:13 +0100 Subject: [PATCH 4/5] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- scripts/install-demo-site.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install-demo-site.ps1 b/scripts/install-demo-site.ps1 index a79d77b..cb8150a 100644 --- a/scripts/install-demo-site.ps1 +++ b/scripts/install-demo-site.ps1 @@ -60,7 +60,8 @@ $LibraryProject = "src\Umbraco.Community.AzureSSO\Umbraco.Community.AzureSSO.csp if ((Test-Path $DemoDir) -and -not $Force) { Write-Host "Demo folder '$DemoDir' already exists. Use -Force to recreate." -ForegroundColor Yellow Write-Host "Or open the existing $SolutionName.slnx" -ForegroundColor Yellow - exit 0 + Pop-Location + return } # Clean up existing demo if Force From 8d2e64bf428c7dc01a7767ffb0e0f8588035355c Mon Sep 17 00:00:00 2001 From: Steve Temple Date: Thu, 30 Jul 2026 09:44:43 +0100 Subject: [PATCH 5/5] Update description in demo site script --- scripts/install-demo-site.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/install-demo-site.ps1 b/scripts/install-demo-site.ps1 index cb8150a..c20fba4 100644 --- a/scripts/install-demo-site.ps1 +++ b/scripts/install-demo-site.ps1 @@ -152,8 +152,9 @@ Write-Host "" Write-Host "Next steps:" -ForegroundColor Yellow Write-Host " 1. Follow EntraIDSetup.md to create an App Registration in Azure" Write-Host " 2. Fill in the AzureSSO.Credentials values in $DemoSiteDir\appsettings.Development.json" -Write-Host " 3. Set AzureSSO.Enabled to true" -Write-Host " 4. Open $SolutionName.slnx in your IDE, build, and run the $DemoSiteName project" +Write-Host " 3. Configure any optional settings as needed in $DemoSiteDir\appsettings.Development.json" +Write-Host " 4. Set AzureSSO.Enabled to true" +Write-Host " 5. Open $SolutionName.slnx in your IDE, build, and run the $DemoSiteName project" Write-Host "" # Restore original directory