지난 포스팅에서는 MACP가 무엇인지를 소개해 드렸습니다.
2023.04.09 - [Lync & SFB & Team] - Skype for business Server 2019. MACP (Modern Admin Control Panel)
이번에는 MACP 사이트를 ADFS와의 클레임 인증 설정에 대해서 다루고자 합니다.
아래의 기술자료에 나와 있는 스크립트를 기준으로 진행합니다.
Agenda
I. ADFS에서 Script 적용
II. Skype Frontend Server에서 Script 적용
III. Access Control Policy 설정 추가를 위한 Web API 추가
I. ADFS에서 Script 적용
아래의 스크립트를 ADFS에서 PS1 파일로 저장
<#
.SYNOPSIS
Helper script to configure MACP Application in ADFS Farm.
.DESCRIPTION
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK
OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.EXAMPLE
Creates an application in ADFS Farm with name "MACPApp"
#>
$confirmation = Read-Host -Prompt "Are you running this script on an ADFS Farm server. Confirm[y/n]"
if ($confirmation -match '[^yY]')
{
if ($confirmation -match '[nN]')
{
Write-Host "`n`rPlease run this script only on ADFS Farm servers." -ForeGroundColor Red
}
else
{
Write-Host "`n`rInvalid Input." -ForeGroundColor Red
}
Write-Host "`n`rAborting Process." -ForeGroundColor Red
return
}
Write-Host "ADFS application group and native client application for Admin Control Panel can be setup only on ADFS Farm servers running on Windows Server 2016 or above. Checking the current server version ..."
$version = [System.Environment]::OSVersion.Version.Major
# Version reference for Windows Server here:
# https://learn.microsoft.com/windows/win32/sysinfo/operating-system-version
if ($version -lt 10)
{
Write-Host "`n`rInvalid Window Server version. Please run this script on ADFS farm servers running on Windows Server 2016 or above." -ForeGroundColor Red
return
}
Write-Host "Valid Windows Server Version." -ForeGroundColor Green
Write-Host "`n`rCreating an ADFS application group and native client application for Admin Control Panel in the ADFS Farm."
# Creating ApplicationGroup.
$groupName = Read-Host -Prompt "Enter ADFS Application Group Name for Admin Control Panel. Press enter to use default name as 'MACP' "
if([string]::IsNullOrWhiteSpace($groupName))
{
$groupName = "MACP"
}
try
{
New-AdfsApplicationGroup -Name $groupName
}
catch
{
Write-Error "`nError in creating Application Group $groupName. Please ensure that you have enabled and configured WindowsFeature ADFS-Federation for this server. Aborting Process."
throw
exit
}
# Creating a new native MACP App under above Application Group.
$appName = Read-Host -Prompt "Enter Admin Control Panel Application Name. Press ENTER to use default name as 'MACPApp' "
if([string]::IsNullOrWhiteSpace($appName))
{
$appName = "MACPApp"
}
$clientIdentifier = [guid]::NewGuid()
$computerDetails = Get-WmiObject win32_computersystem
if (($computerDetails -eq $null) -OR
($computerDetails.DNSHostName -eq $null) -OR
($computerDetails.Domain -eq $null))
{
# Unable to find computer details.
# We will return false in that case.
Write-Error "`nUnable to find host and domain details. Aborting process."
return
}
$defaultDomain = $computerDetails.Domain
$domain = Read-Host -Prompt "Enter Admin Control Panel application's domain name e.g. contoso.com. Press ENTER to keep $defaultDomain"
if([string]::IsNullOrWhiteSpace($domain))
{
$domain = $defaultDomain
}
$extDomain = Read-Host -Prompt "Enter Admin Control Panel application's external domain name if its different from $domain or else ADFS OAuth wouldn't work for external website for Admin Control Panel "
[string[]] $poolList= @()
$poolList = (Read-Host -Prompt "Enter all the front end pool names in a comma separated manner without the domain name e.g. pool0,pool1,pool2 etc")
$poolList = $poolList.Split(',').Split(' ')
if($poolList.Count -eq 0)
{
Write-Error "`nAt least 1 pool machine name is mandatory. Please run the script again once you are ready with the names. Deleting $groupName."
Remove-AdfsApplicationGroup -TargetApplicationGroupIdentifier $groupName
exit
}
foreach($poolName in $poolList)
{
if ($poolName -match "^(\w+).$domain$" -or `
(![string]::IsNullOrWhiteSpace($extDomain) -and `
$poolName -match "^(\w+).$extDomain$"))
{
Write-Error "Enter pool name $poolName without domain name."
Remove-AdfsApplicationGroup -TargetApplicationGroupIdentifier $groupName
exit
}
}
[string[]] $redirectUrls = @()
foreach ($poolName in $poolList)
{
$redirectUrls += "https://" + $poolName + "." + $domain + "/macp/login"
$redirectUrls += "https://" + $poolName + "." + $domain + "/macp/logout"
$redirectUrls += "https://" + $poolName + "." + $domain + "/macp/portal_oauth_iframe.html"
if (![string]::IsNullOrWhiteSpace($extDomain))
{
$redirectUrls += "https://" + $poolName + "." + $extDomain + "/macp/login"
$redirectUrls += "https://" + $poolName + "." + $extDomain + "/macp/logout"
$redirectUrls += "https://" + $poolName + "." + $extDomain + "/macp/portal_oauth_iframe.html"
}
}
# Handling entry for simplifiedUrl.
$simplifiedUrl = Read-Host -Prompt "Enter Admin Control Panel application's simplified URL eg. https://admin.contoso.com. Press ENTER to skip "
if (![string]::IsNullOrWhiteSpace($simplifiedUrl))
{
if ($simplifiedUrl.StartsWith('http://'))
{
$simplifiedUrl = $simplifiedUrl.Replace('http://', '')
}
if (!$simplifiedUrl.StartsWith('https://'))
{
$simplifiedUrl = "https://" + $simplifiedUrl
}
$simplifiedUrl = $simplifiedUrl.TrimEnd('/')
$redirectUrls += $simplifiedUrl + "/login"
$redirectUrls += $simplifiedUrl + "/logout"
$redirectUrls += $simplifiedUrl + "/macp/portal_oauth_iframe.html"
}
try
{
Add-AdfsNativeClientApplication -ApplicationGroupIdentifier $groupName -Name $appName -Identifier $clientIdentifier -RedirectUri $redirectUrls
}
catch
{
Write-Error "`nUnable to create Admin Control Panel Native Client App. Deleting $groupName. Aborting process."
Remove-AdfsApplicationGroup -TargetApplicationGroupIdentifier $groupName
throw
exit
}
Write-Host "`nAdmin Control panel Native App created successfully with client Id: $clientIdentifier." -ForeGroundColor Green
Write-Host "`nDetails of the App are:`n"
Get-AdfsNativeClientApplication -Identifier $clientIdentifier
ADFS 서버에서 Script 실행
Are you running this script on an ADFS Farm server. Confirm[y/n]:
ADFS 팜 서버에서 이 스크립트를 실행하고 있습니까?
-> ADFS에서 스크립트를 실행하는 것이 맞는지 확인 후 Y
Creating an ADFS application group and native client application for Admin Control Panel in the ADFS Farm.
ADFS 팜에서 관리자 제어판에 대한 ADFS 애플리케이션 그룹 및 기본 클라이언트 애플리케이션 생성.
Enter ADFS Application Group Name for Admin Control Panel. Press enter to use default name as 'MACP'
관리자 제어판에 ADFS 애플리케이션 그룹 이름을 입력합니다. 기본 이름을 'MACP'로 사용하려면 Enter 키를 누르세요.
-> ADFS에 표시되는 App Group 이름입니다. Enter나 MACP를 입력합니다.
Enter Admin Control Panel Application Name. Press ENTER to use default name as 'MACPApp'
관리자 제어판 애플리케이션 이름을 입력합니다. 기본 이름을 'MACPApp'으로 사용하려면 Enter 키를 누르세요.
Enter Admin Control Panel application's domain name e.g. contoso.com. Press ENTER to keep corp.contoso.kr
관리자 제어판 애플리케이션의 도메인 이름을 입력합니다. contoso.com. corp.contoso.kr을 유지하려면 Enter 키를 누르세요.
-> AD Domain Name이 맞으면 Enter 입력
Enter Admin Control Panel application's external domain name if its different from corp.contoso.kr or else ADFS OAuth wouldn't work for external website for Admin Control Panel
corp.contoso.kr과 다른 경우 관리자 제어판 응용 프로그램의 외부 도메인 이름을 입력하십시오. 그렇지 않으면 관리자 제어판의 외부 웹 사이트에서 ADFS OAuth가 작동하지 않습니다.
-> 외부에서 사용하는 도메인을 입력합니다.
Enter all the front end pool names in a comma separated manner without the domain name e.g. pool0,pool1,pool2 etc:
도메인 이름 없이 쉼표로 구분된 방식으로 모든 프런트 엔드 풀 이름을 입력합니다. pool0, pool1, pool2 등:
제 환경은 Pool.corp.contoso.kr 이기 때문에 pool 만 입력하였습니다.
-> 설치할 Frontend Pool 입력
Enter Admin Control Panel application's simplified URL eg. https://admin.contoso.com. Press ENTER to skip
예를 들어 관리자 제어판 애플리케이션의 약식 URL을 입력합니다. https://admin.contoso.com. 건너뛰려면 Enter 키를 누르세요.
-> 우선 건너 뛰겠습니다.
아래와 같이 완료되었습니다.
ADFS 의 Application Groups 메뉴에서 생성된 정보를 확인할 수 있습니다.
II. Skype Frontend Server에서 Script 적용
아래의 스크립트를 Skype for Business Server에서 PS1파일로 저장합니다.
<#
.SYNOPSIS
Helper script to configure SFB 2019 control panel with ADFS OAuth.
.DESCRIPTION
Copyright (c) Microsoft Corporation. All rights reserved.
THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK
OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
.PARAMETER AdfsClientId
Specify the client identifier application created for the admin control panel in the ADFS.
.PARAMETER AdfsMetadataPublicUri
OAuth metadata public URI for the admin control panel in the ADFS.
.PARAMETER AdfsIssuerName
Issuer Name for the admin control panel in the ADFS.
.PARAMETER AdfsOAuthInstance
ADFS farm instance (FQDN) where the admin control panel is registered for ADFS OAuth.
.PARAMETER SimplifiedUrlPrefix
The Simple URL prefix .
.PARAMETER Pools
List of pool names in which ADFS OAuth is to be setup for the admin control panel in a comma separated format. By default OAuth will setup for all FE pools deployed with Skype for Business 2019.
#>
function GetCurrentPoolName($computerDetails)
{
$computerDnsHostName = $computerDetails.DNSHostName
$domainName = $computerDetails.Domain
$computerFqdn = $computerDnsHostName + "." + $domainName
$poolName = (Get-CsComputer -Identity $computerFqdn).Pool
return $poolName
}
function GetAllSfBW17Pools()
{
$allFEPools = Get-csservice -Registrar | Where-Object {$_.Version -gt 7}
$poolNamesArray = @()
foreach ($pool in $allFEPools)
{
$poolNamesArray += ($pool.PoolFqdn)
}
return $poolNamesArray
}
function GeneratePoolNameArray($pools, $computerDetails)
{
$poolNames = $pools.Split(',')
$poolNamesArray = @()
$domainName = $computerDetails.Domain
foreach ($poolName in $poolNames)
{
if ($poolName -match "^(\w+.)$domainName$")
{
$poolNamesArray += ($poolName)
}
else
{
$poolNamesArray += ($poolName + "." + $domainName)
}
}
return $poolNamesArray
}
function VerifyRoleAndVersionOfSelectedPools($poolArrays)
{
foreach ($poolName in $poolArrays)
{
$services = Get-CsService -PoolFqdn $poolName
if ($services -eq $null)
{
# Unable to find services for this pool.
# We will mark it as not a Front-End machine.
Write-Error "Didn't find any Services on Pool : $poolName"
return $false
}
$found = $false
foreach ($service in $services)
{
# Performing a case insensitive search here.
if ($service.Role.ToLower().Equals("registrar") -AND
$service.Version -gt 7)
{
# Found the Registrar role for the pool.
# Hence, a Front-End machine.
$found = $true
break
}
}
if ($found -eq $false)
{
Write-Error "Pool : $poolName is not Front End pool. Aborting process."
return $false
}
}
return $true
}
function VerifyMacpVersionOnFrontEndPools($cred, $pools, $macpRegistryPath, $minVersion)
{
$returnVal = $true
foreach ($poolName in $pools)
{
$computers = (Get-CsComputer -Pool $poolName).Identity
$versionString = $null
foreach ($computer in $computers)
{
$psSession = New-PSSession -computerName $computer -credential $cred
$currentVersionStr = Invoke-Command -Session $psSession -ArgumentList $macpRegistryPath -ScriptBlock {
param($macpRegistryPath)
Get-ItemPropertyValue -Path $macpRegistryPath -Name "Version"
}
if ($psSession)
{
Remove-PSSession -Session $psSession
}
if (!$currentVersionStr)
{
Write-Error "Could not fetch current version of Admin Control Panel on $computer. Please check your connection or the credentials provided. Aborting process."
$returnVal = $false;
}
else
{
Write-Host "`n`rInstalled version of Admin Control Panel on $computer : $currentVersionStr"
if ($versionString -eq $null)
{
$versionString = $currentVersionStr
}
else
{
if ($currentVersionStr -ne $versionString)
{
Write-Error "Product version doesn't match across servers in Front-End pool: $poolName"
$returnVal = $false
}
}
if (([System.Version]$currentVersionStr -lt [System.Version]$minVersion) -OR
([System.Version]$currentVersionStr -eq [System.Version]$minVersion))
{
Write-Error "Install updates on Server: $computer in Front-End pool : $poolName to active ADFS OAuth Support for Admin Control Panel."
$returnVal = $false
}
}
if ($returnVal -eq $false)
{
break
}
}
}
return $returnVal
}
function GetEditedFileContentForWebConfig($disableAdfsOAuth, $relativePath, $macpRegistryPath, $adfsClientId, $adfsMetadataPublicUri, $adfsIssuerName, $adfsOAuthInstance)
{
$installDir = Get-ItemPropertyValue -Path $macpRegistryPath -Name "InstallDir"
$filePath = Join-Path -Path $installDir -ChildPath $relativePath
$fileContent = Get-Content -Path $filePath
# We handle the case of replace the vdirAuth for web.config.
# If the search string is not found or more than one instance is found we simply skip.
$webConfigVdirSearchStr = $fileContent -match "<vdirAuth authType(.*)"
if ($webConfigVdirSearchStr.Length -eq 1)
{
$searchVal = $webConfigVdirSearchStr[0]
$searchedStrTrimmed = $searchVal.Trim()
$adfsOAuthLineMatch = $fileContent -match "<adfsOAuth (.*)"
if ($adfsOAuthLineMatch.Length -eq 1)
{
$adfsOAuthStr = $adfsOAuthLineMatch[0]
$fileContent = $fileContent.Replace($adfsOAuthStr, '')
}
if ($disableAdfsOAuth -ne $true)
{
# Falling from WebTicket -> ADFSOnPrem,
# Also adding the tokenized adfsOAuth settings entry.
$countOfWhiteSpacesNeeded = $searchVal.Length - $searchedStrTrimmed.Length + 2
$replaceStr = "<vdirAuth authType=`"ADFSOnPrem`">"
$replaceStr = $replaceStr + "`r`n" + $(" " * $countOfWhiteSpacesNeeded) + `
"<adfsOAuth adfsMetadataPublicUri=`"$adfsMetadataPublicUri`" adfsClientId=`"$adfsClientId`" adfsIssuerName=`"$adfsIssuerName`" />"
}
else
{
# Falling back from ADFSOnPrem -> WebTicket,
# Removing the adfsOAuth settings entry.
$replaceStr = "<vdirAuth authType=`"WebTicket`">"
}
$fileContent = $fileContent.Replace($searchedStrTrimmed, $replaceStr)
}
# Removing all empty lines
$fileContent = $fileContent.Where({$_ -ne ''})
return $fileContent
}
function GetEditedFileContentForIndexAspx($disableAdfsOAuth, $relativePath, $macpRegistryPath, $adfsClientId, $adfsOAuthInstance, $simplifiedUrlPrefix)
{
$installDir = Get-ItemPropertyValue -Path $macpRegistryPath -Name "InstallDir"
$filePath = Join-Path -Path $installDir -ChildPath $relativePath
$fileContent = Get-Content -Path $filePath
$patternString = '${{{0}}}'
# Handling replacements back to tokens in index.aspx file
$searchedStr = $fileContent -match "`"instance`": (.*)"
if ($searchedStr.Length -gt 0)
{
foreach ($searchVal in $searchedStr)
{
$matchVal = $searchVal.ToLower().Contains("microsoftonline")
if ($matchVal -eq $false)
{
$newReplacementValue = $patternString -f "AdfsOAuthInstance"
$newStr = "`"instance`": `"$newReplacementValue`","
$fileContent = $fileContent.Replace($searchVal.Trim(), $newStr)
break
}
}
}
$searchedStr = $fileContent -match "`"clientId`": (.*)"
if ($searchedStr.Length -eq 1)
{
$newReplacementValue = $patternString -f "AdfsClientId"
$newStr = "`"clientId`": `"$newReplacementValue`","
$fileContent = $fileContent.Replace($searchedStr[0].Trim(), $newStr)
}
$searchedStr = $fileContent -match "`"adfsOAuthEnabled`": (.*)"
if ($searchedStr.Length -eq 1)
{
$newReplacementValue = $patternString -f "AdfsOAuthEnabled"
$newStr = "`"adfsOAuthEnabled`": `"$newReplacementValue`","
$fileContent = $fileContent.Replace($searchedStr[0].Trim(), $newStr)
}
$searchedStr = $fileContent -match "`"simplifiedUrlPrefix`": (.*)"
if ($searchedStr.Length -eq 1)
{
$newReplacementValue = $patternString -f "SimplifiedUrlPrefix"
$newStr = "`"simplifiedUrlPrefix`": `"$newReplacementValue`","
$fileContent = $fileContent.Replace($searchedStr[0].Trim(), $newStr)
}
$adfsOAuthEnabled = $false
if ($disableAdfsOAuth -ne $true)
{
$fileContent = $fileContent.Replace($patternString -f "AdfsClientId", $adfsClientId)
$fileContent = $fileContent.Replace($patternString -f "AdfsOAuthInstance", $adfsOAuthInstance)
$fileContent = $fileContent.Replace($patternString -f "SimplifiedUrlPrefix", $simplifiedUrlPrefix)
$adfsOAuthEnabled = $true
}
$fileContent = $fileContent.Replace($patternString -f "AdfsOAuthEnabled", $adfsOAuthEnabled)
# Removing all empty lines
$fileContent = $fileContent.Where({$_ -ne ''})
return $fileContent
}
function GenerateHttpsPrefix($inputVal)
{
if ($inputVal.StartsWith('http://'))
{
$inputVal = $inputVal.Replace('http://', '')
}
if (!$inputVal.StartsWith('https://'))
{
$inputVal = "https://" + $inputVal
}
return $inputVal
}
function SetupOAuthOnFrontEndPools($cred, $pools, $macpRegistryPath, $disableAdfsOAuth, $relativePathToContentMap, $adfsClientId, $adfsMetadataPublicUri, $adfsIssuerName, $adfsOAuthInstance)
{
foreach ($poolName in $pools)
{
$computers = (Get-CsComputer -Pool $poolName).Identity
foreach ($computer in $computers)
{
$psSession = New-PSSession -computerName $computer -credential $cred
Invoke-Command -Session $psSession `
-ArgumentList $macpRegistryPath, $disableAdfsOAuth, $relativePathToContentMap, $adfsClientId, `
$adfsMetadataPublicUri, $adfsIssuerName, $adfsOAuthInstance `
-ScriptBlock {
param($macpRegistryPath, $disableAdfsOAuth, $relativePathToContentMap, $adfsClientId, $adfsMetadataPublicUri, $adfsIssuerName, $adfsOAuthInstance)
$installDir = Get-ItemPropertyValue -Path $macpRegistryPath -Name "InstallDir"
foreach ($val in $relativePathToContentMap.GetEnumerator())
{
$relativePath = $($val.Name)
$fileContent = $($val.Value)
$filePath = Join-Path -Path $installDir -ChildPath $relativePath
Set-Content -Path $filePath -Value $fileContent
}
$adfsOAuthEnabled = $true
if ($disableAdfsOAuth -eq $true)
{
$adfsOAuthEnabled = $false
}
Set-ItemProperty -Path $macpRegistryPath -Name "AdfsOAuthEnabled" -Value $adfsOAuthEnabled
Set-ItemProperty -Path $macpRegistryPath -Name "AdfsClientId" -Value $adfsClientId
Set-ItemProperty -Path $macpRegistryPath -Name "AdfsMetadataPublicUri" -Value $adfsMetadataPublicUri
Set-ItemProperty -Path $macpRegistryPath -Name "AdfsIssuerName" -Value $adfsIssuerName
Set-ItemProperty -Path $macpRegistryPath -Name "AdfsOAuthInstance" -Value $adfsOAuthInstance
# Changing the application pool of Macp app to make it same as that of Skype for Business Server Web Site. This is needed for URL rewrite to work in case of Simple URL.
# This is important to note that this change is required only for the Simple URL feature. However, we chose to make this change whenever ADFS is configured.
# This change may not be reverted when we fall back to WebTicket. And that is alright.
Import-Module WebAdministration
Set-ItemProperty -Path 'IIS:\Sites\Skype for Business Server Internal Web Site\Macp' -Name applicationPool -Value LyncIntFeature
Set-ItemProperty -Path 'IIS:\Sites\Skype for Business Server External Web Site\Macp' -Name applicationPool -Value LyncExtFeature
iisreset
}
Remove-PSSession -Session $psSession
}
}
}
# Script execution start
$disableAdfsOAuth = $null
$adfsOAuthInstance = $null
$adfsClientId = $null
$adfsMetadataPublicUri = $null
$adfsIssuerName = $null
$simplifiedUrlPrefix = $null
$pools = $null
$mode = Read-Host -Prompt "Do you want to Enable [e] or Disable [d] ADFS OAuth for Admin Control Panel. Confirm[e/d]"
if([string]::IsNullOrWhiteSpace($mode))
{
$mode = 'e'
}
if ($mode -match '^[eE]$')
{
$disableAdfsOAuth = $false
Write-Host "`n`rStarting script to enable ADFS OAuth." -ForeGroundColor Green
$adfsOAuthInstance = Read-Host -Prompt "Enter the ADFS farm instance (FQDN) where the Admin Control Panel is registered for ADFS OAuth"
$adfsOAuthInstance = GenerateHttpsPrefix -input $adfsOAuthInstance
# Make sure that instance name has a trailing slash
$adfsOAuthInstance = $adfsOAuthInstance.TrimEnd('/') + '/'
$defaultAdfsMetadataPublicUri = $adfsOAuthInstance + 'FederationMetadata/2007-06/FederationMetadata.xml'
$adfsMetadataPublicUri = Read-Host -Prompt "Enter OAuth Metadata Public URI for the Admin Control Panel in the ADFS. Press ENTER to use default $defaultAdfsMetadataPublicUri "
if([string]::IsNullOrWhiteSpace($adfsMetadataPublicUri))
{
$adfsMetadataPublicUri = $defaultAdfsMetadataPublicUri
}
else
{
$adfsMetadataPublicUri = GenerateHttpsPrefix -input $adfsMetadataPublicUri
}
$adfsIssuerNameDefault = $adfsOAuthInstance.TrimEnd('/') + '/adfs'
$adfsIssuerName = Read-Host -Prompt "Enter Issuer Name for the Admin Control Panel in the ADFS. Press ENTER to use default $adfsIssuerNameDefault"
if([string]::IsNullOrWhiteSpace($adfsIssuerName))
{
$adfsIssuerName = $adfsIssuerNameDefault
}
$adfsClientId = Read-Host -Prompt "Enter Client Identifier of the application created for the Admin Control Panel in the ADFS"
if([string]::IsNullOrWhiteSpace($adfsClientId))
{
Write-Host "`n`rClient identifier cannot be empty" -ForeGroundColor Red
Write-Host "`n`rAborting Process." -ForeGroundColor Red
return
}
$simplifiedUrlPrefix = Read-Host -Prompt "Enter the Simple URL prefix for the Admin Control Panel (if configured). Press ENTER to use default 'admin.' "
if([string]::IsNullOrWhiteSpace($simplifiedUrlPrefix))
{
$simplifiedUrlPrefix = 'admin.'
}
}
elseif ($mode -match '^[dD]$')
{
$disableAdfsOAuth = $true
Write-Host "`n`rStarting script to disable ADFS OAuth."-ForeGroundColor Green
}
else
{
Write-Host "`n`rInvalid Input." -ForeGroundColor Red
Write-Host "`n`rAborting Process." -ForeGroundColor Red
return
}
$pool = Read-Host -Prompt "Enter pool names in which ADFS OAuth is to be setup for the admin control panel in a comma separated format. Press ENTER to setup OAuth for all FE pools deployed with Skype for Business 2019"
# This is the registry path for Admin Control Panel for SfB 2019 machines.
$macpRegPath = "HKLM:\SOFTWARE\Microsoft\Real-Time Communications\{D00E3324-D7F8-4735-B4CF-206FE63FD577}"
# This is the min version string above which ADFS OAuth is enabled for Admin Control Panel.
$cu3VersionStr = "7.0.2046.216"
$poolArray=@()
$computerDetails = Get-WmiObject win32_computersystem
if (($computerDetails -eq $null) -OR
($computerDetails.DNSHostName -eq $null) -OR
($computerDetails.Domain -eq $null))
{
# Unable to find computer details.
# We will return false in that case.
Write-Error "Unable to find host and domain details. Aborting process."
return
}
if ($pools -eq $null)
{
$poolName = GetAllSfBW17Pools
if ($poolName.length -eq 0)
{
Write-Error "Error generating list of FE pool FQDN. Aborting process."
return
}
$poolArray += ($poolName)
}
else
{
$poolArray = GeneratePoolNameArray -pools $pools -computerDetails $computerDetails
if ($poolArray.length -eq 0)
{
Write-Error "Error parsing pool names."
return
}
}
$status = VerifyRoleAndVersionOfSelectedPools -poolArray $poolArray
if ($status -eq $false)
{
Write-Error "Not all the pools mentioned are Front end pool or deployed with SfB 2019. Aborting process."
return
}
Write-Host "`n`rStarting modification to all the servers in Skype for Business 2019 FE pools (or Pools mentioned using -Pools parameter). Please provide Administrator credentials."
$cred = Get-Credential
if ($cred -eq $null)
{
Write-Error "Credential is not defined. Aborting process."
return
}
if ($disableAdfsOAuth.Equals($false))
{
$adfsOAuthInstance = GenerateHttpsPrefix -input $adfsOAuthInstance
$adfsMetadataPublicUri = GenerateHttpsPrefix -input $adfsMetadataPublicUri
$adfsIssuerName = GenerateHttpsPrefix -input $adfsIssuerName
# Removing any trailing slash from the issuer name
$adfsIssuerName = $adfsIssuerName.TrimEnd('/')
# Make sure that instance name has a trailing slash
$adfsOAuthInstance = $adfsOAuthInstance.TrimEnd('/') + '/'
}
$status = VerifyMacpVersionOnFrontEndPools -cred $cred -pools $poolArray -macpRegistryPath $macpRegPath -minVersion $cu3VersionStr
if ($status -eq $false)
{
Write-Error "Not all the pools have the required CU installed for MacpWebComponents. Please update to the latest version. Aborting process."
return
}
$internalOcsPswsPath = "Web Components\OcsPsws\Int\"
$externalOcsPswsPath = "Web Components\OcsPsws\Ext\"
$internalMacpPath = "Web Components\Macp\Int\"
$externalMacpPath = "Web Components\Macp\Ext\"
$ocsPswsInternalWebConfigPath = Join-Path -Path $internalOcsPswsPath -ChildPath "web.config"
$ocsPswsExternalWebConfigPath = Join-Path -Path $externalOcsPswsPath -ChildPath "web.config"
$macpInternalIndexAspxPath = Join-Path -Path $internalMacpPath -ChildPath "index.aspx"
$macpExternalIndexAspxPath = Join-Path -Path $externalMacpPath -ChildPath "index.aspx"
$internalOcsPswsWebConfigContent = GetEditedFileContentForWebConfig -disableAdfsOAuth $disableAdfsOAuth -relativePath $ocsPswsInternalWebConfigPath `
-macpRegistryPath $macpRegPath -adfsClientId $adfsClientId -adfsMetadataPublicUri $adfsMetadataPublicUri -adfsIssuerName $adfsIssuerName -adfsOAuthInstance $adfsOAuthInstance
$externalOcsPswsWebConfigContent = GetEditedFileContentForWebConfig -disableAdfsOAuth $disableAdfsOAuth -relativePath $ocsPswsExternalWebConfigPath `
-macpRegistryPath $macpRegPath -adfsClientId $adfsClientId -adfsMetadataPublicUri $adfsMetadataPublicUri -adfsIssuerName $adfsIssuerName -adfsOAuthInstance $adfsOAuthInstance
$internalIndexAspxContent = GetEditedFileContentForIndexAspx -disableAdfsOAuth $disableAdfsOAuth -relativePath $macpInternalIndexAspxPath `
-macpRegistryPath $macpRegPath -adfsClientId $adfsClientId -adfsOAuthInstance $adfsOAuthInstance -simplifiedUrlPrefix $simplifiedUrlPrefix
$externalIndexAspxContent = GetEditedFileContentForIndexAspx -disableAdfsOAuth $disableAdfsOAuth -relativePath $macpExternalIndexAspxPath `
-macpRegistryPath $macpRegPath -adfsClientId $adfsClientId -adfsOAuthInstance $adfsOAuthInstance -simplifiedUrlPrefix $simplifiedUrlPrefix
$finalRelativePathToContentMap = @{}
$key = Join-Path -Path $internalOcsPswsPath -ChildPath "web.config"
$finalRelativePathToContentMap[$key] = $internalOcsPswsWebConfigContent
$key = Join-Path -Path $externalOcsPswsPath -ChildPath "web.config"
$finalRelativePathToContentMap[$key] = $externalOcsPswsWebConfigContent
$key = Join-Path -Path $internalMacpPath -ChildPath "index.aspx"
$finalRelativePathToContentMap[$key] = $internalIndexAspxContent
$key = Join-Path -Path $externalMacpPath -ChildPath "index.aspx"
$finalRelativePathToContentMap[$key] = $externalIndexAspxContent
SetupOAuthOnFrontEndPools -cred $cred -pools $poolArray -macpRegistryPath $macpRegPath `
-disableAdfsOAuth $disableAdfsOAuth -relativePathToContentMap $finalRelativePathToContentMap -adfsClientId $adfsClientId `
-adfsMetadataPublicUri $adfsMetadataPublicUri -adfsIssuerName $adfsIssuerName -adfsOAuthInstance $adfsOAuthInstance
if ($disableAdfsOAuth -eq $true)
{
Write-Host "`n`rADFS OAuth has been disabled for the admin control panel." -ForeGroundColor Green
}
else
{
Write-Host "`n`rADFS OAuth has been enabled for the admin control panel." -ForeGroundColor Green
}
Skype for Business Server Management Shell에서 해당 Script를 실행합니다.
Do you want to Enable [e] or Disable [d] ADFS OAuth for Admin Control Panel. Confirm[e/d]
관리자 제어판에 대해 [e]를 활성화하거나 [d] ADFS OAuth를 비활성화하시겠습니까? 확인[e/d]
-> e 입력
Enter the ADFS farm instance (FQDN) where the Admin Control Panel is registered for ADFS OAuth
관리자 제어판이 ADFS OAuth에 등록된 ADFS 팜 인스턴스(FQDN)를 입력합니다.
-> ADFS 페이지 URL을 입력합니다.
Enter OAuth Metadata Public URI for the Admin Control Panel in the ADFS. Press ENTER to use default https://sts.contoso.kr/FederationMetadata/2007-06/FederationMetadata.xml
-> 엔터를 입력합니다.
Enter Issuer Name for the Admin Control Panel in the ADFS. Press ENTER to use default https://sts.contoso.kr/adfs:
-> 엔터를 입력합니다.
Enter Client Identifier of the application created for the Admin Control Panel in the ADFS:
-> ADFS에서 생성한 MACPApp의 Client ID를 입력합니다.
아래의 위치에서 확인할 수 있습니다.
Enter the Simple URL prefix for the Admin Control Panel (if configured). Press ENTER to use default 'admin.' :
-> 엔터를 입력합니다.
Enter pool names in which ADFS OAuth is to be setup for the admin control panel in a comma separated format. Press ENTER to setup OAuth for all FE pools deployed with Skype for Business 2019:
관리자 제어판에 대해 ADFS OAuth를 설정할 풀 이름을 쉼표로 구분된 형식으로 입력합니다. 비즈니스용 Skype 2019와 함께 배포된 모든 FE 풀에 대해 OAuth를 설정하려면 Enter 키를 누릅니다.
-> 엔터를 입력합니다.
ADFS App에 등록된 URL로 브라우저에 접속합니다.
https://pool.corp.contoso.kr/macp/
ADFS login창이 나타납니다.
로그인 완료
만약 제 환경처럼 Internal, External URL을 별도로 설정했다면 ADFS App의 Redirect URI에서 아래와 같이 3개의 URL 형태를 추가하면 동일하게 접속할 수 있습니다.
https://skypeint.corp.contoso.kr/macp/portal_oauth_iframe.html https://skypeint.corp.contoso.kr/macp/login https://skypeint.corp.contoso.kr/macp/logout |
추가된 URL 로 시도하여 확인
III. Access Control Policy 설정 추가를 위한 Web API 추가
여기서 만약 Azure MFA 와 같은 Access Control Policy를 사용하려면 ADFS 에서 몇가지 수정 작업이 필요합니다.
일반 클레임 인증 설정과 다르게 Native Application 으로 생성하면 Access Control Policy를 지정하는 메뉴가 없습니다.
스크립트 상에서는 아래의 부분에 해당하며
만약 ADFS 관리 메뉴에서 생성한다면 Add Application Group
Native Application에 해당합니다.
아래는 Native Application을 생성시 진행되는 절차입니다.
만약 Web API 관련 메뉴를 선택하면
아래와 같이 지정할 수 있는 형태가 됩니다.
아래의 글에 관련 내용이 나와 있어서 참고하여 Web API를 추가하는 방법으로 진행해 보도록 하겠습니다.
Skype for Business MACP, AD FS et Home Realm Discovery – Stefan Plizga
아래의 메뉴에서 Add applicatoin
Web API -> Next
MACP 의 Client ID를 추가합니다.
Access Control Policy 지정 -> Next
Next
Next
Close
설정 완료후 로그인 시도 (AAD Sync가 되어 있는 서비스 계정을 입력합니다.)
MFA 요청이 진행
실제 제 모바일에 아래와 같이 MFA 창이 인증창이 확인되었습니다.
MACP 페이지를 서버가 아닌 내부에서 접속시 ADFS 를 통해서 2단계 인증까지 진행할 수 있음을 확인하였습니다.