반응형

지난 포스팅에서는 MACP 무엇인지를 소개해 드렸습니다.

2023.04.09 - [Lync & SFB & Team] - Skype for business Server 2019. MACP (Modern Admin Control Panel)

 

이번에는 MACP 사이트를 ADFS와의 클레임 인증 설정에 대해서 다루고자 합니다.

아래의 기술자료에 나와 있는 스크립트를 기준으로 진행합니다.

Skype for Business Server 2019 control panel authentication script - Skype for Business Server 2019 | Microsoft Learn

 

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

ADFS Server에서 실행 확인

 

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를 입력합니다.

App Name 지정

 

 

Enter Admin Control Panel Application Name. Press ENTER to use default name as 'MACPApp'

관리자 제어판 애플리케이션 이름을 입력합니다. 기본 이름을 'MACPApp'으로 사용하려면 Enter 키를 누르세요.

Application Name 지정

 

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 입력

AD Domain Name

 

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가 작동하지 않습니다.

-> 외부에서 사용하는 도메인을 입력합니다.

External Domain 지정

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 메뉴에서 생성된 정보를 확인할 있습니다.

MACP Application Group

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 입력

Enable ADFS OAuth

 

 

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:

-> 엔터를 입력합니다.

ADFS Issuer Name 확인

 

Enter Client Identifier of the application created for the Admin Control Panel in the ADFS:

-> ADFS에서 생성한 MACPApp의 Client ID를 입력합니다.

아래의 위치에서 확인할 수 있습니다.

Client ID

 

 

Enter the Simple URL prefix for the Admin Control Panel (if configured). Press ENTER to use default 'admin.' :

-> 엔터를 입력합니다.

Simple URL 지정

 

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 키를 누릅니다.

-> 엔터를 입력합니다.

설정 변경 서버 지정

 

서비스 계정 입력

 

Script 진행 완료

 

 

ADFS App 등록된 URL 브라우저에 접속합니다.

https://pool.corp.contoso.kr/macp/

MACP Page

 

ADFS login창이 나타납니다.

ADFS Login

 

로그인 완료

Log in 성공

 

만약 환경처럼 Internal, External URL 별도로 설정했다면 ADFS App Redirect URI에서 아래와 같이 3개의 URL 형태를 추가하면 동일하게 접속할 있습니다.

 

Redirect URI 추가

 

추가된 URL 로 시도하여 확인

추가 URL 페이지

 

III. Access Control Policy 설정 추가를 위한 Web API 추가

여기서 만약 Azure MFA 같은 Access Control Policy를 사용하려면 ADFS 에서 몇가지 수정 작업이 필요합니다.

일반 클레임 인증 설정과 다르게 Native Application 으로 생성하면 Access Control Policy를 지정하는 메뉴가 없습니다.

Native Application 화면

 

스크립트 상에서는 아래의 부분에 해당하며

MACP ADFS Script

 

만약 ADFS 관리 메뉴에서 생성한다면 Add Application Group

Add Applicatoin Group

 

Native Application 해당합니다.

Native application

 

아래는 Native Application 생성시 진행되는 절차입니다.

Native Application 진행 절차

 

만약 Web API 관련 메뉴를 선택하면

web API 예시1

아래와 같이 지정할 있는 형태가 됩니다.

web API 예시2

 

아래의 글에 관련 내용이 나와 있어서 참고하여 Web API 추가하는 방법으로 진행해 보도록 하겠습니다.

Skype for Business MACP, AD FS et Home Realm Discovery – Stefan Plizga

 

아래의 메뉴에서 Add applicatoin

Add application

 

Web API -> Next

Web API

 

MACP Client ID 추가합니다.

Client ID 추가

 

Access Control Policy 지정 -> Next

Apply Access Control Policy

 

Next

Configure Application Permissions

Next

Summary

 

Close

Complete

 

설정 완료후 로그인 시도 (AAD Sync가 되어 있는 서비스 계정을 입력합니다.)

ADFS Login

 

MFA 요청이 진행

MFA 요청

 

실제 제 모바일에 아래와 같이 MFA 창이 인증창이 확인되었습니다.

ADFS MFA 진행

 

MACP 페이지를 서버가 아닌 내부에서 접속시 ADFS 를 통해서 2단계 인증까지 진행할 수 있음을 확인하였습니다.

 

반응형

+ Recent posts