通过脚本更新 Jenkins 凭据
update Jenkins credentials by script
我在 Windows 上有一个 Jenkins 服务器 运行ning。它在凭据插件中存储 username:password。这是一个定期更新密码的服务用户。
我正在寻找一种方法来 运行 一个脚本,最好是 Powershell,它将更新 Jenkins 密码存储中的凭据,这样当我在构建作业脚本中使用它时它总是最新的.
密码由 Thycotic Secret Server 安装管理,因此我应该能够自动更新此密码,但我几乎找不到如何完成此操作的线索,即使 [=写凭证 api 的人的 11=] 几乎完全提到了这种情况,然后继续 link 到凭证插件的下载页面,其中没有说明如何实际使用 api .
更新
已接受的答案完美无缺,但其余方法调用示例使用的是 curl,如果您使用的是 windows,则帮助不大。特别是如果您尝试调用 REST URL 但您的 Jenkins 服务器正在使用 AD 集成。为此,您可以使用以下脚本。
通过转到人员 > 用户 > 配置 > 显示 API 令牌找到用户 ID 和 API 令牌。
$user = "UserID"
$pass = "APIToken"
$pair = "${user}:${pass}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ Authorization = $basicAuthValue }
Invoke-WebRequest `
-uri "http://YourJenkinsServer:8080/scriptler/run/changeCredentialPassword.groovy?username=UrlEncodedTargetusername&password=URLEncodedNewPassword" `
-method Get `
-Headers $headers
Jenkins 支持使用 Groovy 语言编写脚本。你可以获得一个 scripting console by opening in a browser the URL /script
of your Jenkins instance. (i.e: http://localhost:8080/script)
Groovy 语言(相对于 powershell 或其他任何语言)的优势在于那些 Groovy 脚本在 Jenkins 中执行并且可以访问所有内容(配置、插件、作业等) .
然后以下代码会将用户 'BillHurt' 的密码更改为 's3crEt!':
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, c.id, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword('BillHurt', 's3crEt!')
经典自动化(/scriptText
)
要自动执行此脚本,您可以将其保存到一个文件(比方说 /tmp/changepassword.groovy
)和 运行 以下 curl 命令:
curl -d "script=$(cat /tmp/changepassword.groovy)" http://localhost:8080/scriptText
应该以 HTTP 200
状态和文本响应:
found credential 801cf176-3455-4b6d-a461-457a288fd202 for username BillHurt
password changed for BillHurt
使用 Scriptler 插件实现自动化
您也可以安装 Jenkins Scriptler plugin 并进行如下操作:
- 在侧面菜单中打开 Scriptler 工具
- 填写第 3 个字段,注意将 Id 字段设置为
changeCredentialPassword.groovy
- 选中定义脚本参数复选框
- 添加 2 个参数:
username
和 password
- 粘贴以下脚本:
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
jenkins.model.Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, null, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword("$username", "$password")
- 然后单击提交按钮
现在您可以调用以下URL来更改密码(替换username
和password
参数):http://localhost:8080/scriptler/run/changeCredentialPassword.groovy?username=BillHurt&password=s3crEt%21 (notice the need to urlencode参数值)
或卷曲:
curl -G http://localhost:8080/scriptler/run/changeCredentialPassword.groovy --data-urlencode 'username=BillHurt' --data-urlencode "password=s3crEt!"
来源:
- Printing a list of credentials and their IDs
- Create UserPrivateKeySource Credential via Groovy?
- credential plugin source code
- Scriptler plugin
搜索引擎提示:使用关键字 'Jenkins.instance.'
、'com.cloudbees.plugins.credentials'
和 UsernamePasswordCredentialsImpl
我从未找到让 Groovy 脚本停止更新凭据 ID 的方法,但我注意到如果我使用 Web 界面更新凭据,ID 并没有改变。
考虑到这一点,下面的脚本实际上将编写 Jenkins Web 界面的脚本来执行更新。
澄清一下,这很重要的原因是,如果您使用凭证绑定插件之类的东西在您的工作中使用凭证,将 ID 更新为凭证会破坏 link 和您的工作将失败。请原谅使用 $args 而不是 param() 因为这只是一个丑陋的黑客,我稍后会改进。
请注意向表单字段添加 json 有效负载。我发现在这种非常特殊的格式中这是必需的,否则表单提交将失败。
$username = $args[0]
$password = $args[1]
Add-Type -AssemblyName System.Web
#1. Log in and capture the session.
$homepageResponse = Invoke-WebRequest -uri http://Servername.domain.com/login?from=%2F -SessionVariable session
$loginForm = $homepageResponse.Forms['login']
$loginForm.Fields.j_username = $username
$loginForm.Fields.j_password = $password
$loginResponse = Invoke-WebRequest `
-Uri http://Servername.domain.com/j_acegi_security_check `
-Method Post `
-Body $loginForm.Fields `
-WebSession $session
#2. Get Credential ID
$uri = "http://Servername.domain.com/credential-store/domain/_/api/xml"
foreach($id in [string]((([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content)).domainWrapper.Credentials | Get-Member -MemberType Property).Name -split ' '){
$id = $id -replace '_',''
$uri = "http://Servername.domain.com/credential-store/domain/_/credential/$id/api/xml"
$displayName = ([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content).credentialsWrapper.displayName
if($displayName -match $username){
$credentialID = $id
}
}
#3. Get Update Form
$updatePage = Invoke-WebRequest -Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/update" -WebSession $session
$updateForm = $updatePage.Forms['update']
$updateForm.Fields.'_.password' = $password
#4. Submit Update Form
$json = @{"stapler-class" = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl";
"scope"="GLOBAL";"username"="domain$username";"password"=$password;"description"="";"id"=$id} | ConvertTo-Json
$updateForm.Fields.Add("json",$json)
Invoke-WebRequest `
-Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/updateSubmit" `
-Method Post `
-Body $updateForm.Fields `
-WebSession $session
决定写一个新的答案,虽然它基本上是对@Tomasleveil 的答案的一些更新:
- 删除已弃用的调用(感谢 jenkins wiki, for other listing options see plugin consumer guide)
- 添加一些评论
- 保留凭据 ID 以避免破坏现有作业
- 根据描述查找凭据,因为用户名很少如此唯一(reader 可以轻松将其更改为 ID 查找)
开始了:
credentialsDescription = "my credentials description"
newPassword = "hello"
// list credentials
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
// com.cloudbees.plugins.credentials.common.StandardUsernameCredentials to catch all types
com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials.class,
Jenkins.instance,
null,
null
);
// select based on description (based on ID might be even better)
cred = creds.find { it.description == credentialsDescription}
println "current values: ${cred.username}:${cred.password} / ${cred.id}"
// not sure what the other stores would be useful for, but you can list more stores by
// com.cloudbees.plugins.credentials.CredentialsProvider.all()
credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
// replace existing credentials with a new instance
updated = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
cred,
// make sure you create an instance from the correct type
new com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl(cred.scope, cred.id, cred.description, cred.username, newPassword)
)
if (updated) {
println "password changed for '${cred.description}'"
} else {
println "failed to change password for '${cred.description}'"
}
我在 Windows 上有一个 Jenkins 服务器 运行ning。它在凭据插件中存储 username:password。这是一个定期更新密码的服务用户。
我正在寻找一种方法来 运行 一个脚本,最好是 Powershell,它将更新 Jenkins 密码存储中的凭据,这样当我在构建作业脚本中使用它时它总是最新的.
密码由 Thycotic Secret Server 安装管理,因此我应该能够自动更新此密码,但我几乎找不到如何完成此操作的线索,即使 [=写凭证 api 的人的 11=] 几乎完全提到了这种情况,然后继续 link 到凭证插件的下载页面,其中没有说明如何实际使用 api .
更新
已接受的答案完美无缺,但其余方法调用示例使用的是 curl,如果您使用的是 windows,则帮助不大。特别是如果您尝试调用 REST URL 但您的 Jenkins 服务器正在使用 AD 集成。为此,您可以使用以下脚本。
通过转到人员 > 用户 > 配置 > 显示 API 令牌找到用户 ID 和 API 令牌。
$user = "UserID"
$pass = "APIToken"
$pair = "${user}:${pass}"
$bytes = [System.Text.Encoding]::ASCII.GetBytes($pair)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ Authorization = $basicAuthValue }
Invoke-WebRequest `
-uri "http://YourJenkinsServer:8080/scriptler/run/changeCredentialPassword.groovy?username=UrlEncodedTargetusername&password=URLEncodedNewPassword" `
-method Get `
-Headers $headers
Jenkins 支持使用 Groovy 语言编写脚本。你可以获得一个 scripting console by opening in a browser the URL /script
of your Jenkins instance. (i.e: http://localhost:8080/script)
Groovy 语言(相对于 powershell 或其他任何语言)的优势在于那些 Groovy 脚本在 Jenkins 中执行并且可以访问所有内容(配置、插件、作业等) .
然后以下代码会将用户 'BillHurt' 的密码更改为 's3crEt!':
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, c.id, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword('BillHurt', 's3crEt!')
经典自动化(/scriptText
)
要自动执行此脚本,您可以将其保存到一个文件(比方说 /tmp/changepassword.groovy
)和 运行 以下 curl 命令:
curl -d "script=$(cat /tmp/changepassword.groovy)" http://localhost:8080/scriptText
应该以 HTTP 200
状态和文本响应:
found credential 801cf176-3455-4b6d-a461-457a288fd202 for username BillHurt
password changed for BillHurt
使用 Scriptler 插件实现自动化
您也可以安装 Jenkins Scriptler plugin 并进行如下操作:
- 在侧面菜单中打开 Scriptler 工具
- 填写第 3 个字段,注意将 Id 字段设置为
changeCredentialPassword.groovy
- 选中定义脚本参数复选框
- 添加 2 个参数:
username
和password
- 粘贴以下脚本:
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
jenkins.model.Jenkins.instance
)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, null, c.description, c.username, new_password)
)
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
changePassword("$username", "$password")
- 然后单击提交按钮
现在您可以调用以下URL来更改密码(替换username
和password
参数):http://localhost:8080/scriptler/run/changeCredentialPassword.groovy?username=BillHurt&password=s3crEt%21 (notice the need to urlencode参数值)
或卷曲:
curl -G http://localhost:8080/scriptler/run/changeCredentialPassword.groovy --data-urlencode 'username=BillHurt' --data-urlencode "password=s3crEt!"
来源:
- Printing a list of credentials and their IDs
- Create UserPrivateKeySource Credential via Groovy?
- credential plugin source code
- Scriptler plugin
搜索引擎提示:使用关键字 'Jenkins.instance.'
、'com.cloudbees.plugins.credentials'
和 UsernamePasswordCredentialsImpl
我从未找到让 Groovy 脚本停止更新凭据 ID 的方法,但我注意到如果我使用 Web 界面更新凭据,ID 并没有改变。
考虑到这一点,下面的脚本实际上将编写 Jenkins Web 界面的脚本来执行更新。
澄清一下,这很重要的原因是,如果您使用凭证绑定插件之类的东西在您的工作中使用凭证,将 ID 更新为凭证会破坏 link 和您的工作将失败。请原谅使用 $args 而不是 param() 因为这只是一个丑陋的黑客,我稍后会改进。
请注意向表单字段添加 json 有效负载。我发现在这种非常特殊的格式中这是必需的,否则表单提交将失败。
$username = $args[0]
$password = $args[1]
Add-Type -AssemblyName System.Web
#1. Log in and capture the session.
$homepageResponse = Invoke-WebRequest -uri http://Servername.domain.com/login?from=%2F -SessionVariable session
$loginForm = $homepageResponse.Forms['login']
$loginForm.Fields.j_username = $username
$loginForm.Fields.j_password = $password
$loginResponse = Invoke-WebRequest `
-Uri http://Servername.domain.com/j_acegi_security_check `
-Method Post `
-Body $loginForm.Fields `
-WebSession $session
#2. Get Credential ID
$uri = "http://Servername.domain.com/credential-store/domain/_/api/xml"
foreach($id in [string]((([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content)).domainWrapper.Credentials | Get-Member -MemberType Property).Name -split ' '){
$id = $id -replace '_',''
$uri = "http://Servername.domain.com/credential-store/domain/_/credential/$id/api/xml"
$displayName = ([xml](Invoke-WebRequest -uri $uri -method Get -Headers $headers -WebSession $session).content).credentialsWrapper.displayName
if($displayName -match $username){
$credentialID = $id
}
}
#3. Get Update Form
$updatePage = Invoke-WebRequest -Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/update" -WebSession $session
$updateForm = $updatePage.Forms['update']
$updateForm.Fields.'_.password' = $password
#4. Submit Update Form
$json = @{"stapler-class" = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl";
"scope"="GLOBAL";"username"="domain$username";"password"=$password;"description"="";"id"=$id} | ConvertTo-Json
$updateForm.Fields.Add("json",$json)
Invoke-WebRequest `
-Uri "http://Servername.domain.com/credential-store/domain/_/credential/$credentialID/updateSubmit" `
-Method Post `
-Body $updateForm.Fields `
-WebSession $session
决定写一个新的答案,虽然它基本上是对@Tomasleveil 的答案的一些更新:
- 删除已弃用的调用(感谢 jenkins wiki, for other listing options see plugin consumer guide)
- 添加一些评论
- 保留凭据 ID 以避免破坏现有作业
- 根据描述查找凭据,因为用户名很少如此唯一(reader 可以轻松将其更改为 ID 查找)
开始了:
credentialsDescription = "my credentials description"
newPassword = "hello"
// list credentials
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
// com.cloudbees.plugins.credentials.common.StandardUsernameCredentials to catch all types
com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials.class,
Jenkins.instance,
null,
null
);
// select based on description (based on ID might be even better)
cred = creds.find { it.description == credentialsDescription}
println "current values: ${cred.username}:${cred.password} / ${cred.id}"
// not sure what the other stores would be useful for, but you can list more stores by
// com.cloudbees.plugins.credentials.CredentialsProvider.all()
credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
// replace existing credentials with a new instance
updated = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
cred,
// make sure you create an instance from the correct type
new com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl(cred.scope, cred.id, cred.description, cred.username, newPassword)
)
if (updated) {
println "password changed for '${cred.description}'"
} else {
println "failed to change password for '${cred.description}'"
}