I have a PowerShell script that is intended to modify a web config transform as a pre-build event in a build definition. I've gotten it working for the most part, however when it goes to save the updated file I am getting access denied.
Is there a way to give the right access, without opening a window as this is done via the TFS build agent?
Here is the script:
param(
[string]$buildTarget="Dev",
[string]$projectName="SalesTools"
)
$VerbosePreference = "continue"
Write-Verbose "Params: buildTarget = '$($buildTarget)', projectName = '$($projectName)'"
# Make sure path to source code directory is available
if (-not $Env:TF_BUILD_SOURCESDIRECTORY)
{
Write-Error ("TF_BUILD_SOURCESDIRECTORY environment variable is missing.")
exit 1
}
elseif (-not (Test-Path $Env:TF_BUILD_SOURCESDIRECTORY))
{
Write-Error "TF_BUILD_SOURCESDIRECTORY does not exist: $Env:TF_BUILD_SOURCESDIRECTORY"
exit 1
}
Write-Verbose "TF_BUILD_SOURCESDIRECTORY: $Env:TF_BUILD_SOURCESDIRECTORY"
$webConfig = "$($Env:TF_BUILD_SOURCESDIRECTORY)\$($buildTarget)\SalesTools.Web\$($projectName)\web.$($buildTarget).config"
#$webConfig = "$($Env:TF_BUILD_SOURCESDIRECTORY)\$($buildTarget)\SalesTools.Web\ARCTools\web.$($buildTarget).config"
Write-Verbose "File Path: $($webConfig)"
$doc = (gc $webConfig) -as [xml]
$versionNumber = $doc.SelectSingleNode('//appSettings/add[#key="versionNumber"]/#value').'#text'
Write-Verbose "Current Version Number: $($versionNumber)"
if (($versionNumber))
{
$versionInfo = $versionNumber.Split(".")
$versionIteration = $versionInfo[1]
$minorVersion = $versionInfo[2] -as [int]
$minorVersion = $minorVersion + 1
$currentIteration = Get-Iteration
$newVersionInfo = ("v: 1.$($currentIteration).$($minorVersion)")
}
else
{
Write-Error "Could not get version info from config."
exit 1
}
$doc.SelectSingleNode('//appSettings/add[#key="versionNumber"]/#value').'#text' = $newVersionInfo
$doc.Save($webConfig)
Before you read & update the web.config, try to change the "Read-Only" attribute of web.config file. Because by default, all the source files are "Read-Only".
Add this line before "$doc = ....":
attrib -R $webConfig /S
Related
I try to retrieve a string from a powershell script and use it in Jenkins file, in order to change the displayed version in SonarQube.
I started to implement functionality for taking the project version from package.json. Basically I give a Workspace directory as param and I ask for the full path if he finds a package.json in Workspace itself or in child folders. If the file is found, parse it and return version:
updateDisplayedVersionInSonar.ps1
param (
[Parameter(Mandatory = $true)]
[string]$Workspace
)
try{
$packageFullPath = ""
$pcgVersion = ""
Get-ChildItem -Path ${Workspace} -Filter package.json
-Recurse -ErrorAction SilentlyContinue -Force | % {$packageFullPath = $_.FullName}
try {
Test-Path $packageFullPath -PathType leaf
$json = Get-Content $packageFullPath | Out-String | ConvertFrom-Json
if($json.PSobject.Properties.Name -contains "version"){
$pcgVersion = $json.version
}
else{
$pcgVersion = "unknown"
}
Write-Output $pcgVersion
}
catch {
Write-Output "There is no package.json file!"
}
}
catch{
$ErrorMessage = $_.Exception.Message
write-host "An error has occured: ${ErrorMessage}"
exit 1
}
Now I want to use the version returned from ps script in a Jenkins file:
stage('SonarQube Frontend') {
environment {
sonarqubeScannerHome = tool name: 'SonarQube Scanner', type: hudson.plugins.sonar.SonarRunnerInstallation'
sonarQubeId = 'SonarQubeServer'
sonarProjectName = "\"SPACE ${REPOSITORY_NAME}\""
sonarProjectKey = "${REPOSITORY_NAME}"
testsPaths = 'app/myProject-ui/webapp/TEST/unit/utils'
testExecutionReportPaths = 'app/myProject-ui/reports/sonar/TESTS-qunit.xml'
javascriptLcovReportPaths = 'app/myProject-ui/reports/coverage/lcov.info'
}
steps {
withSonarQubeEnv(env.sonarQubeId) {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: sonarQubeId, usernameVariable: 'SONAR_USER', passwordVariable: 'SONAR_PASSWORD']]) {
script{
sonarProperties = " -Dsonar.projectName=${env.sonarProjectName}" +
" -Dsonar.projectKey=${env.sonarProjectKey}" +
" -Dsonar.login=${SONAR_USER}" +
" -Dsonar.password=${SONAR_PASSWORD}" +
" -Dsonar.sources=./" +
" -Dsonar.exclusions=**/*.java"
//some other conditions
//this line will be executed and i will see in Jenkins Console output the version found in package.json
powershell "powershell -File C:/Automation/updateDisplayedVersionInSonar.ps1 -Workspace '${env.WORKSPACE}/app'"
//I try to make it as a variable, but it will print "echo version here - null -" :(
pcgVersion = powershell "powershell -File C:/Automation/updateDisplayedVersionInSonar.ps1 -Workspace '${env.WORKSPACE}/app'"
echo "echo version here - ${pcgVersion} -"
//I want to use it here in order to be displayed the correct version of the app in Sonar
sonarProperties = sonarProperties + " -Dsonar.projectVersion= ${pcgVersion}"
}
bat "${env.sonarqubeScannerHome}/bin/sonar-scanner" + " -Dsonar.host.url=${SONAR_HOST_URL}" + sonarProperties
}
}
}
} //end of SonarQube Frontend stage
I tried the solution from How to execute powershell script from jenkins by passing parameters but without any result.
I tried also to do this way:
version = powershell(returnStdout: true, script:'powershell -File C:/SCP/Automation/updateDisplayedVersionInSonar.ps1 -Workspace "${env.WORKSPACE}"')
version = powershell(returnStdout: true, script:'C:/SCP/Automation/updateDisplayedVersionInSonar.ps1 -Workspace "${env.WORKSPACE}"')
I found quite a lot of examples of how to use Jenkins variable in Powershell, but not vice versa :(
What I am doing wrong? It is possible to achieve this?
Thank you!
You could use Jenkins Pipeline Utility instead.
def getPackageVersion() {
def package = readJSON file: '${env.WORKSPACE}/app/package.json'
echo package.version
return package.version
}
Then you should be able to access it like this.
def pcgVersion = getPackageVersion()
I am running a vsts build inline PowerShell script task to create package for Azure cloud service. It works fine and create package file from my local machine, but when I try to run from VSTS PowerShell inline task it gives error :
##[error]Cannot find path ‘D:\a_tasks\InlinePowershell_31f040e5-e040-4336-878a-59a493355534\1.1.6\ServiceConfiguration.Cloud.Test.cscfg’ because it does not exist.
Here is my PowerShell inline script below, It fails on the following line:
Copy-Item $serviceConfigurationPath $packageOutDir
I really appreciate your help on this.
Thanks,
# This is the VSTS repository path
$workingDirectory = “$/DevCodeBase/ToolDevBranch1.33”
$webProjectName = “WebRole1”
$cloudProjectName = ‘ProjAzureDeployment’
$evv =’Test’
$cppack = ‘C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe’
$solutionDir = [string]::Format(“{0}”, $workingDirectory)
$webDir = [string]::Format(“{0}\{1}”, $workingDirectory, $webProjectName)
$packageOutDir = [string]::Format(“{0}\{1}”, $workingDirectory, $cloudProjectName)
$rolePropertyFile = [string]::Format(“{0}\{1}\{2}”, $workingDirectory, $cloudProjectName, “roleproperties.txt”)
# Create Role Properties File – This property file specifies the .Net framework against which webrole is going to run.
New-Item $rolePropertyFile -Type file -Force -Value “TargetFrameWorkVersion=v4.5” | Out-Null
New-Item $packageOutDir -Type directory -Force | Out-Null
# CSPack command Definition
$serviceDefinitionPath = [string]::Format(“{0}\{1}\ServiceDefinition.csdef”, $solutionDir, $cloudProjectName)
if ($evv -eq “Test”){
$serviceConfigurationPath = “ServiceConfiguration.Cloud.Test.cscfg”
}
else
{
$serviceConfigurationPath = [string]::Format(“{0}\{1}\ServiceConfiguration.Cloud.cscfg”, $solutionDir, $cloudProjectName)
}
$serviceRole = [string]::Format(“/role:{0};{1}”, $webProjectName, $webDir)
$rolePropertiesFile = [string]::Format(“/rolePropertiesFile:{0};{1}”, $webProjectName, $rolePropertyFile)
$sites = [string]::Format(“/sites:{0};Web;{1}”, $webProjectName, $webDir)
$packageOutput = [string]::Format(“/out:{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
# $packageOutput = [string]::Format(“{0}\{1}.cspkg”, $packageOutDir, $cloudProjectName)
Write-Host $packageOutput
Write-Host $serviceConfigurationPath
# Build CSPKG file
& “C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9\bin\cspack.exe” $serviceDefinitionPath $serviceRole $rolePropertiesFile $sites $packageOutput /useCtpPackageFormat | Out-Null
Write-Host $serviceDefinitionPath
Write-Host $serviceRole
Write-Host $rolePropertiesFile
Write-Host $sites
Write-Host $packageOutput
Write-Host ‘before copy’
# Copy configuration file
Copy-Item $serviceConfigurationPath $packageOutDir
# Remove Role Properties File
Remove-Item -Path $rolePropertyFile -Force | Out-Null
In the VSTS task you'll have to specify an absolute path, otherwise the script will look in the temporary directory created for your inline powershell script.
For instance, you could supply the path to the file as a parameter like
-filepath "$(System.DefaultWorkingDirectory)\Solution\config.json"
(For a list of the variables you can use, have a peek here)
If you want to keep using a relative path, you can move to a file based (ie non-inline) script and use a relative path to that.
My build server is doing all the steps necessary to build a zip of the new website. I would like to add a step to checkin zipfile to TFS. I have created a ps1 file to perform the checkin. I am running it in ISE so there is no dependency on having TeamCity. Here are the errors that I am seeing.
No matter how I do workspace.GET, it does not get the latest code
from the server.
Even when I change a file on the hard drive it
does not see changes.
Because no changes are detected the zip is
not checked in to TFS.
Here is the code....
#============================================================================
# Method to check in all zip files
#
# Example of WorkingDir passed in
# "D:\TeamCity\buildAgent\work\281509782e84e723\Powershell"
#
# Example of where freshly created zips live
# "D:\TeamCity\buildAgent\work\281509782e84e723\Zips"
#
# this script is based on
# From https://github.com/mmessano/PowerShell/blob/master/TFSCheckIn.ps1
# From http://stackoverflow.com/questions/25917753/check-a-file-into-tfs-using-powershell
# from http://lennartjansson2.wordpress.com/2011/10/13/setting-tfs-vcs-security-with-ps-2/
#
#============================================================================
function StackOverflow {
Param( [Parameter(Mandatory=$true)][string]$WorkingDir )
Write-BuildLog "Inside StackOverflow"
# Get the direcory where new zips where built
$NewZipFiles = $WorkingDir + "\..\Zips\*"
# This is the url to the TFS server + Project collection
$tfsServer = "YourServerAndCollection";
# this is the full path on server where zips live
# You need to start description with $
$tfsServerPath = "$/MyProject/FullPathToDirwithZips"
# Where on local hard drive should files from TFS be placed
$LocalCkoutDir = "D:\MyLocalHDPath"
# Debug print var to verify correct
Write-BuildLog "NewZipFiles => $NewZipFiles"
Write-BuildLog "tfsServer => $tfsServer"
Write-BuildLog "tfsServerPath => $tfsServerPath"
Write-BuildLog "LocalCkoutDir => $LocalCkoutDir"
# Get the TeamCity build number
#$VarName = "BUILD_NUMBER"
#$TeamCityVersionNbr = (get-item env:$VarName).Value
$TeamCityVersionNbr = "MyProject_03_02_81"
Write-BuildLog "Version Nbr $TeamCityVersionNbr"
$CheckInComment = "Check in zips for $BuildNumber"
# Load the assemblies needed for TFS:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Common") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client") | out-null
#Set up connection to TFS Server and get version control
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($tfsServer)
$versionControlType = [Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer]
$versionControlServer = $tfs.GetService($versionControlType)
#check to see if workspace already exists. If it does delete it.
$WorkSpaceNameForCheckIn = "TeamCityWorkspace"
$ThisBoxName = [System.Environment]::MachineName
$test = $versionControlServer.QueryWorkspaces( $WorkSpaceNameForCheckIn, $versionControlServer.AuthenticatedUser, $ThisBoxName )
if ( $test.length -eq 1 )
{
$test[0].Delete()
}
# Generate a workspace
$workspace = $versionControlServer.CreateWorkspace($WorkSpaceNameForCheckIn);
# Map Server path to local path
$workspace.Map($tfsServerPath, $LocalCkoutDir)
# DEBUG: build filename of a zip.
# We will overwrite this file to test the get
$file = "AZipFileThatExists.zip"
$filePath = $LocalCkoutDir + "\" + $file
"hello world" | Out-File $filePath
# I tried the simple get but it does not get
# Get the zip files from the server to local directory
$getstatus = $workspace.Get()
# Csharp way of doing it
#workspace.Map(projectPath, workingDirectory);
# var myItemSpec = new ItemSpec(projectPath, RecursionType.Full);
#GetRequest request = new GetRequest(myItemSpec, VersionSpec.Latest);
#GetStatus status = workspace.Get(request, GetOptions.GetAll | GetOptions.Overwrite); // this line doesn't do anything - no failures or er
# This does not work either
# Powershell checkout the file. Overwrite if file exists. Get even if TFS thinks it is up to date.
$NewItemSpec = New-Object Microsoft.TeamFoundation.VersionControl.Client.ItemSpec ( $tfsServerPath, [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full)
$NewRequest = New-Object Microsoft.TeamFoundation.VersionControl.Client.GetRequest( $NewItemSpec, [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest)
$getstatus = $workspace.Get( $NewRequest, [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::GetAll -bOr [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite )
# I have not tested the rest of this since the "get" does not work.
# Mark the files before we refresh them with new zips
$result = $workspace.PendEdit($LocalCkoutDir)
# Copy zips that where built by TeamCity to checkin direcory
Copy-Item $NewZipFiles $LocalCkoutDir -force -recurse
# check if we have some pending changes. If we do checkin changes
$pendings = $workspace.GetPendingChanges();
if($pendings.Count -gt 0){
$result = $workspace.CheckIn($pendings, $CheckInComment);
Write-BuildLog "Changes where checked in";
}
else
{
Write-BuildLog "No changes found";
}
# delete the workspace
$result = $workspace.Delete()
}
#============================================================================
# Write to the build log
#============================================================================
function Write-BuildLog {
param( [Parameter( Mandatory=$true)] $Message
)
write-host $Message
#write-host "##teamcity[message text='" + $Message + "']"
}
$myDir = Split-Path -Parent $MyInvocation.MyCommand.Path
StackOverflow $myDir
use the tf command line
Example for checkin:
cd C:\TFS\Arquitectura
%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\TF.exe checkin $/Arquitectura/Main /recursive
On Windows x64
"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\TF.exe" checkin $/Arquitectura/Main /recursive
See for more information on the tf commandline: http://msdn.microsoft.com/en-us/library/z51z7zy0(v=VS.90).aspx
Only learning curve about use tf.exe with Powershell. Maybe source code sample is required.
Source: Scripting TFS Command Line for Get Latest Version, Check Out and Check in, programmatically
Disclaimer: I don't know enough about ps to accomplish this in a reasonable amount of time, so yes, I am asking someone else to do my dirty job.
I want to be able to run a web.config transformation without opening a command line.
I have following files in a folder:
web.config - actual web config
web.qa.config - web config transformation for qa env
web.production.config - web config transformation for production env
transform.ps1 - powershell script I want to use to run transformation
Here is what I want:
PS file shall enumerate current directory using .*\.(?<env>.*?)\.config and let me choose which <env> I am interested in generate web.config for. In my example I will be presented with two options: "qa", "production".
After I (user) select the environment (let's say it is "qa", selected environment is stored as $env, and corresponding filename will be stored as $transformation) script shall do following:
backup original web.config as web.config.bak
execute following command:
.
echo applying $transformation...
[ctt][1].exe source:web.config transformation:$transformation destination:web.config preservewhitespaces verbose
echo done.
ctt.exe is a tool based on XDT that runs web.config transformation from command line.
Okay, looks simple enough, I'll do your dirty job for you. ;)
Save the following as transform.ps1:
$environments = #()f
gci | %{if ($_ -match '.*\.(?<env>.*?)\.config') {$environments += $matches.env}}
Write-Host "`nEnvironments:"
for ($i = 0; $i -lt $environments.Length; $i++) {Write-Host "[$($i + 1)] $($environments[$i])"}
Write-Host
do {
$selection = [int](Read-Host "Please select an environment")
if ($selection -gt 0 -and $selection -le $environments.Length) {
$continue = $true
} else {
Write-Host "Invalid selection. Please enter the number of the environment you would like to select from the list."
}
} until ($continue)
$transformation = "web.$($environments[$selection - 1]).config"
if (Test-Path .\web.config) {
cpi .\web.config .\web.config.bak
} else {
Write-Warning "web.config does not exist. No backup will be created."
}
if ($?) {
Write-Host "`nApplying $transformation..."
[ctt][1].exe source:web.config transformation:$transformation destination:web.config preservewhitespaces verbose
Write-Host "Done.`n"
} else {
Write-Error "Failed to create a backup of web.config. Transformation aborted."
}
We are using power shell to deploy our 2012 SSIS packages and have environment variables on a SSIS 2012 Server. Now during project deployment I am attempting to loop through eachvariable in the environment variables collection (foreach($variable in $environment.Variables)). That is no problem. I can see "EnvironmentVariable[#Name = 'something']"....however attempting to retrieve the name ("something") from the variable via $variable.Name or $variable.Key doesn't work. I've tried looping through $environment.Variables.Keys and still nothing. My power shell skills are a little weak since I've been using NANT the past several years but is there something I'm just not seeing?
Thanks in advance,
Anthony
Adding snippet of existing power shell script. The bolded $variable.Name is not working within the CreateETLPackages task. There is a lot of setup and other scripts called from this scripts so I haven't included everything. When $variable.Name is returned in a debug statement it returns "EnvironmentVariable[#Name = 'something']" as I mentoned in my original post:
Task CreateSSISFolder -Depends CreateSSISCatalog {
if (!$script:SSISCanBeDeployed) { return }
# Create the project for the packages in the catalog
$catalog = $script:SSISCatalog
if ($catalog.Folders.Count -eq 0) {
Write-Host "Creating folder $SSISFolderName ..."
$script:SSISFolder = New-Object "Microsoft.SqlServer.Management.IntegrationServices.CatalogFolder" ($catalog, $SSISFolderName, "Folder for EDGE ETL packages")
$script:SSISFolder.Create()
Write-Host "... done"
} else {
Write-Host "SSIS folder $SSISFolderName already exists; skipping create"
}
}
Task CreateSSISEnvironment -Depends CreateSSISFolder {
if (!$script:SSISCanBeDeployed) { return }
# Create the environment in the project
$folder = $script:SSISFolder
$environment = $folder.Environments[$SSISEnvironmentName]
if ($environment -eq $null) {
# Create the environment
Write-Host "Creating environment $SSISEnvironmentName ..."
$environment = New-Object "Microsoft.SqlServer.Management.IntegrationServices.EnvironmentInfo" ($folder, $SSISEnvironmentName, "Environment to configure the SSIS packages")
$environment.Create()
Write-Host "... done"
# Now create the variables (Constructor args: variable name, type, default value, sensitivity, description)
$environment.Variables.Add("TestDatabase", [System.TypeCode]::String, "Data Source=$SSISServerName.TestDatabase;User ID=<USERNAME>;Password=<PASSWORD>;Initial Catalog=EdgeAviTrack;Provider=SQLNCLI11.1;Persist Security Info=True;Auto Translate=False;", $false, "Connection string for TestDatabase database")
$environment.Alter()
} else {
Write-Host "Environment $SSISEnvironmentName already exists; skipping create"
}
}
Task CreateETLPackages -Depends CreateSSISFolder, CreateSSISEnvironment {
if (!$script:SSISCanBeDeployed) { return }
# Get list of ETL .ispac files in the solution
$SSISProjects = GetListOfDeploymentFiles "*.ispac"
if ($SSISProjects -ne $null) {
$folder = $script:SSISFolder
$environment = $folder.Environments[$SSISEnvironmentName]
if ($folder -ne $null) {
foreach ($file in $SSISProjects) {
# Read the ispac file, and deploy it to the folder
[byte[]] $projectFile = [System.IO.File]::ReadAllBytes($file.FullName)
$nameParts = $file.Name.split(".")
$curProjectName = [string]::join(".", $nameParts[0..($nameParts.length - 2)])
Write-Debug "Deploying SSIS project $curProjectName"
$project = $folder.DeployProject($curProjectName, $projectFile)
if ($project.Status -ne "Success") {
Write-Error "SSIS packages did not deploy correctly!"
} else {
# Get the full information set, rather than the short version returned from DeployProject
$project = $folder.Projects[$curProjectName]
}
# Connect the project to the environment to stitch up all the connection strings
if ($project.References.Item($SSISEnvironmentName, ".") -eq $null) {
Write-Host "Adding environment reference to $SSISEnvironmentName ..."
$project.References.Add($SSISEnvironmentName)
$project.Alter()
Write-Host "... done"
}
# Connect all the project parameters to the environment variables
Write-Host "Adding connection string references to environment variables ..."
foreach($varialble in $environment.Variables) {
try {
$project.Parameters["CM." + **$varialble.Name** + ".ConnectionString"].Set([Microsoft.SqlServer.Management.IntegrationServices.ParameterInfo+ParameterValueType]::Referenced, **$variable.Name**)
}
catch {
Write-Debug "Unable to set connection string **$variable.Name** on SSIS project $curProjectName"
}
}
$project.Alter()
Write-Host "... done"
}
}
}
}
Ok I found my issue. Looks like I was trying I need to use the $($object.Name) to get what I need out. Appreciate those that reached out to for their help.
Thanks,
Anthony