问题
With the following DSC configuration (snippet of the resources section only, cut out preamble, diagnostics, and Start-DscConfiguration parts for SO):
Package TFSServer2013 {
Name = "Microsoft Team Foundation Server 2013 Update 3 (x64)"
Path = '$InstallerPath\Team Foundation Server 2013\tfs_server.exe'
Arguments = "/quiet"
Credential = $Credential
Ensure = "Present"
ProductID = "B516BA7C-3C20-3FCC-B130-B42058ABF87A"
}
File TFSUserFile {
DestinationPath = "$Env:TEMP\TFSConfig\TfsUserName.clixml"
Attributes = "Hidden"
SourcePath = $TFSUsernameFile
Ensure = "Present"
Force = $true
Credential = $Credential
Type = "File"
MatchSource = $true
}
File TFSPasswordFile {
DestinationPath = "$Env:TEMP\TFSConfig\TfsUserPassword.clixml"
Attributes = "Hidden"
SourcePath = $TFSUserPasswordFile
Ensure = "Present"
Force = $true
Credential = $Credential
Type = "File"
MatchSource = $true
}
File TfsBuildAgentConfigureScript {
DestinationPath = "$LocalInstallerPath\PowerShell\Configure-TfsBuildService.ps1"
SourcePath = "$POSModulePath\Configure-TfsBuildService.ps1"
Ensure = "Present"
Force = $true
Credential = $Credential
Type = "File"
MatchSource = $true
}
Script TFSConfigure {
SetScript = "
. C:\BuildAgent\Installers\PowerShell\Configure-TfsBuildService.ps1
Setup-TfsBuildService -ConfigIniFile 'C:\BuildAgent\Logs\TfsConfig.ini' -TfsBuildControllerName 'TFSMASTER' -TfsServiceUri 'http://mytfsservice:8080/tfs/My_Collection' -TfsUsernameClixml ""`$Env:TEMP\TFSConfig\TfsUserName.clixml"" -LogFilesPath 'C:\BuildAgent\Logs' -TfsUserPasswordClixml ""`$Env:TEMP\TFSConfig\TfsUserPassword.clixml""
if (`$false) { Remove-Item -Force -Path ""$Env:TEMP\TFSConfig\TfsUserPassword.clixml"" }
"
TestScript = "
New-EventLog -LogName 'Windows Powershell' -Source DSC -ErrorAction SilentlyContinue
Write-EventLog -LogName 'Windows PowerShell' -Source DSC -EntryType Information -EventId 1 -Message ""Testing if TFS build service is up and running.""
`$srv = Get-WmiObject -Class Win32_Service | Where-Object -FilterScript { (`$PSItem.Name -ne `$null) -and (`$PSItem.Name.Equals(""TFSBuildServiceHost.2013"")) }
return (`$srv -ne `$null)
"
GetScript = "
New-EventLog -LogName 'Windows Powershell' -Source DSC -ErrorAction SilentlyContinue
`$ensure='Absent'
if (Test-Path 'C:\BuildAgent\Logs\TfsConfig.ini' -ErrorAction Ignore) { `$ensure = 'Present' }
Write-EventLog -LogName 'Windows PowerShell' -Source DSC -EntryType Information -EventId 1 -Message ""TFSConfigure GetScript (C:\BuildAgent\Logs\TfsConfig.ini): Ensure=`$ensure""
@{Ensure=`$ensure}
"
Credential = $Credential
DependsOn = "[File]TFSPasswordFile","[File]TFSUserFile","[Package]TFSServer2013","[File]TfsBuildAgentConfigureScript"
}
My script to configure the TFS Build Service in unattended mode is as follows (I've taken out logging, error checking, and diagnostics for the purpose of SO):
Function Setup-TfsBuildService
{
Param(
[Parameter(Mandatory=$true)]
[string]
$ConfigIniFile,
[Parameter(Mandatory=$true)]
[string]
$TfsBuildControllerName,
[Parameter(Mandatory=$true)]
[string]
$TfsServiceUri,
[Parameter(Mandatory=$true)]
[string]
$TfsUsernameClixml,
[Parameter(Mandatory=$true)]
[string]
$LogFilesPath,
[Parameter(Mandatory=$true)]
[string]
$TfsUserPasswordClixml,
[Parameter()]
[string]
$TfsConfigExePath="$Env:ProgramFiles\Microsoft Team Foundation Server 12.0\Tools\TfsConfig.exe"
)
if (Test-Path -Path $TfsUsernameClixml)
{
$tfsuser = (Import-CliXml -Path $TfsUsernameClixml)
}
else { return }
Remove-Item -Force -Path $ConfigIniFile -ErrorAction SilentlyContinue
# Stop any existing TFS 2013 build service
if (Get-Service -Name "TFSBuildServiceHost.2013" -ErrorAction Ignore)
{
$srv = Get-WmiObject -Class Win32_Service | Where-Object -FilterScript { ($PSItem.Name -ne $null) -and ($PSItem.Name.Equals("TFSBuildServiceHost.2013")) }
$srv.StopService() | Out-Null
$srv.Delete() | Out-Null
}
# Create the unattend file:
$inputparams = "ConfigurationType=scale;AgentCount=1;ExistingControllerName=$TfsBuildControllerName;CleanResources=True;CollectionUrl=$TfsServiceUri;IsServiceAccountBuiltIn=False;ServiceAccountName=$tfsuser"
$tfsconfoutput = & $TfsConfigExePath unattend /create /type:build /unattendfile:"$ConfigIniFile" /inputs:$inputparams 2>&1
$tfsconfoutput | Out-File -FilePath (Join-Path $LogFilesPath "TfsConfigUnattendFile.log") -Force
# Install the service:
$Error.Clear()
$tfsuserpswd = (Import-CliXml -Path $TfsUserPasswordClixml)
$tfsconfoutput = & $TfsConfigExePath unattend /configure /unattendfile:"$ConfigIniFile" /inputs:"ServiceAccountPassword=$tfsuserpswd" /continue 2>&1
Remove-Variable tfsuserpswd
$tfsconfoutput | Out-File -FilePath (Join-Path $LogFilesPath "TfsInstallUnattend.log") -Force
}
I can run the script Configure-TfsBuildService.ps1 successfully on any of my build Windows 2012 R2 agents successfully as long as I'm logged in locally.
Apart from the TFS Build Service configuration block, I can run my DSC configuration successfully against all of my build agents.
However, when I attempt to run my TFS Build Service configuration block, my DSC configuration 'succeeds' however the unattended configuration of the service fails with the following log message:
[Info @15:40:47.754] +-+-+-+-+-| Verifying that the running account has required Team Foundation Server permissions |+-+-+-+-+-
[Info @15:40:47.754] Starting Node: TBPERMISSIONS
[Info @15:40:47.754] NodePath : VINPUTS/Progress/Conditional/TBPERMISSIONS
[Error @15:40:47.920]
Exception Message: TF30063: You are not authorized to access http://mytfsservice:8080/tfs/My_Collection. (type TeamFoundationServerUnauthorizedException)
Exception Stack Trace: at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest()
at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage message, TimeSpan timeout)
at Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation operation, Object[] parameters, TimeSpan timeout, Object[]& outputs)
at Microsoft.TeamFoundation.Framework.Client.LocationWebService.Connect(Int32 connectOptions, Int32 lastChangeId, Int32 features)
at Microsoft.TeamFoundation.Framework.Client.FrameworkServerDataProvider.Connect(ConnectOptions connectOptions)
at Microsoft.TeamFoundation.Admin.AuthenticatedCollectionProvider.Microsoft.TeamFoundation.Admin.IAuthenticatedCollectionProvider.GetAuthenticatedConnection()
at Microsoft.TeamFoundation.Admin.VerifyPermissionsToConfigure.Run(ActivityContext context)
Inner Exception Details:
Exception Message: The remote server returned an error: (401) Unauthorized. (type WebException)Status: ProtocolError
Response Status Code: Unauthorized
Response Status Message: Unauthorized
Exception Stack Trace: at System.Net.HttpWebRequest.GetResponse()
at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequestAndGetResponse(HttpWebRequest webRequest, WebException& webException)
[Info @15:40:47.920] Node returned: Error
[Error @15:40:47.920] TF30063: You are not authorized to access http://mytfsservice:8080/tfs/My_Collection.
[Info @15:40:47.920] Completed BuildServicePermissions: Error
[Info @15:40:47.920] -----------------------------------------------------
Since I am running the DSC configuration with my own credentials, and I am a Project Collection Administrator on the TFS service, there is no issue with permissions. I've proven that by successfully running the configuration script locally on an agent machine and being successful there.
I realize that there is no double-hop permissions allowed with PowerShell, but since DSC executes on the agent involved, there shouldn't be an issue of double-hop credentials and the permissions would be allowed to be relayed to the TFS service for registration.
Maybe I've missed something more trivial?
EDIT: A description of unattended installation of TFS 2012/2013 is found here: http://blogs.msdn.com/b/visualstudioalm/archive/2012/10/12/unattended-installation-of-team-foundation-server-2012.aspx
回答1:
You may still be running into double-hop issues. DSC requires WinRM, even when run in Push mode, because no matter what it's "remoting" into the machine when it executes the configuration. That still counts as a hop.
Consider these 2 executions:
$sb = { Get-Content \\server\share\file.txt }
Invoke-Command -ScriptBlock $sb
Invoke-Command -ComputerName . -ScriptBlock $sb
The first invocation should work (no remoting is being done with that parameter set). The second should fail with a permission error.
来源:https://stackoverflow.com/questions/26304425/why-can-i-not-configure-tfs-build-service-in-unattend-mode-via-powershell-dsc